vips.go 13 KB

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