helpers.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. /*
  2. Package imaging provides basic image manipulation functions (resize, rotate, flip, crop, etc.).
  3. This package is based on the standard Go image package and works best along with it.
  4. Image manipulation functions provided by the package take any image type
  5. that implements `image.Image` interface as an input, and return a new image of
  6. `*image.NRGBA` type (32bit RGBA colors, not premultiplied by alpha).
  7. */
  8. package imaging
  9. import (
  10. "errors"
  11. "image"
  12. "image/color"
  13. "image/gif"
  14. "image/jpeg"
  15. "image/png"
  16. "io"
  17. "os"
  18. "path/filepath"
  19. "strings"
  20. "golang.org/x/image/bmp"
  21. "golang.org/x/image/tiff"
  22. )
  23. type Format int
  24. const (
  25. JPEG Format = iota
  26. PNG
  27. GIF
  28. TIFF
  29. BMP
  30. )
  31. func (f Format) String() string {
  32. switch f {
  33. case JPEG:
  34. return "JPEG"
  35. case PNG:
  36. return "PNG"
  37. case GIF:
  38. return "GIF"
  39. case TIFF:
  40. return "TIFF"
  41. case BMP:
  42. return "BMP"
  43. default:
  44. return "Unsupported"
  45. }
  46. }
  47. var (
  48. ErrUnsupportedFormat = errors.New("imaging: unsupported image format")
  49. )
  50. // Decode reads an image from r.
  51. func Decode(r io.Reader) (image.Image, error) {
  52. img, _, err := image.Decode(r)
  53. if err != nil {
  54. return nil, err
  55. }
  56. return toNRGBA(img), nil
  57. }
  58. // Open loads an image from file
  59. func Open(filename string) (image.Image, error) {
  60. file, err := os.Open(filename)
  61. if err != nil {
  62. return nil, err
  63. }
  64. defer file.Close()
  65. img, err := Decode(file)
  66. return img, err
  67. }
  68. // Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP).
  69. func Encode(w io.Writer, img image.Image, format Format) error {
  70. var err error
  71. switch format {
  72. case JPEG:
  73. var rgba *image.RGBA
  74. if nrgba, ok := img.(*image.NRGBA); ok {
  75. if nrgba.Opaque() {
  76. rgba = &image.RGBA{
  77. Pix: nrgba.Pix,
  78. Stride: nrgba.Stride,
  79. Rect: nrgba.Rect,
  80. }
  81. }
  82. }
  83. if rgba != nil {
  84. err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: 95})
  85. } else {
  86. err = jpeg.Encode(w, img, &jpeg.Options{Quality: 95})
  87. }
  88. case PNG:
  89. err = png.Encode(w, img)
  90. case GIF:
  91. err = gif.Encode(w, img, &gif.Options{NumColors: 256})
  92. case TIFF:
  93. err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
  94. case BMP:
  95. err = bmp.Encode(w, img)
  96. default:
  97. err = ErrUnsupportedFormat
  98. }
  99. return err
  100. }
  101. // Save saves the image to file with the specified filename.
  102. // The format is determined from the filename extension: "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
  103. func Save(img image.Image, filename string) (err error) {
  104. formats := map[string]Format{
  105. ".jpg": JPEG,
  106. ".jpeg": JPEG,
  107. ".png": PNG,
  108. ".tif": TIFF,
  109. ".tiff": TIFF,
  110. ".bmp": BMP,
  111. ".gif": GIF,
  112. }
  113. ext := strings.ToLower(filepath.Ext(filename))
  114. f, ok := formats[ext]
  115. if !ok {
  116. return ErrUnsupportedFormat
  117. }
  118. file, err := os.Create(filename)
  119. if err != nil {
  120. return err
  121. }
  122. defer file.Close()
  123. return Encode(file, img, f)
  124. }
  125. // New creates a new image with the specified width and height, and fills it with the specified color.
  126. func New(width, height int, fillColor color.Color) *image.NRGBA {
  127. if width <= 0 || height <= 0 {
  128. return &image.NRGBA{}
  129. }
  130. dst := image.NewNRGBA(image.Rect(0, 0, width, height))
  131. c := color.NRGBAModel.Convert(fillColor).(color.NRGBA)
  132. if c.R == 0 && c.G == 0 && c.B == 0 && c.A == 0 {
  133. return dst
  134. }
  135. cs := []uint8{c.R, c.G, c.B, c.A}
  136. // fill the first row
  137. for x := 0; x < width; x++ {
  138. copy(dst.Pix[x*4:(x+1)*4], cs)
  139. }
  140. // copy the first row to other rows
  141. for y := 1; y < height; y++ {
  142. copy(dst.Pix[y*dst.Stride:y*dst.Stride+width*4], dst.Pix[0:width*4])
  143. }
  144. return dst
  145. }
  146. // Clone returns a copy of the given image.
  147. func Clone(img image.Image) *image.NRGBA {
  148. srcBounds := img.Bounds()
  149. srcMinX := srcBounds.Min.X
  150. srcMinY := srcBounds.Min.Y
  151. dstBounds := srcBounds.Sub(srcBounds.Min)
  152. dstW := dstBounds.Dx()
  153. dstH := dstBounds.Dy()
  154. dst := image.NewNRGBA(dstBounds)
  155. switch src := img.(type) {
  156. case *image.NRGBA:
  157. rowSize := srcBounds.Dx() * 4
  158. parallel(dstH, func(partStart, partEnd int) {
  159. for dstY := partStart; dstY < partEnd; dstY++ {
  160. di := dst.PixOffset(0, dstY)
  161. si := src.PixOffset(srcMinX, srcMinY+dstY)
  162. copy(dst.Pix[di:di+rowSize], src.Pix[si:si+rowSize])
  163. }
  164. })
  165. case *image.NRGBA64:
  166. parallel(dstH, func(partStart, partEnd int) {
  167. for dstY := partStart; dstY < partEnd; dstY++ {
  168. di := dst.PixOffset(0, dstY)
  169. si := src.PixOffset(srcMinX, srcMinY+dstY)
  170. for dstX := 0; dstX < dstW; dstX++ {
  171. dst.Pix[di+0] = src.Pix[si+0]
  172. dst.Pix[di+1] = src.Pix[si+2]
  173. dst.Pix[di+2] = src.Pix[si+4]
  174. dst.Pix[di+3] = src.Pix[si+6]
  175. di += 4
  176. si += 8
  177. }
  178. }
  179. })
  180. case *image.RGBA:
  181. parallel(dstH, func(partStart, partEnd int) {
  182. for dstY := partStart; dstY < partEnd; dstY++ {
  183. di := dst.PixOffset(0, dstY)
  184. si := src.PixOffset(srcMinX, srcMinY+dstY)
  185. for dstX := 0; dstX < dstW; dstX++ {
  186. a := src.Pix[si+3]
  187. dst.Pix[di+3] = a
  188. switch a {
  189. case 0:
  190. dst.Pix[di+0] = 0
  191. dst.Pix[di+1] = 0
  192. dst.Pix[di+2] = 0
  193. case 0xff:
  194. dst.Pix[di+0] = src.Pix[si+0]
  195. dst.Pix[di+1] = src.Pix[si+1]
  196. dst.Pix[di+2] = src.Pix[si+2]
  197. default:
  198. var tmp uint16
  199. tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a)
  200. dst.Pix[di+0] = uint8(tmp)
  201. tmp = uint16(src.Pix[si+1]) * 0xff / uint16(a)
  202. dst.Pix[di+1] = uint8(tmp)
  203. tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a)
  204. dst.Pix[di+2] = uint8(tmp)
  205. }
  206. di += 4
  207. si += 4
  208. }
  209. }
  210. })
  211. case *image.RGBA64:
  212. parallel(dstH, func(partStart, partEnd int) {
  213. for dstY := partStart; dstY < partEnd; dstY++ {
  214. di := dst.PixOffset(0, dstY)
  215. si := src.PixOffset(srcMinX, srcMinY+dstY)
  216. for dstX := 0; dstX < dstW; dstX++ {
  217. a := src.Pix[si+6]
  218. dst.Pix[di+3] = a
  219. switch a {
  220. case 0:
  221. dst.Pix[di+0] = 0
  222. dst.Pix[di+1] = 0
  223. dst.Pix[di+2] = 0
  224. case 0xff:
  225. dst.Pix[di+0] = src.Pix[si+0]
  226. dst.Pix[di+1] = src.Pix[si+2]
  227. dst.Pix[di+2] = src.Pix[si+4]
  228. default:
  229. var tmp uint16
  230. tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a)
  231. dst.Pix[di+0] = uint8(tmp)
  232. tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a)
  233. dst.Pix[di+1] = uint8(tmp)
  234. tmp = uint16(src.Pix[si+4]) * 0xff / uint16(a)
  235. dst.Pix[di+2] = uint8(tmp)
  236. }
  237. di += 4
  238. si += 8
  239. }
  240. }
  241. })
  242. case *image.Gray:
  243. parallel(dstH, func(partStart, partEnd int) {
  244. for dstY := partStart; dstY < partEnd; dstY++ {
  245. di := dst.PixOffset(0, dstY)
  246. si := src.PixOffset(srcMinX, srcMinY+dstY)
  247. for dstX := 0; dstX < dstW; dstX++ {
  248. c := src.Pix[si]
  249. dst.Pix[di+0] = c
  250. dst.Pix[di+1] = c
  251. dst.Pix[di+2] = c
  252. dst.Pix[di+3] = 0xff
  253. di += 4
  254. si += 1
  255. }
  256. }
  257. })
  258. case *image.Gray16:
  259. parallel(dstH, func(partStart, partEnd int) {
  260. for dstY := partStart; dstY < partEnd; dstY++ {
  261. di := dst.PixOffset(0, dstY)
  262. si := src.PixOffset(srcMinX, srcMinY+dstY)
  263. for dstX := 0; dstX < dstW; dstX++ {
  264. c := src.Pix[si]
  265. dst.Pix[di+0] = c
  266. dst.Pix[di+1] = c
  267. dst.Pix[di+2] = c
  268. dst.Pix[di+3] = 0xff
  269. di += 4
  270. si += 2
  271. }
  272. }
  273. })
  274. case *image.YCbCr:
  275. parallel(dstH, func(partStart, partEnd int) {
  276. for dstY := partStart; dstY < partEnd; dstY++ {
  277. di := dst.PixOffset(0, dstY)
  278. for dstX := 0; dstX < dstW; dstX++ {
  279. srcX := srcMinX + dstX
  280. srcY := srcMinY + dstY
  281. siy := src.YOffset(srcX, srcY)
  282. sic := src.COffset(srcX, srcY)
  283. r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic])
  284. dst.Pix[di+0] = r
  285. dst.Pix[di+1] = g
  286. dst.Pix[di+2] = b
  287. dst.Pix[di+3] = 0xff
  288. di += 4
  289. }
  290. }
  291. })
  292. case *image.Paletted:
  293. plen := len(src.Palette)
  294. pnew := make([]color.NRGBA, plen)
  295. for i := 0; i < plen; i++ {
  296. pnew[i] = color.NRGBAModel.Convert(src.Palette[i]).(color.NRGBA)
  297. }
  298. parallel(dstH, func(partStart, partEnd int) {
  299. for dstY := partStart; dstY < partEnd; dstY++ {
  300. di := dst.PixOffset(0, dstY)
  301. si := src.PixOffset(srcMinX, srcMinY+dstY)
  302. for dstX := 0; dstX < dstW; dstX++ {
  303. c := pnew[src.Pix[si]]
  304. dst.Pix[di+0] = c.R
  305. dst.Pix[di+1] = c.G
  306. dst.Pix[di+2] = c.B
  307. dst.Pix[di+3] = c.A
  308. di += 4
  309. si += 1
  310. }
  311. }
  312. })
  313. default:
  314. parallel(dstH, func(partStart, partEnd int) {
  315. for dstY := partStart; dstY < partEnd; dstY++ {
  316. di := dst.PixOffset(0, dstY)
  317. for dstX := 0; dstX < dstW; dstX++ {
  318. c := color.NRGBAModel.Convert(img.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA)
  319. dst.Pix[di+0] = c.R
  320. dst.Pix[di+1] = c.G
  321. dst.Pix[di+2] = c.B
  322. dst.Pix[di+3] = c.A
  323. di += 4
  324. }
  325. }
  326. })
  327. }
  328. return dst
  329. }
  330. // This function used internally to convert any image type to NRGBA if needed.
  331. func toNRGBA(img image.Image) *image.NRGBA {
  332. srcBounds := img.Bounds()
  333. if srcBounds.Min.X == 0 && srcBounds.Min.Y == 0 {
  334. if src0, ok := img.(*image.NRGBA); ok {
  335. return src0
  336. }
  337. }
  338. return Clone(img)
  339. }