vips.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. package main
  2. /*
  3. #cgo pkg-config: vips
  4. #cgo LDFLAGS: -s -w
  5. #cgo CFLAGS: -O3
  6. #include "vips.h"
  7. */
  8. import "C"
  9. import (
  10. "context"
  11. "math"
  12. "os"
  13. "runtime"
  14. "time"
  15. "unsafe"
  16. )
  17. type vipsImage struct {
  18. VipsImage *C.VipsImage
  19. }
  20. var (
  21. vipsSupportSmartcrop bool
  22. vipsTypeSupportLoad = make(map[imageType]bool)
  23. vipsTypeSupportSave = make(map[imageType]bool)
  24. watermark *watermarkData
  25. )
  26. var vipsConf struct {
  27. JpegProgressive C.int
  28. PngInterlaced C.int
  29. PngQuantize C.int
  30. PngQuantizationColors C.int
  31. WatermarkOpacity C.double
  32. }
  33. const (
  34. vipsAngleD0 = C.VIPS_ANGLE_D0
  35. vipsAngleD90 = C.VIPS_ANGLE_D90
  36. vipsAngleD180 = C.VIPS_ANGLE_D180
  37. vipsAngleD270 = C.VIPS_ANGLE_D270
  38. )
  39. func initVips() {
  40. runtime.LockOSThread()
  41. defer runtime.UnlockOSThread()
  42. if err := C.vips_initialize(); err != 0 {
  43. C.vips_shutdown()
  44. logFatal("unable to start vips!")
  45. }
  46. // Disable libvips cache. Since processing pipeline is fine tuned, we won't get much profit from it.
  47. // Enabled cache can cause SIGSEGV on Musl-based systems like Alpine.
  48. C.vips_cache_set_max_mem(0)
  49. C.vips_cache_set_max(0)
  50. C.vips_concurrency_set(1)
  51. if len(os.Getenv("IMGPROXY_VIPS_LEAK_CHECK")) > 0 {
  52. C.vips_leak_set(C.gboolean(1))
  53. }
  54. if len(os.Getenv("IMGPROXY_VIPS_CACHE_TRACE")) > 0 {
  55. C.vips_cache_set_trace(C.gboolean(1))
  56. }
  57. vipsSupportSmartcrop = C.vips_support_smartcrop() == 1
  58. if int(C.vips_type_find_load_go(C.int(imageTypeJPEG))) != 0 {
  59. vipsTypeSupportLoad[imageTypeJPEG] = true
  60. }
  61. if int(C.vips_type_find_load_go(C.int(imageTypePNG))) != 0 {
  62. vipsTypeSupportLoad[imageTypePNG] = true
  63. }
  64. if int(C.vips_type_find_load_go(C.int(imageTypeWEBP))) != 0 {
  65. vipsTypeSupportLoad[imageTypeWEBP] = true
  66. }
  67. if int(C.vips_type_find_load_go(C.int(imageTypeGIF))) != 0 {
  68. vipsTypeSupportLoad[imageTypeGIF] = true
  69. }
  70. if int(C.vips_type_find_load_go(C.int(imageTypeSVG))) != 0 {
  71. vipsTypeSupportLoad[imageTypeSVG] = true
  72. }
  73. if int(C.vips_type_find_load_go(C.int(imageTypeHEIC))) != 0 {
  74. vipsTypeSupportLoad[imageTypeHEIC] = true
  75. }
  76. // we load ICO with github.com/mat/besticon/ico and send decoded data to vips
  77. vipsTypeSupportLoad[imageTypeICO] = true
  78. if int(C.vips_type_find_save_go(C.int(imageTypeJPEG))) != 0 {
  79. vipsTypeSupportSave[imageTypeJPEG] = true
  80. }
  81. if int(C.vips_type_find_save_go(C.int(imageTypePNG))) != 0 {
  82. vipsTypeSupportSave[imageTypePNG] = true
  83. }
  84. if int(C.vips_type_find_save_go(C.int(imageTypeWEBP))) != 0 {
  85. vipsTypeSupportSave[imageTypeWEBP] = true
  86. }
  87. if int(C.vips_type_find_save_go(C.int(imageTypeGIF))) != 0 {
  88. vipsTypeSupportSave[imageTypeGIF] = true
  89. }
  90. if int(C.vips_type_find_save_go(C.int(imageTypeICO))) != 0 {
  91. vipsTypeSupportSave[imageTypeICO] = true
  92. }
  93. if int(C.vips_type_find_save_go(C.int(imageTypeHEIC))) != 0 {
  94. vipsTypeSupportSave[imageTypeHEIC] = true
  95. }
  96. if conf.JpegProgressive {
  97. vipsConf.JpegProgressive = C.int(1)
  98. }
  99. if conf.PngInterlaced {
  100. vipsConf.PngInterlaced = C.int(1)
  101. }
  102. if conf.PngQuantize {
  103. vipsConf.PngQuantize = C.int(1)
  104. }
  105. vipsConf.PngQuantizationColors = C.int(conf.PngQuantizationColors)
  106. vipsConf.WatermarkOpacity = C.double(conf.WatermarkOpacity)
  107. if err := vipsLoadWatermark(); err != nil {
  108. logFatal(err.Error())
  109. }
  110. vipsCollectMetrics()
  111. }
  112. func shutdownVips() {
  113. C.vips_shutdown()
  114. }
  115. func vipsCollectMetrics() {
  116. if prometheusEnabled {
  117. go func() {
  118. for range time.Tick(5 * time.Second) {
  119. prometheusVipsMemory.Set(float64(C.vips_tracked_get_mem()))
  120. prometheusVipsMaxMemory.Set(float64(C.vips_tracked_get_mem_highwater()))
  121. prometheusVipsAllocs.Set(float64(C.vips_tracked_get_allocs()))
  122. }
  123. }()
  124. }
  125. }
  126. func vipsCleanup() {
  127. C.vips_cleanup()
  128. }
  129. func vipsError() error {
  130. return newUnexpectedError(C.GoString(C.vips_error_buffer()), 1)
  131. }
  132. func vipsLoadWatermark() (err error) {
  133. watermark, err = getWatermarkData()
  134. return
  135. }
  136. func (img *vipsImage) Width() int {
  137. return int(img.VipsImage.Xsize)
  138. }
  139. func (img *vipsImage) Height() int {
  140. return int(img.VipsImage.Ysize)
  141. }
  142. func (img *vipsImage) Load(data []byte, imgtype imageType, shrink int, scale float64, pages int) error {
  143. var tmp *C.VipsImage
  144. err := C.int(0)
  145. switch imgtype {
  146. case imageTypeJPEG:
  147. err = C.vips_jpegload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(shrink), &tmp)
  148. case imageTypePNG:
  149. err = C.vips_pngload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), &tmp)
  150. case imageTypeWEBP:
  151. err = C.vips_webpload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.double(scale), C.int(pages), &tmp)
  152. case imageTypeGIF:
  153. err = C.vips_gifload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(pages), &tmp)
  154. case imageTypeSVG:
  155. err = C.vips_svgload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.double(scale), &tmp)
  156. case imageTypeICO:
  157. rawData, width, height, icoErr := icoData(data)
  158. if icoErr != nil {
  159. return icoErr
  160. }
  161. tmp = C.vips_image_new_from_memory_copy(unsafe.Pointer(&rawData[0]), C.size_t(width*height*4), C.int(width), C.int(height), 4, C.VIPS_FORMAT_UCHAR)
  162. case imageTypeHEIC:
  163. err = C.vips_heifload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), &tmp)
  164. }
  165. if err != 0 {
  166. return vipsError()
  167. }
  168. C.swap_and_clear(&img.VipsImage, tmp)
  169. return nil
  170. }
  171. func (img *vipsImage) Save(imgtype imageType, quality int) ([]byte, context.CancelFunc, error) {
  172. var ptr unsafe.Pointer
  173. cancel := func() {
  174. C.g_free_go(&ptr)
  175. }
  176. err := C.int(0)
  177. imgsize := C.size_t(0)
  178. switch imgtype {
  179. case imageTypeJPEG:
  180. err = C.vips_jpegsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality), vipsConf.JpegProgressive)
  181. case imageTypePNG:
  182. err = C.vips_pngsave_go(img.VipsImage, &ptr, &imgsize, vipsConf.PngInterlaced, vipsConf.PngQuantize, vipsConf.PngQuantizationColors)
  183. case imageTypeWEBP:
  184. err = C.vips_webpsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality))
  185. case imageTypeGIF:
  186. err = C.vips_gifsave_go(img.VipsImage, &ptr, &imgsize)
  187. case imageTypeICO:
  188. err = C.vips_icosave_go(img.VipsImage, &ptr, &imgsize)
  189. case imageTypeHEIC:
  190. err = C.vips_heifsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality))
  191. }
  192. if err != 0 {
  193. C.g_free_go(&ptr)
  194. return nil, cancel, vipsError()
  195. }
  196. b := (*[math.MaxInt32]byte)(ptr)[:int(imgsize):int(imgsize)]
  197. return b, cancel, nil
  198. }
  199. func (img *vipsImage) Clear() {
  200. if img.VipsImage != nil {
  201. C.clear_image(&img.VipsImage)
  202. }
  203. }
  204. func (img *vipsImage) Arrayjoin(in []*vipsImage) error {
  205. var tmp *C.VipsImage
  206. arr := make([]*C.VipsImage, len(in))
  207. for i, im := range in {
  208. arr[i] = im.VipsImage
  209. }
  210. if C.vips_arrayjoin_go(&arr[0], &tmp, C.int(len(arr))) != 0 {
  211. return vipsError()
  212. }
  213. C.swap_and_clear(&img.VipsImage, tmp)
  214. return nil
  215. }
  216. func vipsSupportAnimation(imgtype imageType) bool {
  217. return imgtype == imageTypeGIF ||
  218. (imgtype == imageTypeWEBP && C.vips_support_webp_animation() != 0)
  219. }
  220. func (img *vipsImage) IsAnimated() bool {
  221. return C.vips_is_animated(img.VipsImage) > 0
  222. }
  223. func (img *vipsImage) HasAlpha() bool {
  224. return C.vips_image_hasalpha_go(img.VipsImage) > 0
  225. }
  226. func (img *vipsImage) GetInt(name string) (int, error) {
  227. var i C.int
  228. if C.vips_image_get_int(img.VipsImage, cachedCString(name), &i) != 0 {
  229. return 0, vipsError()
  230. }
  231. return int(i), nil
  232. }
  233. func (img *vipsImage) SetInt(name string, value int) {
  234. C.vips_image_set_int(img.VipsImage, cachedCString(name), C.int(value))
  235. }
  236. func (img *vipsImage) CastUchar() error {
  237. var tmp *C.VipsImage
  238. if C.vips_image_get_format(img.VipsImage) != C.VIPS_FORMAT_UCHAR {
  239. if C.vips_cast_go(img.VipsImage, &tmp, C.VIPS_FORMAT_UCHAR) != 0 {
  240. return vipsError()
  241. }
  242. C.swap_and_clear(&img.VipsImage, tmp)
  243. }
  244. return nil
  245. }
  246. func (img *vipsImage) Rad2Float() error {
  247. var tmp *C.VipsImage
  248. if C.vips_image_get_coding(img.VipsImage) == C.VIPS_CODING_RAD {
  249. if C.vips_rad2float_go(img.VipsImage, &tmp) != 0 {
  250. return vipsError()
  251. }
  252. C.swap_and_clear(&img.VipsImage, tmp)
  253. }
  254. return nil
  255. }
  256. func (img *vipsImage) Resize(scale float64, hasAlpa bool) error {
  257. var tmp *C.VipsImage
  258. if hasAlpa {
  259. if C.vips_resize_with_premultiply(img.VipsImage, &tmp, C.double(scale)) != 0 {
  260. return vipsError()
  261. }
  262. } else {
  263. if C.vips_resize_go(img.VipsImage, &tmp, C.double(scale)) != 0 {
  264. return vipsError()
  265. }
  266. }
  267. C.swap_and_clear(&img.VipsImage, tmp)
  268. return nil
  269. }
  270. func (img *vipsImage) Orientation() C.int {
  271. return C.vips_get_exif_orientation(img.VipsImage)
  272. }
  273. func (img *vipsImage) Rotate(angle int) error {
  274. var tmp *C.VipsImage
  275. if C.vips_rot_go(img.VipsImage, &tmp, C.VipsAngle(angle)) != 0 {
  276. return vipsError()
  277. }
  278. C.swap_and_clear(&img.VipsImage, tmp)
  279. return nil
  280. }
  281. func (img *vipsImage) Flip() error {
  282. var tmp *C.VipsImage
  283. if C.vips_flip_horizontal_go(img.VipsImage, &tmp) != 0 {
  284. return vipsError()
  285. }
  286. C.swap_and_clear(&img.VipsImage, tmp)
  287. return nil
  288. }
  289. func (img *vipsImage) Crop(left, top, width, height int) error {
  290. var tmp *C.VipsImage
  291. if C.vips_extract_area_go(img.VipsImage, &tmp, C.int(left), C.int(top), C.int(width), C.int(height)) != 0 {
  292. return vipsError()
  293. }
  294. C.swap_and_clear(&img.VipsImage, tmp)
  295. return nil
  296. }
  297. func (img *vipsImage) Extract(out *vipsImage, left, top, width, height int) error {
  298. if C.vips_extract_area_go(img.VipsImage, &out.VipsImage, C.int(left), C.int(top), C.int(width), C.int(height)) != 0 {
  299. return vipsError()
  300. }
  301. return nil
  302. }
  303. func (img *vipsImage) SmartCrop(width, height int) error {
  304. var tmp *C.VipsImage
  305. if C.vips_smartcrop_go(img.VipsImage, &tmp, C.int(width), C.int(height)) != 0 {
  306. return vipsError()
  307. }
  308. C.swap_and_clear(&img.VipsImage, tmp)
  309. return nil
  310. }
  311. func (img *vipsImage) EnsureAlpha() error {
  312. var tmp *C.VipsImage
  313. if C.vips_ensure_alpha(img.VipsImage, &tmp) != 0 {
  314. return vipsError()
  315. }
  316. C.swap_and_clear(&img.VipsImage, tmp)
  317. return nil
  318. }
  319. func (img *vipsImage) Flatten(bg rgbColor) error {
  320. var tmp *C.VipsImage
  321. if C.vips_flatten_go(img.VipsImage, &tmp, C.double(bg.R), C.double(bg.G), C.double(bg.B)) != 0 {
  322. return vipsError()
  323. }
  324. C.swap_and_clear(&img.VipsImage, tmp)
  325. return nil
  326. }
  327. func (img *vipsImage) Blur(sigma float32) error {
  328. var tmp *C.VipsImage
  329. if C.vips_gaussblur_go(img.VipsImage, &tmp, C.double(sigma)) != 0 {
  330. return vipsError()
  331. }
  332. C.swap_and_clear(&img.VipsImage, tmp)
  333. return nil
  334. }
  335. func (img *vipsImage) Sharpen(sigma float32) error {
  336. var tmp *C.VipsImage
  337. if C.vips_sharpen_go(img.VipsImage, &tmp, C.double(sigma)) != 0 {
  338. return vipsError()
  339. }
  340. C.swap_and_clear(&img.VipsImage, tmp)
  341. return nil
  342. }
  343. func (img *vipsImage) ImportColourProfile(evenSRGB bool) error {
  344. var tmp *C.VipsImage
  345. if img.VipsImage.Coding != C.VIPS_CODING_NONE {
  346. return nil
  347. }
  348. if img.VipsImage.BandFmt != C.VIPS_FORMAT_UCHAR && img.VipsImage.BandFmt != C.VIPS_FORMAT_USHORT {
  349. return nil
  350. }
  351. profile := (*C.char)(nil)
  352. if C.vips_has_embedded_icc(img.VipsImage) == 0 {
  353. // No embedded profile
  354. // If vips doesn't have built-in profile, use profile built-in to imgproxy for CMYK
  355. // TODO: Remove this. Supporting built-in profiles is pain, vips does it better
  356. if img.VipsImage.Type == C.VIPS_INTERPRETATION_CMYK && C.vips_support_builtin_icc() == 0 {
  357. p, err := cmykProfilePath()
  358. if err != nil {
  359. return err
  360. }
  361. profile = cachedCString(p)
  362. } else {
  363. // imgproxy doesn't have built-in profile for other interpretations,
  364. // so we can't do anything here
  365. return nil
  366. }
  367. }
  368. // Don't import sRGB IEC61966 2.1 unless evenSRGB
  369. if img.VipsImage.Type == C.VIPS_INTERPRETATION_sRGB && !evenSRGB && C.vips_icc_is_srgb_iec61966(img.VipsImage) != 0 {
  370. return nil
  371. }
  372. if C.vips_icc_import_go(img.VipsImage, &tmp, profile) == 0 {
  373. C.swap_and_clear(&img.VipsImage, tmp)
  374. } else {
  375. logWarning("Can't import ICC profile: %s", vipsError())
  376. }
  377. return nil
  378. }
  379. func (img *vipsImage) IsSRGB() bool {
  380. return img.VipsImage.Type == C.VIPS_INTERPRETATION_sRGB
  381. }
  382. func (img *vipsImage) LinearColourspace() error {
  383. return img.Colorspace(C.VIPS_INTERPRETATION_scRGB)
  384. }
  385. func (img *vipsImage) RgbColourspace() error {
  386. return img.Colorspace(C.VIPS_INTERPRETATION_sRGB)
  387. }
  388. func (img *vipsImage) Colorspace(colorspace C.VipsInterpretation) error {
  389. if img.VipsImage.Type != colorspace {
  390. var tmp *C.VipsImage
  391. if C.vips_colourspace_go(img.VipsImage, &tmp, colorspace) != 0 {
  392. return vipsError()
  393. }
  394. C.swap_and_clear(&img.VipsImage, tmp)
  395. }
  396. return nil
  397. }
  398. func (img *vipsImage) CopyMemory() error {
  399. var tmp *C.VipsImage
  400. if tmp = C.vips_image_copy_memory(img.VipsImage); tmp == nil {
  401. return vipsError()
  402. }
  403. C.swap_and_clear(&img.VipsImage, tmp)
  404. return nil
  405. }
  406. func (img *vipsImage) Replicate(width, height int) error {
  407. var tmp *C.VipsImage
  408. if C.vips_replicate_go(img.VipsImage, &tmp, C.int(width), C.int(height)) != 0 {
  409. return vipsError()
  410. }
  411. C.swap_and_clear(&img.VipsImage, tmp)
  412. return nil
  413. }
  414. func (img *vipsImage) Embed(gravity gravityType, width, height int, offX, offY int, bg rgbColor) error {
  415. wmWidth := img.Width()
  416. wmHeight := img.Height()
  417. left := (width-wmWidth+1)/2 + offX
  418. top := (height-wmHeight+1)/2 + offY
  419. if gravity == gravityNorth || gravity == gravityNorthEast || gravity == gravityNorthWest {
  420. top = offY
  421. }
  422. if gravity == gravityEast || gravity == gravityNorthEast || gravity == gravitySouthEast {
  423. left = width - wmWidth - offX
  424. }
  425. if gravity == gravitySouth || gravity == gravitySouthEast || gravity == gravitySouthWest {
  426. top = height - wmHeight - offY
  427. }
  428. if gravity == gravityWest || gravity == gravityNorthWest || gravity == gravitySouthWest {
  429. left = offX
  430. }
  431. if left > width {
  432. left = width - wmWidth
  433. } else if left < -wmWidth {
  434. left = 0
  435. }
  436. if top > height {
  437. top = height - wmHeight
  438. } else if top < -wmHeight {
  439. top = 0
  440. }
  441. if err := img.RgbColourspace(); err != nil {
  442. return err
  443. }
  444. var bgc []C.double
  445. if img.HasAlpha() {
  446. bgc = []C.double{C.double(0)}
  447. } else {
  448. bgc = []C.double{C.double(bg.R), C.double(bg.G), C.double(bg.B)}
  449. }
  450. var tmp *C.VipsImage
  451. if C.vips_embed_go(img.VipsImage, &tmp, C.int(left), C.int(top), C.int(width), C.int(height), &bgc[0], C.int(len(bgc))) != 0 {
  452. return vipsError()
  453. }
  454. C.swap_and_clear(&img.VipsImage, tmp)
  455. return nil
  456. }
  457. func (img *vipsImage) ApplyWatermark(wm *vipsImage, opacity float64) error {
  458. var tmp *C.VipsImage
  459. if C.vips_apply_watermark(img.VipsImage, wm.VipsImage, &tmp, C.double(opacity)) != 0 {
  460. return vipsError()
  461. }
  462. C.swap_and_clear(&img.VipsImage, tmp)
  463. return nil
  464. }