vips.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. package bimg
  2. /*
  3. #cgo pkg-config: vips
  4. #include "vips.h"
  5. */
  6. import "C"
  7. import (
  8. "errors"
  9. "fmt"
  10. "math"
  11. "os"
  12. "runtime"
  13. "strings"
  14. "sync"
  15. "unsafe"
  16. d "github.com/tj/go-debug"
  17. )
  18. // debug is internally used to
  19. var debug = d.Debug("bimg")
  20. // VipsVersion exposes the current libvips semantic version
  21. const VipsVersion = string(C.VIPS_VERSION)
  22. // VipsMajorVersion exposes the current libvips major version number
  23. const VipsMajorVersion = int(C.VIPS_MAJOR_VERSION)
  24. // VipsMinorVersion exposes the current libvips minor version number
  25. const VipsMinorVersion = int(C.VIPS_MINOR_VERSION)
  26. const (
  27. maxCacheMem = 100 * 1024 * 1024
  28. maxCacheSize = 500
  29. )
  30. var (
  31. m sync.Mutex
  32. initialized bool
  33. )
  34. // VipsMemoryInfo represents the memory stats provided by libvips.
  35. type VipsMemoryInfo struct {
  36. Memory int64
  37. MemoryHighwater int64
  38. Allocations int64
  39. }
  40. // vipsSaveOptions represents the internal option used to talk with libvips.
  41. type vipsSaveOptions struct {
  42. Quality int
  43. Compression int
  44. Type ImageType
  45. Interlace bool
  46. NoProfile bool
  47. StripMetadata bool
  48. OutputICC string // Absolute path to the output ICC profile
  49. Interpretation Interpretation
  50. }
  51. type vipsWatermarkOptions struct {
  52. Width C.int
  53. DPI C.int
  54. Margin C.int
  55. NoReplicate C.int
  56. Opacity C.float
  57. Background [3]C.double
  58. }
  59. type vipsWatermarkImageOptions struct {
  60. Left C.int
  61. Top C.int
  62. Opacity C.float
  63. }
  64. type vipsWatermarkTextOptions struct {
  65. Text *C.char
  66. Font *C.char
  67. }
  68. func init() {
  69. Initialize()
  70. }
  71. // Initialize is used to explicitly start libvips in thread-safe way.
  72. // Only call this function if you have previously turned off libvips.
  73. func Initialize() {
  74. if C.VIPS_MAJOR_VERSION <= 7 && C.VIPS_MINOR_VERSION < 40 {
  75. panic("unsupported libvips version!")
  76. }
  77. m.Lock()
  78. runtime.LockOSThread()
  79. defer m.Unlock()
  80. defer runtime.UnlockOSThread()
  81. err := C.vips_init(C.CString("bimg"))
  82. if err != 0 {
  83. panic("unable to start vips!")
  84. }
  85. // Set libvips cache params
  86. C.vips_cache_set_max_mem(maxCacheMem)
  87. C.vips_cache_set_max(maxCacheSize)
  88. // Define a custom thread concurrency limit in libvips (this may generate thread-unsafe issues)
  89. // See: https://github.com/jcupitt/libvips/issues/261#issuecomment-92850414
  90. if os.Getenv("VIPS_CONCURRENCY") == "" {
  91. C.vips_concurrency_set(1)
  92. }
  93. // Enable libvips cache tracing
  94. if os.Getenv("VIPS_TRACE") != "" {
  95. C.vips_enable_cache_set_trace()
  96. }
  97. initialized = true
  98. }
  99. // Shutdown is used to shutdown libvips in a thread-safe way.
  100. // You can call this to drop caches as well.
  101. // If libvips was already initialized, the function is no-op
  102. func Shutdown() {
  103. m.Lock()
  104. defer m.Unlock()
  105. if initialized {
  106. C.vips_shutdown()
  107. initialized = false
  108. }
  109. }
  110. // VipsCacheSetMaxMem Sets the maximum amount of tracked memory allowed before the vips operation cache
  111. // begins to drop entries.
  112. func VipsCacheSetMaxMem(maxCacheMem int) {
  113. C.vips_cache_set_max_mem(C.size_t(maxCacheMem))
  114. }
  115. // VipsCacheSetMax sets the maximum number of operations to keep in the vips operation cache.
  116. func VipsCacheSetMax(maxCacheSize int) {
  117. C.vips_cache_set_max(C.int(maxCacheSize))
  118. }
  119. // VipsCacheDropAll drops the vips operation cache, freeing the allocated memory.
  120. func VipsCacheDropAll() {
  121. C.vips_cache_drop_all()
  122. }
  123. // VipsDebugInfo outputs to stdout libvips collected data. Useful for debugging.
  124. func VipsDebugInfo() {
  125. C.im__print_all()
  126. }
  127. // VipsMemory gets memory info stats from libvips (cache size, memory allocs...)
  128. func VipsMemory() VipsMemoryInfo {
  129. return VipsMemoryInfo{
  130. Memory: int64(C.vips_tracked_get_mem()),
  131. MemoryHighwater: int64(C.vips_tracked_get_mem_highwater()),
  132. Allocations: int64(C.vips_tracked_get_allocs()),
  133. }
  134. }
  135. // VipsIsTypeSupported returns true if the given image type
  136. // is supported by the current libvips compilation.
  137. func VipsIsTypeSupported(t ImageType) bool {
  138. if t == JPEG {
  139. return int(C.vips_type_find_bridge(C.JPEG)) != 0
  140. }
  141. if t == WEBP {
  142. return int(C.vips_type_find_bridge(C.WEBP)) != 0
  143. }
  144. if t == PNG {
  145. return int(C.vips_type_find_bridge(C.PNG)) != 0
  146. }
  147. if t == GIF {
  148. return int(C.vips_type_find_bridge(C.GIF)) != 0
  149. }
  150. if t == PDF {
  151. return int(C.vips_type_find_bridge(C.PDF)) != 0
  152. }
  153. if t == SVG {
  154. return int(C.vips_type_find_bridge(C.SVG)) != 0
  155. }
  156. if t == TIFF {
  157. return int(C.vips_type_find_bridge(C.TIFF)) != 0
  158. }
  159. if t == MAGICK {
  160. return int(C.vips_type_find_bridge(C.MAGICK)) != 0
  161. }
  162. return false
  163. }
  164. // VipsIsTypeSupportedSave returns true if the given image type
  165. // is supported by the current libvips compilation for the
  166. // save operation.
  167. func VipsIsTypeSupportedSave(t ImageType) bool {
  168. if t == JPEG {
  169. return int(C.vips_type_find_save_bridge(C.JPEG)) != 0
  170. }
  171. if t == WEBP {
  172. return int(C.vips_type_find_save_bridge(C.WEBP)) != 0
  173. }
  174. if t == PNG {
  175. return int(C.vips_type_find_save_bridge(C.PNG)) != 0
  176. }
  177. if t == TIFF {
  178. return int(C.vips_type_find_save_bridge(C.TIFF)) != 0
  179. }
  180. return false
  181. }
  182. func vipsExifOrientation(image *C.VipsImage) int {
  183. return int(C.vips_exif_orientation(image))
  184. }
  185. func vipsHasAlpha(image *C.VipsImage) bool {
  186. return int(C.has_alpha_channel(image)) > 0
  187. }
  188. func vipsHasProfile(image *C.VipsImage) bool {
  189. return int(C.has_profile_embed(image)) > 0
  190. }
  191. func vipsWindowSize(name string) float64 {
  192. cname := C.CString(name)
  193. defer C.free(unsafe.Pointer(cname))
  194. return float64(C.interpolator_window_size(cname))
  195. }
  196. func vipsSpace(image *C.VipsImage) string {
  197. return C.GoString(C.vips_enum_nick_bridge(image))
  198. }
  199. func vipsRotate(image *C.VipsImage, angle Angle) (*C.VipsImage, error) {
  200. var out *C.VipsImage
  201. defer C.g_object_unref(C.gpointer(image))
  202. err := C.vips_rotate(image, &out, C.int(angle))
  203. if err != 0 {
  204. return nil, catchVipsError()
  205. }
  206. return out, nil
  207. }
  208. func vipsFlip(image *C.VipsImage, direction Direction) (*C.VipsImage, error) {
  209. var out *C.VipsImage
  210. defer C.g_object_unref(C.gpointer(image))
  211. err := C.vips_flip_bridge(image, &out, C.int(direction))
  212. if err != 0 {
  213. return nil, catchVipsError()
  214. }
  215. return out, nil
  216. }
  217. func vipsZoom(image *C.VipsImage, zoom int) (*C.VipsImage, error) {
  218. var out *C.VipsImage
  219. defer C.g_object_unref(C.gpointer(image))
  220. err := C.vips_zoom_bridge(image, &out, C.int(zoom), C.int(zoom))
  221. if err != 0 {
  222. return nil, catchVipsError()
  223. }
  224. return out, nil
  225. }
  226. func vipsWatermark(image *C.VipsImage, w Watermark) (*C.VipsImage, error) {
  227. var out *C.VipsImage
  228. // Defaults
  229. noReplicate := 0
  230. if w.NoReplicate {
  231. noReplicate = 1
  232. }
  233. text := C.CString(w.Text)
  234. font := C.CString(w.Font)
  235. background := [3]C.double{C.double(w.Background.R), C.double(w.Background.G), C.double(w.Background.B)}
  236. textOpts := vipsWatermarkTextOptions{text, font}
  237. opts := vipsWatermarkOptions{C.int(w.Width), C.int(w.DPI), C.int(w.Margin), C.int(noReplicate), C.float(w.Opacity), background}
  238. defer C.free(unsafe.Pointer(text))
  239. defer C.free(unsafe.Pointer(font))
  240. err := C.vips_watermark(image, &out, (*C.WatermarkTextOptions)(unsafe.Pointer(&textOpts)), (*C.WatermarkOptions)(unsafe.Pointer(&opts)))
  241. if err != 0 {
  242. return nil, catchVipsError()
  243. }
  244. return out, nil
  245. }
  246. func vipsRead(buf []byte) (*C.VipsImage, ImageType, error) {
  247. var image *C.VipsImage
  248. imageType := vipsImageType(buf)
  249. if imageType == UNKNOWN {
  250. return nil, UNKNOWN, errors.New("Unsupported image format")
  251. }
  252. length := C.size_t(len(buf))
  253. imageBuf := unsafe.Pointer(&buf[0])
  254. err := C.vips_init_image(imageBuf, length, C.int(imageType), &image)
  255. if err != 0 {
  256. return nil, UNKNOWN, catchVipsError()
  257. }
  258. return image, imageType, nil
  259. }
  260. func vipsColourspaceIsSupportedBuffer(buf []byte) (bool, error) {
  261. image, _, err := vipsRead(buf)
  262. if err != nil {
  263. return false, err
  264. }
  265. C.g_object_unref(C.gpointer(image))
  266. return vipsColourspaceIsSupported(image), nil
  267. }
  268. func vipsColourspaceIsSupported(image *C.VipsImage) bool {
  269. return int(C.vips_colourspace_issupported_bridge(image)) == 1
  270. }
  271. func vipsInterpretationBuffer(buf []byte) (Interpretation, error) {
  272. image, _, err := vipsRead(buf)
  273. if err != nil {
  274. return InterpretationError, err
  275. }
  276. C.g_object_unref(C.gpointer(image))
  277. return vipsInterpretation(image), nil
  278. }
  279. func vipsInterpretation(image *C.VipsImage) Interpretation {
  280. return Interpretation(C.vips_image_guess_interpretation_bridge(image))
  281. }
  282. func vipsFlattenBackground(image *C.VipsImage, background Color) (*C.VipsImage, error) {
  283. var outImage *C.VipsImage
  284. backgroundC := [3]C.double{
  285. C.double(background.R),
  286. C.double(background.G),
  287. C.double(background.B),
  288. }
  289. if vipsHasAlpha(image) {
  290. err := C.vips_flatten_background_brigde(image, &outImage,
  291. backgroundC[0], backgroundC[1], backgroundC[2])
  292. if int(err) != 0 {
  293. return nil, catchVipsError()
  294. }
  295. C.g_object_unref(C.gpointer(image))
  296. image = outImage
  297. }
  298. return image, nil
  299. }
  300. func vipsPreSave(image *C.VipsImage, o *vipsSaveOptions) (*C.VipsImage, error) {
  301. var outImage *C.VipsImage
  302. // Remove ICC profile metadata
  303. if o.NoProfile {
  304. C.remove_profile(image)
  305. }
  306. // Use a default interpretation and cast it to C type
  307. if o.Interpretation == 0 {
  308. o.Interpretation = InterpretationSRGB
  309. }
  310. interpretation := C.VipsInterpretation(o.Interpretation)
  311. // Apply the proper colour space
  312. if vipsColourspaceIsSupported(image) {
  313. err := C.vips_colourspace_bridge(image, &outImage, interpretation)
  314. if int(err) != 0 {
  315. return nil, catchVipsError()
  316. }
  317. image = outImage
  318. }
  319. if o.OutputICC != "" && vipsHasProfile(image) {
  320. debug("Embedded ICC profile found, trying to convert to %s", o.OutputICC)
  321. outputIccPath := C.CString(o.OutputICC)
  322. defer C.free(unsafe.Pointer(outputIccPath))
  323. err := C.vips_icc_transform_bridge(image, &outImage, outputIccPath)
  324. if int(err) != 0 {
  325. return nil, catchVipsError()
  326. }
  327. image = outImage
  328. }
  329. return image, nil
  330. }
  331. func vipsSave(image *C.VipsImage, o vipsSaveOptions) ([]byte, error) {
  332. defer C.g_object_unref(C.gpointer(image))
  333. tmpImage, err := vipsPreSave(image, &o)
  334. if err != nil {
  335. return nil, err
  336. }
  337. // When an image has an unsupported color space, vipsPreSave
  338. // returns the pointer of the image passed to it unmodified.
  339. // When this occurs, we must take care to not dereference the
  340. // original image a second time; we may otherwise erroneously
  341. // free the object twice.
  342. if tmpImage != image {
  343. defer C.g_object_unref(C.gpointer(tmpImage))
  344. }
  345. length := C.size_t(0)
  346. saveErr := C.int(0)
  347. interlace := C.int(boolToInt(o.Interlace))
  348. quality := C.int(o.Quality)
  349. strip := C.int(boolToInt(o.StripMetadata))
  350. if o.Type != 0 && !IsTypeSupportedSave(o.Type) {
  351. return nil, fmt.Errorf("VIPS cannot save to %#v", ImageTypes[o.Type])
  352. }
  353. var ptr unsafe.Pointer
  354. switch o.Type {
  355. case WEBP:
  356. saveErr = C.vips_webpsave_bridge(tmpImage, &ptr, &length, strip, quality)
  357. case PNG:
  358. saveErr = C.vips_pngsave_bridge(tmpImage, &ptr, &length, strip, C.int(o.Compression), quality, interlace)
  359. case TIFF:
  360. saveErr = C.vips_tiffsave_bridge(tmpImage, &ptr, &length)
  361. default:
  362. saveErr = C.vips_jpegsave_bridge(tmpImage, &ptr, &length, strip, quality, interlace)
  363. }
  364. if int(saveErr) != 0 {
  365. return nil, catchVipsError()
  366. }
  367. buf := C.GoBytes(ptr, C.int(length))
  368. // Clean up
  369. C.g_free(C.gpointer(ptr))
  370. C.vips_error_clear()
  371. return buf, nil
  372. }
  373. func getImageBuffer(image *C.VipsImage) ([]byte, error) {
  374. var ptr unsafe.Pointer
  375. length := C.size_t(0)
  376. interlace := C.int(0)
  377. quality := C.int(100)
  378. err := C.int(0)
  379. err = C.vips_jpegsave_bridge(image, &ptr, &length, 1, quality, interlace)
  380. if int(err) != 0 {
  381. return nil, catchVipsError()
  382. }
  383. defer C.g_free(C.gpointer(ptr))
  384. defer C.vips_error_clear()
  385. return C.GoBytes(ptr, C.int(length)), nil
  386. }
  387. func vipsExtract(image *C.VipsImage, left, top, width, height int) (*C.VipsImage, error) {
  388. var buf *C.VipsImage
  389. defer C.g_object_unref(C.gpointer(image))
  390. if width > MaxSize || height > MaxSize {
  391. return nil, errors.New("Maximum image size exceeded")
  392. }
  393. top, left = max(top), max(left)
  394. err := C.vips_extract_area_bridge(image, &buf, C.int(left), C.int(top), C.int(width), C.int(height))
  395. if err != 0 {
  396. return nil, catchVipsError()
  397. }
  398. return buf, nil
  399. }
  400. func vipsSmartCrop(image *C.VipsImage, width, height int) (*C.VipsImage, error) {
  401. var buf *C.VipsImage
  402. defer C.g_object_unref(C.gpointer(image))
  403. if width > MaxSize || height > MaxSize {
  404. return nil, errors.New("Maximum image size exceeded")
  405. }
  406. err := C.vips_smartcrop_bridge(image, &buf, C.int(width), C.int(height))
  407. if err != 0 {
  408. return nil, catchVipsError()
  409. }
  410. return buf, nil
  411. }
  412. func vipsTrim(image *C.VipsImage) (int, int, int, int, error) {
  413. defer C.g_object_unref(C.gpointer(image))
  414. top := C.int(0)
  415. left := C.int(0)
  416. width := C.int(0)
  417. height := C.int(0)
  418. err := C.vips_find_trim_bridge(image, &top, &left, &width, &height)
  419. if err != 0 {
  420. return 0, 0, 0, 0, catchVipsError()
  421. }
  422. return int(top), int(left), int(width), int(height), nil
  423. }
  424. func vipsShrinkJpeg(buf []byte, input *C.VipsImage, shrink int) (*C.VipsImage, error) {
  425. var image *C.VipsImage
  426. var ptr = unsafe.Pointer(&buf[0])
  427. defer C.g_object_unref(C.gpointer(input))
  428. err := C.vips_jpegload_buffer_shrink(ptr, C.size_t(len(buf)), &image, C.int(shrink))
  429. if err != 0 {
  430. return nil, catchVipsError()
  431. }
  432. return image, nil
  433. }
  434. func vipsShrink(input *C.VipsImage, shrink int) (*C.VipsImage, error) {
  435. var image *C.VipsImage
  436. defer C.g_object_unref(C.gpointer(input))
  437. err := C.vips_shrink_bridge(input, &image, C.double(float64(shrink)), C.double(float64(shrink)))
  438. if err != 0 {
  439. return nil, catchVipsError()
  440. }
  441. return image, nil
  442. }
  443. func vipsReduce(input *C.VipsImage, xshrink float64, yshrink float64) (*C.VipsImage, error) {
  444. var image *C.VipsImage
  445. defer C.g_object_unref(C.gpointer(input))
  446. err := C.vips_reduce_bridge(input, &image, C.double(xshrink), C.double(yshrink))
  447. if err != 0 {
  448. return nil, catchVipsError()
  449. }
  450. return image, nil
  451. }
  452. func vipsEmbed(input *C.VipsImage, left, top, width, height int, extend Extend, background Color) (*C.VipsImage, error) {
  453. var image *C.VipsImage
  454. // Max extend value, see: http://www.vips.ecs.soton.ac.uk/supported/8.4/doc/html/libvips/libvips-conversion.html#VipsExtend
  455. if extend > 5 {
  456. extend = ExtendBackground
  457. }
  458. defer C.g_object_unref(C.gpointer(input))
  459. err := C.vips_embed_bridge(input, &image, C.int(left), C.int(top), C.int(width),
  460. C.int(height), C.int(extend), C.double(background.R), C.double(background.G), C.double(background.B))
  461. if err != 0 {
  462. return nil, catchVipsError()
  463. }
  464. return image, nil
  465. }
  466. func vipsAffine(input *C.VipsImage, residualx, residualy float64, i Interpolator) (*C.VipsImage, error) {
  467. var image *C.VipsImage
  468. cstring := C.CString(i.String())
  469. interpolator := C.vips_interpolate_new(cstring)
  470. defer C.free(unsafe.Pointer(cstring))
  471. defer C.g_object_unref(C.gpointer(input))
  472. defer C.g_object_unref(C.gpointer(interpolator))
  473. err := C.vips_affine_interpolator(input, &image, C.double(residualx), 0, 0, C.double(residualy), interpolator)
  474. if err != 0 {
  475. return nil, catchVipsError()
  476. }
  477. return image, nil
  478. }
  479. func vipsImageType(buf []byte) ImageType {
  480. if len(buf) < 12 {
  481. return UNKNOWN
  482. }
  483. if buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF {
  484. return JPEG
  485. }
  486. if IsTypeSupported(GIF) && buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46 {
  487. return GIF
  488. }
  489. if buf[0] == 0x89 && buf[1] == 0x50 && buf[2] == 0x4E && buf[3] == 0x47 {
  490. return PNG
  491. }
  492. if IsTypeSupported(TIFF) &&
  493. ((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
  494. (buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) {
  495. return TIFF
  496. }
  497. if IsTypeSupported(PDF) && buf[0] == 0x25 && buf[1] == 0x50 && buf[2] == 0x44 && buf[3] == 0x46 {
  498. return PDF
  499. }
  500. if IsTypeSupported(WEBP) && buf[8] == 0x57 && buf[9] == 0x45 && buf[10] == 0x42 && buf[11] == 0x50 {
  501. return WEBP
  502. }
  503. if IsTypeSupported(SVG) && IsSVGImage(buf) {
  504. return SVG
  505. }
  506. if IsTypeSupported(MAGICK) && strings.HasSuffix(readImageType(buf), "MagickBuffer") {
  507. return MAGICK
  508. }
  509. return UNKNOWN
  510. }
  511. func readImageType(buf []byte) string {
  512. length := C.size_t(len(buf))
  513. imageBuf := unsafe.Pointer(&buf[0])
  514. load := C.vips_foreign_find_load_buffer(imageBuf, length)
  515. return C.GoString(load)
  516. }
  517. func catchVipsError() error {
  518. s := C.GoString(C.vips_error_buffer())
  519. C.vips_error_clear()
  520. C.vips_thread_shutdown()
  521. return errors.New(s)
  522. }
  523. func boolToInt(b bool) int {
  524. if b {
  525. return 1
  526. }
  527. return 0
  528. }
  529. func vipsGaussianBlur(image *C.VipsImage, o GaussianBlur) (*C.VipsImage, error) {
  530. var out *C.VipsImage
  531. defer C.g_object_unref(C.gpointer(image))
  532. err := C.vips_gaussblur_bridge(image, &out, C.double(o.Sigma), C.double(o.MinAmpl))
  533. if err != 0 {
  534. return nil, catchVipsError()
  535. }
  536. return out, nil
  537. }
  538. func vipsSharpen(image *C.VipsImage, o Sharpen) (*C.VipsImage, error) {
  539. var out *C.VipsImage
  540. defer C.g_object_unref(C.gpointer(image))
  541. err := C.vips_sharpen_bridge(image, &out, C.int(o.Radius), C.double(o.X1), C.double(o.Y2), C.double(o.Y3), C.double(o.M1), C.double(o.M2))
  542. if err != 0 {
  543. return nil, catchVipsError()
  544. }
  545. return out, nil
  546. }
  547. func max(x int) int {
  548. return int(math.Max(float64(x), 0))
  549. }
  550. func vipsDrawWatermark(image *C.VipsImage, o WatermarkImage) (*C.VipsImage, error) {
  551. var out *C.VipsImage
  552. watermark, _, e := vipsRead(o.Buf)
  553. if e != nil {
  554. return nil, e
  555. }
  556. opts := vipsWatermarkImageOptions{C.int(o.Left), C.int(o.Top), C.float(o.Opacity)}
  557. err := C.vips_watermark_image(image, watermark, &out, (*C.WatermarkImageOptions)(unsafe.Pointer(&opts)))
  558. if err != 0 {
  559. return nil, catchVipsError()
  560. }
  561. return out, nil
  562. }