vips.go 15 KB

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