vips.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. package vips
  2. /*
  3. #cgo pkg-config: vips
  4. #cgo CFLAGS: -O3
  5. #cgo LDFLAGS: -lm
  6. #include "vips.h"
  7. */
  8. import "C"
  9. import (
  10. "context"
  11. "math"
  12. "net/http"
  13. "os"
  14. "regexp"
  15. "runtime"
  16. "strings"
  17. "sync"
  18. "time"
  19. "unsafe"
  20. log "github.com/sirupsen/logrus"
  21. "github.com/imgproxy/imgproxy/v3/config"
  22. "github.com/imgproxy/imgproxy/v3/ierrors"
  23. "github.com/imgproxy/imgproxy/v3/imagedata"
  24. "github.com/imgproxy/imgproxy/v3/imagetype"
  25. "github.com/imgproxy/imgproxy/v3/imath"
  26. "github.com/imgproxy/imgproxy/v3/metrics/cloudwatch"
  27. "github.com/imgproxy/imgproxy/v3/metrics/datadog"
  28. "github.com/imgproxy/imgproxy/v3/metrics/newrelic"
  29. "github.com/imgproxy/imgproxy/v3/metrics/otel"
  30. "github.com/imgproxy/imgproxy/v3/metrics/prometheus"
  31. )
  32. const (
  33. webpPresetDefault = "default"
  34. webpPresetPhoto = "photo"
  35. webpPresetPicture = "picture"
  36. webpPresetDrawing = "drawing"
  37. webpPresetIcon = "icon"
  38. webpPresetText = "text"
  39. )
  40. type Image struct {
  41. VipsImage *C.VipsImage
  42. }
  43. var (
  44. cWebpPreset = map[string]C.VipsForeignWebpPreset{
  45. webpPresetDefault: C.VIPS_FOREIGN_WEBP_PRESET_DEFAULT,
  46. webpPresetPhoto: C.VIPS_FOREIGN_WEBP_PRESET_PHOTO,
  47. webpPresetPicture: C.VIPS_FOREIGN_WEBP_PRESET_PICTURE,
  48. webpPresetDrawing: C.VIPS_FOREIGN_WEBP_PRESET_DRAWING,
  49. webpPresetIcon: C.VIPS_FOREIGN_WEBP_PRESET_ICON,
  50. webpPresetText: C.VIPS_FOREIGN_WEBP_PRESET_TEXT,
  51. }
  52. )
  53. var (
  54. typeSupportLoad sync.Map
  55. typeSupportSave sync.Map
  56. gifResolutionLimit int
  57. )
  58. var vipsConf struct {
  59. JpegProgressive C.int
  60. PngInterlaced C.int
  61. PngQuantize C.int
  62. PngQuantizationColors C.int
  63. AvifSpeed C.int
  64. JxlEffort C.int
  65. WebpEffort C.int
  66. WebpPreset C.VipsForeignWebpPreset
  67. PngUnlimited C.int
  68. SvgUnlimited C.int
  69. }
  70. var badImageErrRe = []*regexp.Regexp{
  71. regexp.MustCompile(`^(\S+)load_buffer: `),
  72. regexp.MustCompile(`^VipsJpeg: `),
  73. regexp.MustCompile(`^tiff2vips: `),
  74. regexp.MustCompile(`^webp2vips: `),
  75. }
  76. func Init() error {
  77. runtime.LockOSThread()
  78. defer runtime.UnlockOSThread()
  79. if err := C.vips_initialize(); err != 0 {
  80. C.vips_shutdown()
  81. return newVipsError("unable to start vips!")
  82. }
  83. // Disable libvips cache. Since processing pipeline is fine tuned, we won't get much profit from it.
  84. // Enabled cache can cause SIGSEGV on Musl-based systems like Alpine.
  85. C.vips_cache_set_max_mem(0)
  86. C.vips_cache_set_max(0)
  87. if lambdaFn := os.Getenv("AWS_LAMBDA_FUNCTION_NAME"); len(lambdaFn) > 0 {
  88. // Set vips concurrency level to GOMAXPROCS if we are running in AWS Lambda
  89. // since each function processes only one request at a time
  90. // so we can use all available CPU cores
  91. C.vips_concurrency_set(C.int(imath.Max(1, runtime.GOMAXPROCS(0))))
  92. } else {
  93. C.vips_concurrency_set(1)
  94. }
  95. if len(os.Getenv("IMGPROXY_VIPS_LEAK_CHECK")) > 0 {
  96. C.vips_leak_set(C.gboolean(1))
  97. }
  98. if len(os.Getenv("IMGPROXY_VIPS_CACHE_TRACE")) > 0 {
  99. C.vips_cache_set_trace(C.gboolean(1))
  100. }
  101. gifResolutionLimit = int(C.gif_resolution_limit())
  102. vipsConf.JpegProgressive = gbool(config.JpegProgressive)
  103. vipsConf.PngInterlaced = gbool(config.PngInterlaced)
  104. vipsConf.PngQuantize = gbool(config.PngQuantize)
  105. vipsConf.PngQuantizationColors = C.int(config.PngQuantizationColors)
  106. vipsConf.AvifSpeed = C.int(config.AvifSpeed)
  107. vipsConf.JxlEffort = C.int(config.JxlEffort)
  108. vipsConf.WebpEffort = C.int(config.WebpEffort)
  109. vipsConf.PngUnlimited = gbool(config.PngUnlimited)
  110. vipsConf.SvgUnlimited = gbool(config.SvgUnlimited)
  111. if p, ok := cWebpPreset[config.WebpPreset]; ok {
  112. vipsConf.WebpPreset = p
  113. } else {
  114. return newVipsErrorf("invalid libwebp preset: %s", config.WebpPreset)
  115. }
  116. prometheus.AddGaugeFunc(
  117. "vips_memory_bytes",
  118. "A gauge of the vips tracked memory usage in bytes.",
  119. GetMem,
  120. )
  121. prometheus.AddGaugeFunc(
  122. "vips_max_memory_bytes",
  123. "A gauge of the max vips tracked memory usage in bytes.",
  124. GetMemHighwater,
  125. )
  126. prometheus.AddGaugeFunc(
  127. "vips_allocs",
  128. "A gauge of the number of active vips allocations.",
  129. GetAllocs,
  130. )
  131. datadog.AddGaugeFunc("vips.memory", GetMem)
  132. datadog.AddGaugeFunc("vips.max_memory", GetMemHighwater)
  133. datadog.AddGaugeFunc("vips.allocs", GetAllocs)
  134. newrelic.AddGaugeFunc("vips.memory", GetMem)
  135. newrelic.AddGaugeFunc("vips.max_memory", GetMemHighwater)
  136. newrelic.AddGaugeFunc("vips.allocs", GetAllocs)
  137. otel.AddGaugeFunc(
  138. "vips_memory_bytes",
  139. "A gauge of the vips tracked memory usage in bytes.",
  140. "By",
  141. GetMem,
  142. )
  143. otel.AddGaugeFunc(
  144. "vips_max_memory_bytes",
  145. "A gauge of the max vips tracked memory usage in bytes.",
  146. "By",
  147. GetMemHighwater,
  148. )
  149. otel.AddGaugeFunc(
  150. "vips_allocs",
  151. "A gauge of the number of active vips allocations.",
  152. "1",
  153. GetAllocs,
  154. )
  155. cloudwatch.AddGaugeFunc("VipsMemory", "Bytes", GetMem)
  156. cloudwatch.AddGaugeFunc("VipsMaxMemory", "Bytes", GetMemHighwater)
  157. cloudwatch.AddGaugeFunc("VipsAllocs", "Count", GetAllocs)
  158. return nil
  159. }
  160. func Shutdown() {
  161. C.vips_shutdown()
  162. }
  163. func GetMem() float64 {
  164. return float64(C.vips_tracked_get_mem())
  165. }
  166. func GetMemHighwater() float64 {
  167. return float64(C.vips_tracked_get_mem_highwater())
  168. }
  169. func GetAllocs() float64 {
  170. return float64(C.vips_tracked_get_allocs())
  171. }
  172. func Health() error {
  173. timer := time.NewTimer(5 * time.Second)
  174. defer timer.Stop()
  175. done := make(chan struct{})
  176. var err error
  177. go func(done chan struct{}) {
  178. runtime.LockOSThread()
  179. defer runtime.UnlockOSThread()
  180. defer Cleanup()
  181. if C.vips_health() != 0 {
  182. err = Error()
  183. }
  184. close(done)
  185. }(done)
  186. select {
  187. case <-done:
  188. return err
  189. case <-timer.C:
  190. return context.DeadlineExceeded
  191. }
  192. }
  193. func Cleanup() {
  194. C.vips_cleanup()
  195. }
  196. func Error() error {
  197. defer C.vips_error_clear()
  198. errstr := strings.TrimSpace(C.GoString(C.vips_error_buffer()))
  199. err := newVipsError(errstr)
  200. for _, re := range badImageErrRe {
  201. if re.MatchString(errstr) {
  202. return ierrors.Wrap(
  203. err, 0,
  204. ierrors.WithStatusCode(http.StatusUnprocessableEntity),
  205. ierrors.WithPublicMessage("Broken or unsupported image"),
  206. )
  207. }
  208. }
  209. return err
  210. }
  211. func hasOperation(name string) bool {
  212. return C.vips_type_find(cachedCString("VipsOperation"), cachedCString(name)) != 0
  213. }
  214. func SupportsLoad(it imagetype.Type) bool {
  215. if sup, ok := typeSupportLoad.Load(it); ok {
  216. return sup.(bool)
  217. }
  218. sup := false
  219. switch it {
  220. case imagetype.JPEG:
  221. sup = hasOperation("jpegload_buffer")
  222. case imagetype.JXL:
  223. sup = hasOperation("jxlload_buffer")
  224. case imagetype.PNG:
  225. sup = hasOperation("pngload_buffer")
  226. case imagetype.WEBP:
  227. sup = hasOperation("webpload_buffer")
  228. case imagetype.GIF:
  229. sup = hasOperation("gifload_buffer")
  230. case imagetype.ICO, imagetype.BMP:
  231. sup = true
  232. case imagetype.SVG:
  233. sup = hasOperation("svgload_buffer")
  234. case imagetype.HEIC, imagetype.AVIF:
  235. sup = hasOperation("heifload_buffer")
  236. case imagetype.TIFF:
  237. sup = hasOperation("tiffload_buffer")
  238. }
  239. typeSupportLoad.Store(it, sup)
  240. return sup
  241. }
  242. func SupportsSave(it imagetype.Type) bool {
  243. if sup, ok := typeSupportSave.Load(it); ok {
  244. return sup.(bool)
  245. }
  246. sup := false
  247. switch it {
  248. case imagetype.JPEG:
  249. sup = hasOperation("jpegsave_buffer")
  250. case imagetype.JXL:
  251. sup = hasOperation("jxlsave_buffer")
  252. case imagetype.PNG, imagetype.ICO:
  253. sup = hasOperation("pngsave_buffer")
  254. case imagetype.WEBP:
  255. sup = hasOperation("webpsave_buffer")
  256. case imagetype.GIF:
  257. sup = hasOperation("gifsave_buffer")
  258. case imagetype.HEIC, imagetype.AVIF:
  259. sup = hasOperation("heifsave_buffer")
  260. case imagetype.BMP:
  261. sup = true
  262. case imagetype.TIFF:
  263. sup = hasOperation("tiffsave_buffer")
  264. }
  265. typeSupportSave.Store(it, sup)
  266. return sup
  267. }
  268. func GifResolutionLimit() int {
  269. return gifResolutionLimit
  270. }
  271. func gbool(b bool) C.gboolean {
  272. if b {
  273. return C.gboolean(1)
  274. }
  275. return C.gboolean(0)
  276. }
  277. func cRGB(c Color) C.RGB {
  278. return C.RGB{
  279. r: C.double(c.R),
  280. g: C.double(c.G),
  281. b: C.double(c.B),
  282. }
  283. }
  284. func ptrToBytes(ptr unsafe.Pointer, size int) []byte {
  285. return (*[math.MaxInt32]byte)(ptr)[:int(size):int(size)]
  286. }
  287. func (img *Image) Width() int {
  288. return int(img.VipsImage.Xsize)
  289. }
  290. func (img *Image) Height() int {
  291. return int(img.VipsImage.Ysize)
  292. }
  293. func (img *Image) Pages() int {
  294. p, err := img.GetIntDefault("n-pages", 1)
  295. if err != nil {
  296. return 1
  297. }
  298. return p
  299. }
  300. func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64, pages int) error {
  301. if imgdata.Type == imagetype.ICO {
  302. return img.loadIco(imgdata.Data, shrink, scale, pages)
  303. }
  304. if imgdata.Type == imagetype.BMP {
  305. return img.loadBmp(imgdata.Data, true)
  306. }
  307. var tmp *C.VipsImage
  308. data := unsafe.Pointer(&imgdata.Data[0])
  309. dataSize := C.size_t(len(imgdata.Data))
  310. err := C.int(0)
  311. switch imgdata.Type {
  312. case imagetype.JPEG:
  313. err = C.vips_jpegload_go(data, dataSize, C.int(shrink), &tmp)
  314. case imagetype.JXL:
  315. err = C.vips_jxlload_go(data, dataSize, C.int(pages), &tmp)
  316. case imagetype.PNG:
  317. err = C.vips_pngload_go(data, dataSize, &tmp, vipsConf.PngUnlimited)
  318. case imagetype.WEBP:
  319. err = C.vips_webpload_go(data, dataSize, C.double(scale), C.int(pages), &tmp)
  320. case imagetype.GIF:
  321. err = C.vips_gifload_go(data, dataSize, C.int(pages), &tmp)
  322. case imagetype.SVG:
  323. err = C.vips_svgload_go(data, dataSize, C.double(scale), &tmp, vipsConf.SvgUnlimited)
  324. case imagetype.HEIC, imagetype.AVIF:
  325. err = C.vips_heifload_go(data, dataSize, &tmp, C.int(0))
  326. case imagetype.TIFF:
  327. err = C.vips_tiffload_go(data, dataSize, &tmp)
  328. default:
  329. return newVipsError("Usupported image type to load")
  330. }
  331. if err != 0 {
  332. return Error()
  333. }
  334. C.swap_and_clear(&img.VipsImage, tmp)
  335. if imgdata.Type == imagetype.TIFF {
  336. if C.vips_fix_float_tiff(img.VipsImage, &tmp) == 0 {
  337. C.swap_and_clear(&img.VipsImage, tmp)
  338. } else {
  339. log.Warnf("Can't fix TIFF: %s", Error())
  340. }
  341. }
  342. return nil
  343. }
  344. func (img *Image) LoadThumbnail(imgdata *imagedata.ImageData) error {
  345. if imgdata.Type != imagetype.HEIC && imgdata.Type != imagetype.AVIF {
  346. return newVipsError("Usupported image type to load thumbnail")
  347. }
  348. var tmp *C.VipsImage
  349. data := unsafe.Pointer(&imgdata.Data[0])
  350. dataSize := C.size_t(len(imgdata.Data))
  351. if err := C.vips_heifload_go(data, dataSize, &tmp, C.int(1)); err != 0 {
  352. return Error()
  353. }
  354. C.swap_and_clear(&img.VipsImage, tmp)
  355. return nil
  356. }
  357. func (img *Image) Save(imgtype imagetype.Type, quality int) (*imagedata.ImageData, error) {
  358. if imgtype == imagetype.ICO {
  359. return img.saveAsIco()
  360. }
  361. if imgtype == imagetype.BMP {
  362. return img.saveAsBmp()
  363. }
  364. var ptr unsafe.Pointer
  365. cancel := func() {
  366. C.g_free_go(&ptr)
  367. }
  368. err := C.int(0)
  369. imgsize := C.size_t(0)
  370. switch imgtype {
  371. case imagetype.JPEG:
  372. err = C.vips_jpegsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality), vipsConf.JpegProgressive)
  373. case imagetype.JXL:
  374. err = C.vips_jxlsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality), vipsConf.JxlEffort)
  375. case imagetype.PNG:
  376. err = C.vips_pngsave_go(img.VipsImage, &ptr, &imgsize, vipsConf.PngInterlaced, vipsConf.PngQuantize, vipsConf.PngQuantizationColors)
  377. case imagetype.WEBP:
  378. err = C.vips_webpsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality), vipsConf.WebpEffort, vipsConf.WebpPreset)
  379. case imagetype.GIF:
  380. err = C.vips_gifsave_go(img.VipsImage, &ptr, &imgsize)
  381. case imagetype.HEIC:
  382. err = C.vips_heifsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality))
  383. case imagetype.AVIF:
  384. err = C.vips_avifsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality), vipsConf.AvifSpeed)
  385. case imagetype.TIFF:
  386. err = C.vips_tiffsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality))
  387. default:
  388. return nil, newVipsError("Usupported image type to save")
  389. }
  390. if err != 0 {
  391. cancel()
  392. return nil, Error()
  393. }
  394. imgdata := imagedata.ImageData{
  395. Type: imgtype,
  396. Data: ptrToBytes(ptr, int(imgsize)),
  397. }
  398. imgdata.SetCancel(cancel)
  399. return &imgdata, nil
  400. }
  401. func (img *Image) Clear() {
  402. if img.VipsImage != nil {
  403. C.clear_image(&img.VipsImage)
  404. }
  405. }
  406. func (img *Image) LineCache(lines int) error {
  407. var tmp *C.VipsImage
  408. if C.vips_linecache_seq(img.VipsImage, &tmp, C.int(lines)) != 0 {
  409. return Error()
  410. }
  411. C.swap_and_clear(&img.VipsImage, tmp)
  412. return nil
  413. }
  414. func (img *Image) Arrayjoin(in []*Image) error {
  415. var tmp *C.VipsImage
  416. arr := make([]*C.VipsImage, len(in))
  417. for i, im := range in {
  418. arr[i] = im.VipsImage
  419. }
  420. if C.vips_arrayjoin_go(&arr[0], &tmp, C.int(len(arr))) != 0 {
  421. return Error()
  422. }
  423. C.swap_and_clear(&img.VipsImage, tmp)
  424. return nil
  425. }
  426. func (img *Image) Swap(in *Image) {
  427. img.VipsImage, in.VipsImage = in.VipsImage, img.VipsImage
  428. }
  429. func (img *Image) IsAnimated() bool {
  430. return C.vips_is_animated(img.VipsImage) > 0
  431. }
  432. func (img *Image) HasAlpha() bool {
  433. return C.vips_image_hasalpha(img.VipsImage) > 0
  434. }
  435. func (img *Image) GetInt(name string) (int, error) {
  436. var i C.int
  437. if C.vips_image_get_int(img.VipsImage, cachedCString(name), &i) != 0 {
  438. return 0, Error()
  439. }
  440. return int(i), nil
  441. }
  442. func (img *Image) GetIntDefault(name string, def int) (int, error) {
  443. if C.vips_image_get_typeof(img.VipsImage, cachedCString(name)) == 0 {
  444. return def, nil
  445. }
  446. return img.GetInt(name)
  447. }
  448. func (img *Image) GetIntSlice(name string) ([]int, error) {
  449. var ptr unsafe.Pointer
  450. size := C.int(0)
  451. if C.vips_image_get_array_int_go(img.VipsImage, cachedCString(name), (**C.int)(unsafe.Pointer(&ptr)), &size) != 0 {
  452. return nil, Error()
  453. }
  454. if size == 0 {
  455. return []int{}, nil
  456. }
  457. cOut := (*[math.MaxInt32]C.int)(ptr)[:int(size):int(size)]
  458. out := make([]int, int(size))
  459. for i, el := range cOut {
  460. out[i] = int(el)
  461. }
  462. return out, nil
  463. }
  464. func (img *Image) GetIntSliceDefault(name string, def []int) ([]int, error) {
  465. if C.vips_image_get_typeof(img.VipsImage, cachedCString(name)) == 0 {
  466. return def, nil
  467. }
  468. return img.GetIntSlice(name)
  469. }
  470. func (img *Image) GetDouble(name string) (float64, error) {
  471. var d C.double
  472. if C.vips_image_get_double(img.VipsImage, cachedCString(name), &d) != 0 {
  473. return 0, Error()
  474. }
  475. return float64(d), nil
  476. }
  477. func (img *Image) GetDoubleDefault(name string, def float64) (float64, error) {
  478. if C.vips_image_get_typeof(img.VipsImage, cachedCString(name)) == 0 {
  479. return def, nil
  480. }
  481. return img.GetDouble(name)
  482. }
  483. func (img *Image) GetBlob(name string) ([]byte, error) {
  484. var (
  485. tmp unsafe.Pointer
  486. size C.size_t
  487. )
  488. if C.vips_image_get_blob(img.VipsImage, cachedCString(name), &tmp, &size) != 0 {
  489. return nil, Error()
  490. }
  491. return C.GoBytes(tmp, C.int(size)), nil
  492. }
  493. func (img *Image) SetInt(name string, value int) {
  494. C.vips_image_set_int(img.VipsImage, cachedCString(name), C.int(value))
  495. }
  496. func (img *Image) SetIntSlice(name string, value []int) {
  497. in := make([]C.int, len(value))
  498. for i, el := range value {
  499. in[i] = C.int(el)
  500. }
  501. C.vips_image_set_array_int_go(img.VipsImage, cachedCString(name), &in[0], C.int(len(value)))
  502. }
  503. func (img *Image) SetDouble(name string, value float64) {
  504. C.vips_image_set_double(img.VipsImage, cachedCString(name), C.double(value))
  505. }
  506. func (img *Image) SetBlob(name string, value []byte) {
  507. defer runtime.KeepAlive(value)
  508. C.vips_image_set_blob_copy(img.VipsImage, cachedCString(name), unsafe.Pointer(&value[0]), C.size_t(len(value)))
  509. }
  510. func (img *Image) CastUchar() error {
  511. var tmp *C.VipsImage
  512. if C.vips_image_get_format(img.VipsImage) != C.VIPS_FORMAT_UCHAR {
  513. if C.vips_cast_go(img.VipsImage, &tmp, C.VIPS_FORMAT_UCHAR) != 0 {
  514. return Error()
  515. }
  516. C.swap_and_clear(&img.VipsImage, tmp)
  517. }
  518. return nil
  519. }
  520. func (img *Image) Rad2Float() error {
  521. var tmp *C.VipsImage
  522. if C.vips_image_get_coding(img.VipsImage) == C.VIPS_CODING_RAD {
  523. if C.vips_rad2float_go(img.VipsImage, &tmp) != 0 {
  524. return Error()
  525. }
  526. C.swap_and_clear(&img.VipsImage, tmp)
  527. }
  528. return nil
  529. }
  530. func (img *Image) Resize(wscale, hscale float64) error {
  531. var tmp *C.VipsImage
  532. if C.vips_resize_go(img.VipsImage, &tmp, C.double(wscale), C.double(hscale)) != 0 {
  533. return Error()
  534. }
  535. if wscale < 1.0 || hscale < 1.0 {
  536. C.vips_image_set_int(tmp, cachedCString("imgproxy-scaled-down"), 1)
  537. }
  538. C.swap_and_clear(&img.VipsImage, tmp)
  539. return nil
  540. }
  541. func (img *Image) Orientation() C.int {
  542. return C.vips_get_orientation(img.VipsImage)
  543. }
  544. func (img *Image) Rotate(angle int) error {
  545. var tmp *C.VipsImage
  546. vipsAngle := (angle / 90) % 4
  547. if C.vips_rot_go(img.VipsImage, &tmp, C.VipsAngle(vipsAngle)) != 0 {
  548. return Error()
  549. }
  550. C.vips_autorot_remove_angle(tmp)
  551. C.swap_and_clear(&img.VipsImage, tmp)
  552. return nil
  553. }
  554. func (img *Image) Flip() error {
  555. var tmp *C.VipsImage
  556. if C.vips_flip_horizontal_go(img.VipsImage, &tmp) != 0 {
  557. return Error()
  558. }
  559. C.swap_and_clear(&img.VipsImage, tmp)
  560. return nil
  561. }
  562. func (img *Image) Crop(left, top, width, height int) error {
  563. var tmp *C.VipsImage
  564. if C.vips_extract_area_go(img.VipsImage, &tmp, C.int(left), C.int(top), C.int(width), C.int(height)) != 0 {
  565. return Error()
  566. }
  567. C.swap_and_clear(&img.VipsImage, tmp)
  568. return nil
  569. }
  570. func (img *Image) Extract(out *Image, left, top, width, height int) error {
  571. if C.vips_extract_area_go(img.VipsImage, &out.VipsImage, C.int(left), C.int(top), C.int(width), C.int(height)) != 0 {
  572. return Error()
  573. }
  574. return nil
  575. }
  576. func (img *Image) SmartCrop(width, height int) error {
  577. var tmp *C.VipsImage
  578. if C.vips_smartcrop_go(img.VipsImage, &tmp, C.int(width), C.int(height)) != 0 {
  579. return Error()
  580. }
  581. C.swap_and_clear(&img.VipsImage, tmp)
  582. return nil
  583. }
  584. func (img *Image) Trim(threshold float64, smart bool, color Color, equalHor bool, equalVer bool) error {
  585. var tmp *C.VipsImage
  586. if err := img.CopyMemory(); err != nil {
  587. return err
  588. }
  589. if C.vips_trim(img.VipsImage, &tmp, C.double(threshold),
  590. gbool(smart), cRGB(color), gbool(equalHor), gbool(equalVer)) != 0 {
  591. return Error()
  592. }
  593. C.swap_and_clear(&img.VipsImage, tmp)
  594. return nil
  595. }
  596. func (img *Image) Flatten(bg Color) error {
  597. var tmp *C.VipsImage
  598. if C.vips_flatten_go(img.VipsImage, &tmp, cRGB(bg)) != 0 {
  599. return Error()
  600. }
  601. C.swap_and_clear(&img.VipsImage, tmp)
  602. return nil
  603. }
  604. func (img *Image) ApplyFilters(blurSigma, sharpSigma float32, pixelatePixels int) error {
  605. var tmp *C.VipsImage
  606. if C.vips_apply_filters(img.VipsImage, &tmp, C.double(blurSigma), C.double(sharpSigma), C.int(pixelatePixels)) != 0 {
  607. return Error()
  608. }
  609. C.swap_and_clear(&img.VipsImage, tmp)
  610. return nil
  611. }
  612. func (img *Image) IsRGB() bool {
  613. format := C.vips_image_guess_interpretation(img.VipsImage)
  614. return format == C.VIPS_INTERPRETATION_sRGB ||
  615. format == C.VIPS_INTERPRETATION_scRGB ||
  616. format == C.VIPS_INTERPRETATION_RGB16
  617. }
  618. func (img *Image) IsLinear() bool {
  619. return C.vips_image_guess_interpretation(img.VipsImage) == C.VIPS_INTERPRETATION_scRGB
  620. }
  621. func (img *Image) BackupColourProfile() {
  622. var tmp *C.VipsImage
  623. if C.vips_icc_backup(img.VipsImage, &tmp) == 0 {
  624. C.swap_and_clear(&img.VipsImage, tmp)
  625. } else {
  626. log.Warningf("Can't backup ICC profile: %s", Error())
  627. }
  628. }
  629. func (img *Image) RestoreColourProfile() {
  630. var tmp *C.VipsImage
  631. if C.vips_icc_restore(img.VipsImage, &tmp) == 0 {
  632. C.swap_and_clear(&img.VipsImage, tmp)
  633. } else {
  634. log.Warningf("Can't restore ICC profile: %s", Error())
  635. }
  636. }
  637. func (img *Image) ImportColourProfile() error {
  638. var tmp *C.VipsImage
  639. if img.VipsImage.Coding != C.VIPS_CODING_NONE {
  640. return nil
  641. }
  642. if img.VipsImage.BandFmt != C.VIPS_FORMAT_UCHAR && img.VipsImage.BandFmt != C.VIPS_FORMAT_USHORT {
  643. return nil
  644. }
  645. // Don't import is there's no embedded profile or embedded profile is sRGB
  646. if C.vips_has_embedded_icc(img.VipsImage) == 0 ||
  647. (C.vips_image_guess_interpretation(img.VipsImage) == C.VIPS_INTERPRETATION_sRGB &&
  648. C.vips_icc_is_srgb_iec61966(img.VipsImage) == 1) {
  649. return nil
  650. }
  651. if C.vips_icc_import_go(img.VipsImage, &tmp) == 0 {
  652. C.swap_and_clear(&img.VipsImage, tmp)
  653. } else {
  654. log.Warningf("Can't import ICC profile: %s", Error())
  655. }
  656. return nil
  657. }
  658. func (img *Image) ColourProfileImported() bool {
  659. imported, err := img.GetIntDefault("imgproxy-icc-imported", 0)
  660. return imported > 0 && err == nil
  661. }
  662. func (img *Image) ExportColourProfile() error {
  663. var tmp *C.VipsImage
  664. // Don't export is there's no embedded profile or embedded profile is sRGB
  665. if C.vips_has_embedded_icc(img.VipsImage) == 0 || C.vips_icc_is_srgb_iec61966(img.VipsImage) == 1 {
  666. return nil
  667. }
  668. if C.vips_icc_export_go(img.VipsImage, &tmp) == 0 {
  669. C.swap_and_clear(&img.VipsImage, tmp)
  670. } else {
  671. log.Warningf("Can't export ICC profile: %s", Error())
  672. }
  673. return nil
  674. }
  675. func (img *Image) ExportColourProfileToSRGB() error {
  676. var tmp *C.VipsImage
  677. // Don't export is there's no embedded profile or embedded profile is sRGB
  678. if C.vips_has_embedded_icc(img.VipsImage) == 0 || C.vips_icc_is_srgb_iec61966(img.VipsImage) == 1 {
  679. return nil
  680. }
  681. if C.vips_icc_export_srgb(img.VipsImage, &tmp) == 0 {
  682. C.swap_and_clear(&img.VipsImage, tmp)
  683. } else {
  684. log.Warningf("Can't export ICC profile: %s", Error())
  685. }
  686. return nil
  687. }
  688. func (img *Image) TransformColourProfile() error {
  689. var tmp *C.VipsImage
  690. // Don't transform is there's no embedded profile or embedded profile is sRGB
  691. if C.vips_has_embedded_icc(img.VipsImage) == 0 ||
  692. (C.vips_image_guess_interpretation(img.VipsImage) == C.VIPS_INTERPRETATION_sRGB &&
  693. C.vips_icc_is_srgb_iec61966(img.VipsImage) == 1) {
  694. return nil
  695. }
  696. if C.vips_icc_transform_go(img.VipsImage, &tmp) == 0 {
  697. C.swap_and_clear(&img.VipsImage, tmp)
  698. } else {
  699. log.Warningf("Can't transform ICC profile: %s", Error())
  700. }
  701. return nil
  702. }
  703. func (img *Image) RemoveColourProfile() error {
  704. var tmp *C.VipsImage
  705. if C.vips_icc_remove(img.VipsImage, &tmp) == 0 {
  706. C.swap_and_clear(&img.VipsImage, tmp)
  707. } else {
  708. log.Warningf("Can't remove ICC profile: %s", Error())
  709. }
  710. return nil
  711. }
  712. func (img *Image) LinearColourspace() error {
  713. return img.Colorspace(C.VIPS_INTERPRETATION_scRGB)
  714. }
  715. func (img *Image) RgbColourspace() error {
  716. return img.Colorspace(C.VIPS_INTERPRETATION_sRGB)
  717. }
  718. func (img *Image) Colorspace(colorspace C.VipsInterpretation) error {
  719. if img.VipsImage.Type != colorspace {
  720. var tmp *C.VipsImage
  721. if C.vips_colourspace_go(img.VipsImage, &tmp, colorspace) != 0 {
  722. return Error()
  723. }
  724. C.swap_and_clear(&img.VipsImage, tmp)
  725. }
  726. return nil
  727. }
  728. func (img *Image) CopyMemory() error {
  729. var tmp *C.VipsImage
  730. if tmp = C.vips_image_copy_memory(img.VipsImage); tmp == nil {
  731. return Error()
  732. }
  733. C.swap_and_clear(&img.VipsImage, tmp)
  734. return nil
  735. }
  736. func (img *Image) Replicate(width, height int, centered bool) error {
  737. var tmp *C.VipsImage
  738. if C.vips_replicate_go(img.VipsImage, &tmp, C.int(width), C.int(height), gbool(centered)) != 0 {
  739. return Error()
  740. }
  741. C.swap_and_clear(&img.VipsImage, tmp)
  742. return nil
  743. }
  744. func (img *Image) Embed(width, height int, offX, offY int) error {
  745. var tmp *C.VipsImage
  746. if C.vips_embed_go(img.VipsImage, &tmp, C.int(offX), C.int(offY), C.int(width), C.int(height)) != 0 {
  747. return Error()
  748. }
  749. C.swap_and_clear(&img.VipsImage, tmp)
  750. return nil
  751. }
  752. func (img *Image) ApplyWatermark(wm *Image, left, top int, opacity float64) error {
  753. var tmp *C.VipsImage
  754. if C.vips_apply_watermark(img.VipsImage, wm.VipsImage, &tmp, C.int(left), C.int(top), C.double(opacity)) != 0 {
  755. return Error()
  756. }
  757. C.swap_and_clear(&img.VipsImage, tmp)
  758. return nil
  759. }
  760. func (img *Image) Strip(keepExifCopyright bool) error {
  761. var tmp *C.VipsImage
  762. if C.vips_strip(img.VipsImage, &tmp, gbool(keepExifCopyright)) != 0 {
  763. return Error()
  764. }
  765. C.swap_and_clear(&img.VipsImage, tmp)
  766. return nil
  767. }
  768. func (img *Image) StripAll() error {
  769. var tmp *C.VipsImage
  770. if C.vips_strip_all(img.VipsImage, &tmp) != 0 {
  771. return Error()
  772. }
  773. C.swap_and_clear(&img.VipsImage, tmp)
  774. return nil
  775. }