helpers.go 8.3 KB

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