1
0

vips.go 14 KB

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