vips.go 16 KB

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