vips.go 14 KB

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