diff --git a/go.mod b/go.mod index e3c7f1b92..c8db95b41 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/coreos/go-oidc/v3 v3.11.0 github.com/cs3org/go-cs3apis v0.0.0-20240724121416-062c4e3046cb github.com/cs3org/reva/v2 v2.25.1-0.20241016145214-e5baaccf6614 + github.com/davidbyttow/govips/v2 v2.15.0 github.com/dhowden/tag v0.0.0-20230630033851-978a0926ee25 github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e github.com/egirna/icap-client v0.1.1 diff --git a/go.sum b/go.sum index 449c064ac..030e65c2a 100644 --- a/go.sum +++ b/go.sum @@ -259,6 +259,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidbyttow/govips/v2 v2.15.0 h1:h3lF+rQElBzGXbQSSPqmE3XGySPhcQo2x3t5l/dZ+pU= +github.com/davidbyttow/govips/v2 v2.15.0/go.mod h1:3OQCHj0nf5Mnrplh5VlNvmx3IhJXyxbAoTJZPflUjmM= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0= @@ -845,6 +847,7 @@ github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDm github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI= github.com/nrdcg/desec v0.5.0/go.mod h1:2ejvMazkav1VdDbv2HeQO7w+Ta1CGHqzQr27ZBYTuEQ= github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= @@ -1248,6 +1251,7 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0= golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1468,6 +1472,7 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= @@ -1640,6 +1645,7 @@ gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UD gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= diff --git a/services/thumbnails/pkg/preprocessor/preprocessor_imaging.go b/services/thumbnails/pkg/preprocessor/preprocessor_imaging.go index 99c7d91a8..4510e234e 100644 --- a/services/thumbnails/pkg/preprocessor/preprocessor_imaging.go +++ b/services/thumbnails/pkg/preprocessor/preprocessor_imaging.go @@ -1,3 +1,5 @@ +//go:build !enable_vips + package preprocessor import ( diff --git a/services/thumbnails/pkg/preprocessor/preprocessor_vips.go b/services/thumbnails/pkg/preprocessor/preprocessor_vips.go new file mode 100644 index 000000000..087bafb1b --- /dev/null +++ b/services/thumbnails/pkg/preprocessor/preprocessor_vips.go @@ -0,0 +1,20 @@ +//go:build enable_vips + +package preprocessor + +import ( + "io" + + "github.com/davidbyttow/govips/v2/vips" +) + +func init() { + vips.LoggingSettings(nil, vips.LogLevelError) +} + +type ImageDecoder struct{} + +func (v ImageDecoder) Convert(r io.Reader) (interface{}, error) { + img, err := vips.NewImageFromReader(r) + return img, err +} diff --git a/services/thumbnails/pkg/thumbnail/encoding_imaging.go b/services/thumbnails/pkg/thumbnail/encoding_imaging.go index 7ae2c09b6..c46352d96 100644 --- a/services/thumbnails/pkg/thumbnail/encoding_imaging.go +++ b/services/thumbnails/pkg/thumbnail/encoding_imaging.go @@ -1,3 +1,5 @@ +//go:build !enable_vips + package thumbnail import ( diff --git a/services/thumbnails/pkg/thumbnail/encoding_vips.go b/services/thumbnails/pkg/thumbnail/encoding_vips.go new file mode 100644 index 000000000..d6371296a --- /dev/null +++ b/services/thumbnails/pkg/thumbnail/encoding_vips.go @@ -0,0 +1,66 @@ +//go:build enable_vips + +package thumbnail + +import ( + "io" + + "github.com/davidbyttow/govips/v2/vips" + "github.com/owncloud/ocis/v2/services/thumbnails/pkg/errors" +) + +// PngEncoder encodes to png +type PngEncoder struct{} + +// Encode encodes to png format +func (e PngEncoder) Encode(w io.Writer, img interface{}) error { + m, ok := img.(*vips.ImageRef) + if !ok { + return errors.ErrInvalidType + } + + buf, _, err := m.ExportPng(vips.NewPngExportParams()) + if err != nil { + return err + } + _, err = w.Write(buf) + return err +} + +// Types returns the png suffix +func (e PngEncoder) Types() []string { + return []string{typePng} +} + +// MimeType returns the mimetype for png files. +func (e PngEncoder) MimeType() string { + return "image/png" +} + +// JpegEncoder encodes to jpg +type JpegEncoder struct{} + +// Encode encodes to jpg +func (e JpegEncoder) Encode(w io.Writer, img interface{}) error { + m, ok := img.(*vips.ImageRef) + if !ok { + return errors.ErrInvalidType + } + + buf, _, err := m.ExportJpeg(vips.NewJpegExportParams()) + if err != nil { + return err + } + _, err = w.Write(buf) + return err +} + +// Types returns the jpg suffixes. +func (e JpegEncoder) Types() []string { + return []string{typeJpeg, typeJpg} +} + +// MimeType returns the mimetype for jpg files. +func (e JpegEncoder) MimeType() string { + return "image/jpeg" +} diff --git a/services/thumbnails/pkg/thumbnail/generator_simple.go b/services/thumbnails/pkg/thumbnail/generator_simple.go index d23af905d..557ee6d84 100644 --- a/services/thumbnails/pkg/thumbnail/generator_simple.go +++ b/services/thumbnails/pkg/thumbnail/generator_simple.go @@ -1,3 +1,5 @@ +//go:build !enable_vips + package thumbnail import ( diff --git a/services/thumbnails/pkg/thumbnail/generator_vips.go b/services/thumbnails/pkg/thumbnail/generator_vips.go new file mode 100644 index 000000000..c2c1bd371 --- /dev/null +++ b/services/thumbnails/pkg/thumbnail/generator_vips.go @@ -0,0 +1,62 @@ +//go:build enable_vips + +package thumbnail + +import ( + "image" + "strings" + + "github.com/davidbyttow/govips/v2/vips" + "github.com/owncloud/ocis/v2/services/thumbnails/pkg/errors" +) + +// SimpleGenerator is the default image generator and is used for all image types expect gif. +type SimpleGenerator struct { + crop vips.Interesting + size vips.Size + process string +} + +func NewSimpleGenerator(filetype, process string) (SimpleGenerator, error) { + switch strings.ToLower(process) { + case "thumbnail": + return SimpleGenerator{crop: vips.InterestingAttention, process: process, size: vips.SizeBoth}, nil + case "fit": + return SimpleGenerator{crop: vips.InterestingNone, process: process, size: vips.SizeBoth}, nil + case "resize": + return SimpleGenerator{crop: vips.InterestingNone, process: process, size: vips.SizeForce}, nil + default: + return SimpleGenerator{crop: vips.InterestingNone, process: process}, nil + } +} + +// ProcessorID returns the processor identification. +func (g SimpleGenerator) ProcessorID() string { + return g.process +} + +// Generate generates a alternative image version. +func (g SimpleGenerator) Generate(size image.Rectangle, img interface{}) (interface{}, error) { + m, ok := img.(*vips.ImageRef) + if !ok { + return nil, errors.ErrInvalidType + } + + if err := m.ThumbnailWithSize(size.Dx(), 0, g.crop, g.size); err != nil { + return nil, err + } + + if err := m.RemoveMetadata(); err != nil { + return nil, err + } + + return m, nil +} + +func (g SimpleGenerator) Dimensions(img interface{}) (image.Rectangle, error) { + m, ok := img.(*vips.ImageRef) + if !ok { + return image.Rectangle{}, errors.ErrInvalidType + } + return image.Rect(0, 0, m.Width(), m.Height()), nil +} diff --git a/vendor/github.com/davidbyttow/govips/v2/LICENSE b/vendor/github.com/davidbyttow/govips/v2/LICENSE new file mode 100644 index 000000000..8d0891d78 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/LICENSE @@ -0,0 +1,24 @@ +The MIT License + +Copyright (c) Simple Things LLC and contributors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/arithmetic.c b/vendor/github.com/davidbyttow/govips/v2/vips/arithmetic.c new file mode 100644 index 000000000..557f613cb --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/arithmetic.c @@ -0,0 +1,71 @@ +#include "arithmetic.h" + +int add(VipsImage *left, VipsImage *right, VipsImage **out) { + return vips_add(left, right, out, NULL); +} + +int multiply(VipsImage *left, VipsImage *right, VipsImage **out) { + return vips_multiply(left, right, out, NULL); +} + +int divide(VipsImage *left, VipsImage *right, VipsImage **out) { + return vips_divide(left, right, out, NULL); +} + +int linear(VipsImage *in, VipsImage **out, double *a, double *b, int n) { + return vips_linear(in, out, a, b, n, NULL); +} + +int linear1(VipsImage *in, VipsImage **out, double a, double b) { + return vips_linear1(in, out, a, b, NULL); +} + +int invert_image(VipsImage *in, VipsImage **out) { + return vips_invert(in, out, NULL); +} + +int average(VipsImage *in, double *out) { + return vips_avg(in, out, NULL); +} + +int find_trim(VipsImage *in, int *left, int *top, int *width, int *height, + double threshold, double r, double g, double b) { + + if (in->Type == VIPS_INTERPRETATION_RGB16 || in->Type == VIPS_INTERPRETATION_GREY16) { + r = 65535 * r / 255; + g = 65535 * g / 255; + b = 65535 * b / 255; + } + + double background[3] = {r, g, b}; + VipsArrayDouble *vipsBackground = vips_array_double_new(background, 3); + + int code = vips_find_trim(in, left, top, width, height, "threshold", threshold, "background", vipsBackground, NULL); + + vips_area_unref(VIPS_AREA(vipsBackground)); + return code; +} + +int getpoint(VipsImage *in, double **vector, int n, int x, int y) { + return vips_getpoint(in, vector, &n, x, y, NULL); +} + +int stats(VipsImage *in, VipsImage **out) { + return vips_stats(in, out, NULL); +} + +int hist_find(VipsImage *in, VipsImage **out) { + return vips_hist_find(in, out, NULL); +} + +int hist_cum(VipsImage *in, VipsImage **out) { + return vips_hist_cum(in, out, NULL); +} + +int hist_norm(VipsImage *in, VipsImage **out) { + return vips_hist_norm(in, out, NULL); +} + +int hist_entropy(VipsImage *in, double *out) { + return vips_hist_entropy(in, out, NULL); +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/arithmetic.go b/vendor/github.com/davidbyttow/govips/v2/vips/arithmetic.go new file mode 100644 index 000000000..3bdf5baa1 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/arithmetic.go @@ -0,0 +1,176 @@ +package vips + +// #include "arithmetic.h" +import "C" +import "unsafe" + +// https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-add +func vipsAdd(left *C.VipsImage, right *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("add") + var out *C.VipsImage + + if err := C.add(left, right, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-multiply +func vipsMultiply(left *C.VipsImage, right *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("multiply") + var out *C.VipsImage + + if err := C.multiply(left, right, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-divide +func vipsDivide(left *C.VipsImage, right *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("divide") + var out *C.VipsImage + + if err := C.divide(left, right, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-linear +func vipsLinear(in *C.VipsImage, a, b []float64, n int) (*C.VipsImage, error) { + incOpCounter("linear") + var out *C.VipsImage + + if err := C.linear(in, &out, (*C.double)(&a[0]), (*C.double)(&b[0]), C.int(n)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-linear1 +func vipsLinear1(in *C.VipsImage, a, b float64) (*C.VipsImage, error) { + incOpCounter("linear1") + var out *C.VipsImage + + if err := C.linear1(in, &out, C.double(a), C.double(b)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-invert +func vipsInvert(in *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("invert") + var out *C.VipsImage + + if err := C.invert_image(in, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-avg +func vipsAverage(in *C.VipsImage) (float64, error) { + incOpCounter("average") + var out C.double + + if err := C.average(in, &out); err != 0 { + return 0, handleVipsError() + } + + return float64(out), nil +} + +// https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-find-trim +func vipsFindTrim(in *C.VipsImage, threshold float64, backgroundColor *Color) (int, int, int, int, error) { + incOpCounter("findTrim") + var left, top, width, height C.int + + if err := C.find_trim(in, &left, &top, &width, &height, C.double(threshold), C.double(backgroundColor.R), + C.double(backgroundColor.G), C.double(backgroundColor.B)); err != 0 { + return -1, -1, -1, -1, handleVipsError() + } + + return int(left), int(top), int(width), int(height), nil +} + +// https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-getpoint +func vipsGetPoint(in *C.VipsImage, n int, x int, y int) ([]float64, error) { + incOpCounter("getpoint") + var out *C.double + defer gFreePointer(unsafe.Pointer(out)) + + if err := C.getpoint(in, &out, C.int(n), C.int(x), C.int(y)); err != 0 { + return nil, handleVipsError() + } + + // maximum n is 4 + return (*[4]float64)(unsafe.Pointer(out))[:n:n], nil +} + +// https://www.libvips.org/API/current/libvips-arithmetic.html#vips-stats +func vipsStats(in *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("stats") + var out *C.VipsImage + + if err := C.stats(in, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://www.libvips.org/API/current/libvips-arithmetic.html#vips-hist-find +func vipsHistFind(in *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("histFind") + var out *C.VipsImage + + if err := C.hist_find(in, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://www.libvips.org/API/current/libvips-histogram.html#vips-hist-norm +func vipsHistNorm(in *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("histNorm") + var out *C.VipsImage + + if err := C.hist_norm(in, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://www.libvips.org/API/current/libvips-histogram.html#vips-hist-cum +func vipsHistCum(in *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("histCum") + var out *C.VipsImage + + if err := C.hist_cum(in, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://www.libvips.org/API/current/libvips-histogram.html#vips-hist-entropy +func vipsHistEntropy(in *C.VipsImage) (float64, error) { + incOpCounter("histEntropy") + var out C.double + + if err := C.hist_entropy(in, &out); err != 0 { + return 0, handleVipsError() + } + + return float64(out), nil +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/arithmetic.h b/vendor/github.com/davidbyttow/govips/v2/vips/arithmetic.h new file mode 100644 index 000000000..693b4fdde --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/arithmetic.h @@ -0,0 +1,20 @@ +// https://libvips.github.io/libvips/API/current/libvips-arithmetic.html + +#include +#include + +int add(VipsImage *left, VipsImage *right, VipsImage **out); +int multiply(VipsImage *left, VipsImage *right, VipsImage **out); +int divide(VipsImage *left, VipsImage *right, VipsImage **out); +int linear(VipsImage *in, VipsImage **out, double *a, double *b, int n); +int linear1(VipsImage *in, VipsImage **out, double a, double b); +int invert_image(VipsImage *in, VipsImage **out); +int average(VipsImage *in, double *out); +int find_trim(VipsImage *in, int *left, int *top, int *width, int *height, + double threshold, double r, double g, double b); +int getpoint(VipsImage *in, double **vector, int n, int x, int y); +int stats(VipsImage *in, VipsImage **out); +int hist_find(VipsImage *in, VipsImage **out); +int hist_cum(VipsImage *in, VipsImage **out); +int hist_norm(VipsImage *in, VipsImage **out); +int hist_entropy(VipsImage *in, double *out); diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/color.c b/vendor/github.com/davidbyttow/govips/v2/vips/color.c new file mode 100644 index 000000000..202440ee1 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/color.c @@ -0,0 +1,22 @@ +#include "color.h" +#include + +int is_colorspace_supported(VipsImage *in) { + return vips_colourspace_issupported(in) ? 1 : 0; +} + +int to_colorspace(VipsImage *in, VipsImage **out, VipsInterpretation space) { + return vips_colourspace(in, out, space, NULL); +} + +// https://libvips.github.io/libvips/API/8.6/libvips-colour.html#vips-icc-transform +int icc_transform(VipsImage *in, VipsImage **out, const char *output_profile, const char *input_profile, VipsIntent intent, + int depth, gboolean embedded) { + return vips_icc_transform( + in, out, output_profile, + "input_profile", input_profile ? input_profile : "none", + "intent", intent, + "depth", depth ? depth : 8, + "embedded", embedded, + NULL); +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/color.go b/vendor/github.com/davidbyttow/govips/v2/vips/color.go new file mode 100644 index 000000000..df5aed529 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/color.go @@ -0,0 +1,96 @@ +package vips + +// #include "color.h" +import "C" + +// Color represents an RGB +type Color struct { + R, G, B uint8 +} + +// ColorRGBA represents an RGB with alpha channel (A) +type ColorRGBA struct { + R, G, B, A uint8 +} + +// Interpretation represents VIPS_INTERPRETATION type +type Interpretation int + +// Interpretation enum +const ( + InterpretationError Interpretation = C.VIPS_INTERPRETATION_ERROR + InterpretationMultiband Interpretation = C.VIPS_INTERPRETATION_MULTIBAND + InterpretationBW Interpretation = C.VIPS_INTERPRETATION_B_W + InterpretationHistogram Interpretation = C.VIPS_INTERPRETATION_HISTOGRAM + InterpretationXYZ Interpretation = C.VIPS_INTERPRETATION_XYZ + InterpretationLAB Interpretation = C.VIPS_INTERPRETATION_LAB + InterpretationCMYK Interpretation = C.VIPS_INTERPRETATION_CMYK + InterpretationLABQ Interpretation = C.VIPS_INTERPRETATION_LABQ + InterpretationRGB Interpretation = C.VIPS_INTERPRETATION_RGB + InterpretationRGB16 Interpretation = C.VIPS_INTERPRETATION_RGB16 + InterpretationCMC Interpretation = C.VIPS_INTERPRETATION_CMC + InterpretationLCH Interpretation = C.VIPS_INTERPRETATION_LCH + InterpretationLABS Interpretation = C.VIPS_INTERPRETATION_LABS + InterpretationSRGB Interpretation = C.VIPS_INTERPRETATION_sRGB + InterpretationYXY Interpretation = C.VIPS_INTERPRETATION_YXY + InterpretationFourier Interpretation = C.VIPS_INTERPRETATION_FOURIER + InterpretationGrey16 Interpretation = C.VIPS_INTERPRETATION_GREY16 + InterpretationMatrix Interpretation = C.VIPS_INTERPRETATION_MATRIX + InterpretationScRGB Interpretation = C.VIPS_INTERPRETATION_scRGB + InterpretationHSV Interpretation = C.VIPS_INTERPRETATION_HSV +) + +// Intent represents VIPS_INTENT type +type Intent int + +// Intent enum +const ( + IntentPerceptual Intent = C.VIPS_INTENT_PERCEPTUAL + IntentRelative Intent = C.VIPS_INTENT_RELATIVE + IntentSaturation Intent = C.VIPS_INTENT_SATURATION + IntentAbsolute Intent = C.VIPS_INTENT_ABSOLUTE + IntentLast Intent = C.VIPS_INTENT_LAST +) + +func vipsIsColorSpaceSupported(in *C.VipsImage) bool { + return C.is_colorspace_supported(in) == 1 +} + +// https://libvips.github.io/libvips/API/current/libvips-colour.html#vips-colourspace +func vipsToColorSpace(in *C.VipsImage, interpretation Interpretation) (*C.VipsImage, error) { + incOpCounter("to_colorspace") + var out *C.VipsImage + + inter := C.VipsInterpretation(interpretation) + + if err := C.to_colorspace(in, &out, inter); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +func vipsICCTransform(in *C.VipsImage, outputProfile string, inputProfile string, intent Intent, depth int, + embedded bool) (*C.VipsImage, error) { + var out *C.VipsImage + var cInputProfile *C.char + var cEmbedded C.gboolean + + cOutputProfile := C.CString(outputProfile) + defer freeCString(cOutputProfile) + + if inputProfile != "" { + cInputProfile = C.CString(inputProfile) + defer freeCString(cInputProfile) + } + + if embedded { + cEmbedded = C.TRUE + } + + if res := C.icc_transform(in, &out, cOutputProfile, cInputProfile, C.VipsIntent(intent), C.int(depth), cEmbedded); res != 0 { + return nil, handleImageError(out) + } + + return out, nil +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/color.h b/vendor/github.com/davidbyttow/govips/v2/vips/color.h new file mode 100644 index 000000000..0f68796a6 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/color.h @@ -0,0 +1,10 @@ +// https://libvips.github.io/libvips/API/current/libvips-colour.html + +#include +#include + +int is_colorspace_supported(VipsImage *in); +int to_colorspace(VipsImage *in, VipsImage **out, VipsInterpretation space); + +int icc_transform(VipsImage *in, VipsImage **out, const char *output_profile, const char *input_profile, VipsIntent intent, + int depth, gboolean embedded); diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/composite.go b/vendor/github.com/davidbyttow/govips/v2/vips/composite.go new file mode 100644 index 000000000..209870928 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/composite.go @@ -0,0 +1,27 @@ +package vips + +// #include +import "C" + +// ImageComposite image to composite param +type ImageComposite struct { + Image *ImageRef + BlendMode BlendMode + X, Y int +} + +func toVipsCompositeStructs(r *ImageRef, datas []*ImageComposite) ([]*C.VipsImage, []C.int, []C.int, []C.int) { + ins := []*C.VipsImage{r.image} + modes := []C.int{} + xs := []C.int{} + ys := []C.int{} + + for _, image := range datas { + ins = append(ins, image.Image.image) + modes = append(modes, C.int(image.BlendMode)) + xs = append(xs, C.int(image.X)) + ys = append(ys, C.int(image.Y)) + } + + return ins, modes, xs, ys +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/conversion.c b/vendor/github.com/davidbyttow/govips/v2/vips/conversion.c new file mode 100644 index 000000000..878e80818 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/conversion.c @@ -0,0 +1,380 @@ +#include "conversion.h" + +int copy_image_changing_interpretation(VipsImage *in, VipsImage **out, + VipsInterpretation interpretation) { + return vips_copy(in, out, "interpretation", interpretation, NULL); +} + +int copy_image_changing_resolution(VipsImage *in, VipsImage **out, double xres, + double yres) { + return vips_copy(in, out, "xres", xres, "yres", yres, NULL); +} + +int copy_image(VipsImage *in, VipsImage **out) { + return vips_copy(in, out, NULL); +} + +int embed_image(VipsImage *in, VipsImage **out, int left, int top, int width, + int height, int extend) { + return vips_embed(in, out, left, top, width, height, "extend", extend, NULL); +} + +int embed_image_background(VipsImage *in, VipsImage **out, int left, int top, int width, + int height, double r, double g, double b, double a) { + + double background[3] = {r, g, b}; + double backgroundRGBA[4] = {r, g, b, a}; + + VipsArrayDouble *vipsBackground; + + if (in->Bands <= 3) { + vipsBackground = vips_array_double_new(background, 3); + } else { + vipsBackground = vips_array_double_new(backgroundRGBA, 4); + } + + int code = vips_embed(in, out, left, top, width, height, + "extend", VIPS_EXTEND_BACKGROUND, "background", vipsBackground, NULL); + + vips_area_unref(VIPS_AREA(vipsBackground)); + return code; +} + +int embed_multi_page_image(VipsImage *in, VipsImage **out, int left, int top, int width, + int height, int extend) { + VipsObject *base = VIPS_OBJECT(vips_image_new()); + int page_height = vips_image_get_page_height(in); + int in_width = in->Xsize; + int n_pages = in->Ysize / page_height; + + VipsImage **page = (VipsImage **) vips_object_local_array(base, n_pages); + VipsImage **copy = (VipsImage **) vips_object_local_array(base, 1); + + // split image into cropped frames + for (int i = 0; i < n_pages; i++) { + if ( + vips_extract_area(in, &page[i], 0, page_height * i, in_width, page_height, NULL) || + vips_embed(page[i], &page[i], left, top, width, height, "extend", extend, NULL) + ) { + g_object_unref(base); + return -1; + } + } + // reassemble frames and set page height + // copy before modifying metadata + if( + vips_arrayjoin(page, ©[0], n_pages, "across", 1, NULL) || + vips_copy(copy[0], out, NULL) + ) { + g_object_unref(base); + return -1; + } + vips_image_set_int(*out, VIPS_META_PAGE_HEIGHT, height); + g_object_unref(base); + return 0; +} + +int embed_multi_page_image_background(VipsImage *in, VipsImage **out, int left, int top, int width, + int height, double r, double g, double b, double a) { + double background[3] = {r, g, b}; + double backgroundRGBA[4] = {r, g, b, a}; + + VipsArrayDouble *vipsBackground; + + if (in->Bands <= 3) { + vipsBackground = vips_array_double_new(background, 3); + } else { + vipsBackground = vips_array_double_new(backgroundRGBA, 4); + } + VipsObject *base = VIPS_OBJECT(vips_image_new()); + int page_height = vips_image_get_page_height(in); + int in_width = in->Xsize; + int n_pages = in->Ysize / page_height; + + VipsImage **page = (VipsImage **) vips_object_local_array(base, n_pages); + VipsImage **copy = (VipsImage **) vips_object_local_array(base, 1); + + // split image into cropped frames + for (int i = 0; i < n_pages; i++) { + if ( + vips_extract_area(in, &page[i], 0, page_height * i, in_width, page_height, NULL) || + vips_embed(page[i], &page[i], left, top, width, height, + "extend", VIPS_EXTEND_BACKGROUND, "background", vipsBackground, NULL) + ) { + vips_area_unref(VIPS_AREA(vipsBackground)); + g_object_unref(base); + return -1; + } + } + // reassemble frames and set page height + // copy before modifying metadata + if( + vips_arrayjoin(page, ©[0], n_pages, "across", 1, NULL) || + vips_copy(copy[0], out, NULL) + ) { + vips_area_unref(VIPS_AREA(vipsBackground)); + g_object_unref(base); + return -1; + } + vips_image_set_int(*out, VIPS_META_PAGE_HEIGHT, height); + vips_area_unref(VIPS_AREA(vipsBackground)); + g_object_unref(base); + return 0; +} + +int flip_image(VipsImage *in, VipsImage **out, int direction) { + return vips_flip(in, out, direction, NULL); +} + +int recomb_image(VipsImage *in, VipsImage **out, VipsImage *m) { + return vips_recomb(in, out, m, NULL); +} + +int extract_image_area(VipsImage *in, VipsImage **out, int left, int top, + int width, int height) { + return vips_extract_area(in, out, left, top, width, height, NULL); +} + +int extract_area_multi_page(VipsImage *in, VipsImage **out, int left, int top, int width, int height) { + VipsObject *base = VIPS_OBJECT(vips_image_new()); + int page_height = vips_image_get_page_height(in); + int n_pages = in->Ysize / page_height; + + VipsImage **page = (VipsImage **) vips_object_local_array(base, n_pages); + VipsImage **copy = (VipsImage **) vips_object_local_array(base, 1); + + // split image into cropped frames + for (int i = 0; i < n_pages; i++) { + if(vips_extract_area(in, &page[i], left, page_height * i + top, width, height, NULL)) { + g_object_unref(base); + return -1; + } + } + // reassemble frames and set page height + // copy before modifying metadata + if( + vips_arrayjoin(page, ©[0], n_pages, "across", 1, NULL) || + vips_copy(copy[0], out, NULL) + ) { + g_object_unref(base); + return -1; + } + vips_image_set_int(*out, VIPS_META_PAGE_HEIGHT, height); + g_object_unref(base); + return 0; +} + +int extract_band(VipsImage *in, VipsImage **out, int band, int num) { + if (num > 0) { + return vips_extract_band(in, out, band, "n", num, NULL); + } + return vips_extract_band(in, out, band, NULL); +} + +int rot_image(VipsImage *in, VipsImage **out, VipsAngle angle) { + return vips_rot(in, out, angle, NULL); +} + +int autorot_image(VipsImage *in, VipsImage **out) { + return vips_autorot(in, out, NULL); +} + +int zoom_image(VipsImage *in, VipsImage **out, int xfac, int yfac) { + return vips_zoom(in, out, xfac, yfac, NULL); +} + +int bandjoin(VipsImage **in, VipsImage **out, int n) { + return vips_bandjoin(in, out, n, NULL); +} + +int bandjoin_const(VipsImage *in, VipsImage **out, double constants[], int n) { + return vips_bandjoin_const(in, out, constants, n, NULL); +} + +int similarity(VipsImage *in, VipsImage **out, double scale, double angle, + double r, double g, double b, double a, double idx, double idy, + double odx, double ody) { + if (is_16bit(in->Type)) { + r = 65535 * r / 255; + g = 65535 * g / 255; + b = 65535 * b / 255; + a = 65535 * a / 255; + } + + double background[3] = {r, g, b}; + double backgroundRGBA[4] = {r, g, b, a}; + + VipsArrayDouble *vipsBackground; + + // Ignore the alpha channel if the image doesn't have one + if (in->Bands <= 3) { + vipsBackground = vips_array_double_new(background, 3); + } else { + vipsBackground = vips_array_double_new(backgroundRGBA, 4); + } + + int code = vips_similarity(in, out, "scale", scale, "angle", angle, + "background", vipsBackground, "idx", idx, "idy", + idy, "odx", odx, "ody", ody, NULL); + + vips_area_unref(VIPS_AREA(vipsBackground)); + return code; +} + +int smartcrop(VipsImage *in, VipsImage **out, int width, int height, + int interesting) { + return vips_smartcrop(in, out, width, height, "interesting", interesting, + NULL); +} + +int crop(VipsImage *in, VipsImage **out, int left, int top, + int width, int height) { + // resolve image pages + int page_height = vips_image_get_page_height(in); + int n_pages = in->Ysize / page_height; + if (n_pages <= 1) { + return vips_crop(in, out, left, top, width, height, NULL); + } + + int in_width = in->Xsize; + VipsObject *base = VIPS_OBJECT(vips_image_new()); + VipsImage **page = (VipsImage **) vips_object_local_array(base, n_pages); + VipsImage **copy = (VipsImage **) vips_object_local_array(base, 1); + // split image into cropped frames + for (int i = 0; i < n_pages; i++) { + if ( + vips_extract_area(in, &page[i], 0, page_height * i, in_width, page_height, NULL) || + vips_crop(page[i], &page[i], left, top, width, height, NULL) + ) { + g_object_unref(base); + return -1; + } + } + + // reassemble frames and set page height + // copy before modifying metadata + if( + vips_arrayjoin(page, ©[0], n_pages, "across", 1, NULL) || + vips_copy(copy[0], out, NULL) + ) { + g_object_unref(base); + return -1; + } + vips_image_set_int(*out, VIPS_META_PAGE_HEIGHT, height); + g_object_unref(base); + return 0; +} + +int flatten_image(VipsImage *in, VipsImage **out, double r, double g, + double b) { + if (is_16bit(in->Type)) { + r = 65535 * r / 255; + g = 65535 * g / 255; + b = 65535 * b / 255; + } + + double background[3] = {r, g, b}; + VipsArrayDouble *vipsBackground = vips_array_double_new(background, 3); + + int code = vips_flatten(in, out, "background", vipsBackground, "max_alpha", + is_16bit(in->Type) ? 65535.0 : 255.0, NULL); + + vips_area_unref(VIPS_AREA(vipsBackground)); + return code; +} + +int is_16bit(VipsInterpretation interpretation) { + return interpretation == VIPS_INTERPRETATION_RGB16 || + interpretation == VIPS_INTERPRETATION_GREY16; +} + +int add_alpha(VipsImage *in, VipsImage **out) { + return vips_addalpha(in, out, NULL); +} + +int premultiply_alpha(VipsImage *in, VipsImage **out) { + return vips_premultiply(in, out, "max_alpha", max_alpha(in), NULL); +} + +int unpremultiply_alpha(VipsImage *in, VipsImage **out) { + return vips_unpremultiply(in, out, NULL); +} + +int cast(VipsImage *in, VipsImage **out, int bandFormat) { + return vips_cast(in, out, bandFormat, NULL); +} + +double max_alpha(VipsImage *in) { + switch (in->BandFmt) { + case VIPS_FORMAT_USHORT: + return 65535; + case VIPS_FORMAT_FLOAT: + case VIPS_FORMAT_DOUBLE: + return 1.0; + default: + return 255; + } +} + +int composite_image(VipsImage **in, VipsImage **out, int n, int *mode, int *x, + int *y) { + VipsArrayInt *xs = vips_array_int_new(x, n - 1); + VipsArrayInt *ys = vips_array_int_new(y, n - 1); + + int code = vips_composite(in, out, n, mode, "x", xs, "y", ys, NULL); + + vips_area_unref(VIPS_AREA(xs)); + vips_area_unref(VIPS_AREA(ys)); + return code; +} + +int composite2_image(VipsImage *base, VipsImage *overlay, VipsImage **out, + int mode, gint x, gint y) { + return vips_composite2(base, overlay, out, mode, "x", x, "y", y, NULL); +} + +int insert_image(VipsImage *main, VipsImage *sub, VipsImage **out, int x, int y, int expand, double r, double g, double b, double a) { + if (is_16bit(main->Type)) { + r = 65535 * r / 255; + g = 65535 * g / 255; + b = 65535 * b / 255; + a = 65535 * a / 255; + } + + double background[3] = {r, g, b}; + double backgroundRGBA[4] = {r, g, b, a}; + + VipsArrayDouble *vipsBackground; + + // Ignore the alpha channel if the image doesn't have one + if (main->Bands <= 3) { + vipsBackground = vips_array_double_new(background, 3); + } else { + vipsBackground = vips_array_double_new(backgroundRGBA, 4); + } + int code = vips_insert(main, sub, out, x, y, "expand", expand, "background", vipsBackground, NULL); + + vips_area_unref(VIPS_AREA(vipsBackground)); + + return code; +} + +int join(VipsImage *in1, VipsImage *in2, VipsImage **out, int direction) { + return vips_join(in1, in2, out, direction, NULL); +} + +int arrayjoin(VipsImage **in, VipsImage **out, int n, int across) { + return vips_arrayjoin(in, out, n, "across", across, NULL); +} + +int replicate(VipsImage *in, VipsImage **out, int across, int down) { + return vips_replicate(in, out, across, down, NULL); +} + +int grid(VipsImage *in, VipsImage **out, int tileHeight, int across, int down){ + return vips_grid(in, out, tileHeight, across, down, NULL); +} + +int adjust_gamma(VipsImage *in, VipsImage **out, double g) { + return vips_gamma(in, out, "exponent", g, NULL); +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/conversion.go b/vendor/github.com/davidbyttow/govips/v2/vips/conversion.go new file mode 100644 index 000000000..0738727d9 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/conversion.go @@ -0,0 +1,520 @@ +package vips + +// #cgo CFLAGS: -std=c99 +// #include "conversion.h" +import "C" + +// BandFormat represents VIPS_FORMAT type +type BandFormat int + +// BandFormat enum +const ( + BandFormatNotSet BandFormat = C.VIPS_FORMAT_NOTSET + BandFormatUchar BandFormat = C.VIPS_FORMAT_UCHAR + BandFormatChar BandFormat = C.VIPS_FORMAT_CHAR + BandFormatUshort BandFormat = C.VIPS_FORMAT_USHORT + BandFormatShort BandFormat = C.VIPS_FORMAT_SHORT + BandFormatUint BandFormat = C.VIPS_FORMAT_UINT + BandFormatInt BandFormat = C.VIPS_FORMAT_INT + BandFormatFloat BandFormat = C.VIPS_FORMAT_FLOAT + BandFormatComplex BandFormat = C.VIPS_FORMAT_COMPLEX + BandFormatDouble BandFormat = C.VIPS_FORMAT_DOUBLE + BandFormatDpComplex BandFormat = C.VIPS_FORMAT_DPCOMPLEX +) + +// BlendMode gives the various Porter-Duff and PDF blend modes. +// See https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode +type BlendMode int + +// Constants define the various Porter-Duff and PDF blend modes. +// See https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode +const ( + BlendModeClear BlendMode = C.VIPS_BLEND_MODE_CLEAR + BlendModeSource BlendMode = C.VIPS_BLEND_MODE_SOURCE + BlendModeOver BlendMode = C.VIPS_BLEND_MODE_OVER + BlendModeIn BlendMode = C.VIPS_BLEND_MODE_IN + BlendModeOut BlendMode = C.VIPS_BLEND_MODE_OUT + BlendModeAtop BlendMode = C.VIPS_BLEND_MODE_ATOP + BlendModeDest BlendMode = C.VIPS_BLEND_MODE_DEST + BlendModeDestOver BlendMode = C.VIPS_BLEND_MODE_DEST_OVER + BlendModeDestIn BlendMode = C.VIPS_BLEND_MODE_DEST_IN + BlendModeDestOut BlendMode = C.VIPS_BLEND_MODE_DEST_OUT + BlendModeDestAtop BlendMode = C.VIPS_BLEND_MODE_DEST_ATOP + BlendModeXOR BlendMode = C.VIPS_BLEND_MODE_XOR + BlendModeAdd BlendMode = C.VIPS_BLEND_MODE_ADD + BlendModeSaturate BlendMode = C.VIPS_BLEND_MODE_SATURATE + BlendModeMultiply BlendMode = C.VIPS_BLEND_MODE_MULTIPLY + BlendModeScreen BlendMode = C.VIPS_BLEND_MODE_SCREEN + BlendModeOverlay BlendMode = C.VIPS_BLEND_MODE_OVERLAY + BlendModeDarken BlendMode = C.VIPS_BLEND_MODE_DARKEN + BlendModeLighten BlendMode = C.VIPS_BLEND_MODE_LIGHTEN + BlendModeColorDodge BlendMode = C.VIPS_BLEND_MODE_COLOUR_DODGE + BlendModeColorBurn BlendMode = C.VIPS_BLEND_MODE_COLOUR_BURN + BlendModeHardLight BlendMode = C.VIPS_BLEND_MODE_HARD_LIGHT + BlendModeSoftLight BlendMode = C.VIPS_BLEND_MODE_SOFT_LIGHT + BlendModeDifference BlendMode = C.VIPS_BLEND_MODE_DIFFERENCE + BlendModeExclusion BlendMode = C.VIPS_BLEND_MODE_EXCLUSION +) + +// Direction represents VIPS_DIRECTION type +type Direction int + +// Direction enum +const ( + DirectionHorizontal Direction = C.VIPS_DIRECTION_HORIZONTAL + DirectionVertical Direction = C.VIPS_DIRECTION_VERTICAL +) + +// Angle represents VIPS_ANGLE type +type Angle int + +// Angle enum +const ( + Angle0 Angle = C.VIPS_ANGLE_D0 + Angle90 Angle = C.VIPS_ANGLE_D90 + Angle180 Angle = C.VIPS_ANGLE_D180 + Angle270 Angle = C.VIPS_ANGLE_D270 +) + +// Angle45 represents VIPS_ANGLE45 type +type Angle45 int + +// Angle45 enum +const ( + Angle45_0 Angle45 = C.VIPS_ANGLE45_D0 + Angle45_45 Angle45 = C.VIPS_ANGLE45_D45 + Angle45_90 Angle45 = C.VIPS_ANGLE45_D90 + Angle45_135 Angle45 = C.VIPS_ANGLE45_D135 + Angle45_180 Angle45 = C.VIPS_ANGLE45_D180 + Angle45_225 Angle45 = C.VIPS_ANGLE45_D225 + Angle45_270 Angle45 = C.VIPS_ANGLE45_D270 + Angle45_315 Angle45 = C.VIPS_ANGLE45_D315 +) + +// ExtendStrategy represents VIPS_EXTEND type +type ExtendStrategy int + +// ExtendStrategy enum +const ( + ExtendBlack ExtendStrategy = C.VIPS_EXTEND_BLACK + ExtendCopy ExtendStrategy = C.VIPS_EXTEND_COPY + ExtendRepeat ExtendStrategy = C.VIPS_EXTEND_REPEAT + ExtendMirror ExtendStrategy = C.VIPS_EXTEND_MIRROR + ExtendWhite ExtendStrategy = C.VIPS_EXTEND_WHITE + ExtendBackground ExtendStrategy = C.VIPS_EXTEND_BACKGROUND +) + +// Interesting represents VIPS_INTERESTING type +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsInteresting +type Interesting int + +// Interesting constants represent areas of interest which smart cropping will crop based on. +const ( + InterestingNone Interesting = C.VIPS_INTERESTING_NONE + InterestingCentre Interesting = C.VIPS_INTERESTING_CENTRE + InterestingEntropy Interesting = C.VIPS_INTERESTING_ENTROPY + InterestingAttention Interesting = C.VIPS_INTERESTING_ATTENTION + InterestingLow Interesting = C.VIPS_INTERESTING_LOW + InterestingHigh Interesting = C.VIPS_INTERESTING_HIGH + InterestingAll Interesting = C.VIPS_INTERESTING_ALL + InterestingLast Interesting = C.VIPS_INTERESTING_LAST +) + +func vipsCopyImageChangingInterpretation(in *C.VipsImage, interpretation Interpretation) (*C.VipsImage, error) { + var out *C.VipsImage + + if err := C.copy_image_changing_interpretation(in, &out, C.VipsInterpretation(interpretation)); int(err) != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +func vipsCopyImageChangingResolution(in *C.VipsImage, xres float64, yres float64) (*C.VipsImage, error) { + var out *C.VipsImage + + if err := C.copy_image_changing_resolution(in, &out, C.double(xres), C.double(yres)); int(err) != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-copy +func vipsCopyImage(in *C.VipsImage) (*C.VipsImage, error) { + var out *C.VipsImage + + if err := C.copy_image(in, &out); int(err) != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-embed +func vipsEmbed(in *C.VipsImage, left, top, width, height int, extend ExtendStrategy) (*C.VipsImage, error) { + incOpCounter("embed") + var out *C.VipsImage + + if err := C.embed_image(in, &out, C.int(left), C.int(top), C.int(width), C.int(height), C.int(extend)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-embed +func vipsEmbedBackground(in *C.VipsImage, left, top, width, height int, backgroundColor *ColorRGBA) (*C.VipsImage, error) { + incOpCounter("embed") + var out *C.VipsImage + + if err := C.embed_image_background(in, &out, C.int(left), C.int(top), C.int(width), + C.int(height), C.double(backgroundColor.R), + C.double(backgroundColor.G), C.double(backgroundColor.B), C.double(backgroundColor.A)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +func vipsEmbedMultiPage(in *C.VipsImage, left, top, width, height int, extend ExtendStrategy) (*C.VipsImage, error) { + incOpCounter("embedMultiPage") + var out *C.VipsImage + + if err := C.embed_multi_page_image(in, &out, C.int(left), C.int(top), C.int(width), C.int(height), C.int(extend)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +func vipsEmbedMultiPageBackground(in *C.VipsImage, left, top, width, height int, backgroundColor *ColorRGBA) (*C.VipsImage, error) { + incOpCounter("embedMultiPageBackground") + var out *C.VipsImage + + if err := C.embed_multi_page_image_background(in, &out, C.int(left), C.int(top), C.int(width), + C.int(height), C.double(backgroundColor.R), + C.double(backgroundColor.G), C.double(backgroundColor.B), C.double(backgroundColor.A)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-flip +func vipsFlip(in *C.VipsImage, direction Direction) (*C.VipsImage, error) { + incOpCounter("flip") + var out *C.VipsImage + + if err := C.flip_image(in, &out, C.int(direction)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-recomb +func vipsRecomb(in *C.VipsImage, m *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("recomb") + var out *C.VipsImage + + if err := C.recomb_image(in, &out, m); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-extract-area +func vipsExtractArea(in *C.VipsImage, left, top, width, height int) (*C.VipsImage, error) { + incOpCounter("extractArea") + var out *C.VipsImage + + if err := C.extract_image_area(in, &out, C.int(left), C.int(top), C.int(width), C.int(height)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +func vipsExtractAreaMultiPage(in *C.VipsImage, left, top, width, height int) (*C.VipsImage, error) { + incOpCounter("extractAreaMultiPage") + var out *C.VipsImage + + if err := C.extract_area_multi_page(in, &out, C.int(left), C.int(top), C.int(width), C.int(height)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-extract-band +func vipsExtractBand(in *C.VipsImage, band, num int) (*C.VipsImage, error) { + incOpCounter("extractBand") + var out *C.VipsImage + + if err := C.extract_band(in, &out, C.int(band), C.int(num)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// http://libvips.github.io/libvips/API/current/libvips-resample.html#vips-similarity +func vipsSimilarity(in *C.VipsImage, scale float64, angle float64, color *ColorRGBA, + idx float64, idy float64, odx float64, ody float64) (*C.VipsImage, error) { + incOpCounter("similarity") + var out *C.VipsImage + + if err := C.similarity(in, &out, C.double(scale), C.double(angle), + C.double(color.R), C.double(color.G), C.double(color.B), C.double(color.A), + C.double(idx), C.double(idy), C.double(odx), C.double(ody)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// http://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-smartcrop +func vipsSmartCrop(in *C.VipsImage, width int, height int, interesting Interesting) (*C.VipsImage, error) { + incOpCounter("smartcrop") + var out *C.VipsImage + + if err := C.smartcrop(in, &out, C.int(width), C.int(height), C.int(interesting)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// http://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-crop +func vipsCrop(in *C.VipsImage, left int, top int, width int, height int) (*C.VipsImage, error) { + incOpCounter("crop") + var out *C.VipsImage + + if err := C.crop(in, &out, C.int(left), C.int(top), C.int(width), C.int(height)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-rot +func vipsRotate(in *C.VipsImage, angle Angle) (*C.VipsImage, error) { + incOpCounter("rot") + var out *C.VipsImage + + if err := C.rot_image(in, &out, C.VipsAngle(angle)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-autorot +func vipsAutoRotate(in *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("autorot") + var out *C.VipsImage + + if err := C.autorot_image(in, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-zoom +func vipsZoom(in *C.VipsImage, xFactor, yFactor int) (*C.VipsImage, error) { + incOpCounter("zoom") + var out *C.VipsImage + + if err := C.zoom_image(in, &out, C.int(xFactor), C.int(yFactor)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-bandjoin +func vipsBandJoin(ins []*C.VipsImage) (*C.VipsImage, error) { + incOpCounter("bandjoin") + var out *C.VipsImage + + if err := C.bandjoin(&ins[0], &out, C.int(len(ins))); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// http://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-bandjoin-const +func vipsBandJoinConst(in *C.VipsImage, constants []float64) (*C.VipsImage, error) { + incOpCounter("bandjoin_const") + var out *C.VipsImage + + if err := C.bandjoin_const(in, &out, (*C.double)(&constants[0]), C.int(len(constants))); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-flatten +func vipsFlatten(in *C.VipsImage, color *Color) (*C.VipsImage, error) { + incOpCounter("flatten") + var out *C.VipsImage + + err := C.flatten_image(in, &out, C.double(color.R), C.double(color.G), C.double(color.B)) + if int(err) != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +func vipsAddAlpha(in *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("addAlpha") + var out *C.VipsImage + + if err := C.add_alpha(in, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-premultiply +func vipsPremultiplyAlpha(in *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("premultiplyAlpha") + var out *C.VipsImage + + if err := C.premultiply_alpha(in, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-unpremultiply +func vipsUnpremultiplyAlpha(in *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("unpremultiplyAlpha") + var out *C.VipsImage + + if err := C.unpremultiply_alpha(in, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +func vipsCast(in *C.VipsImage, bandFormat BandFormat) (*C.VipsImage, error) { + incOpCounter("cast") + var out *C.VipsImage + + if err := C.cast(in, &out, C.int(bandFormat)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-composite +func vipsComposite(ins []*C.VipsImage, modes []C.int, xs, ys []C.int) (*C.VipsImage, error) { + incOpCounter("composite_multi") + var out *C.VipsImage + + if err := C.composite_image(&ins[0], &out, C.int(len(ins)), &modes[0], &xs[0], &ys[0]); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-composite2 +func vipsComposite2(base *C.VipsImage, overlay *C.VipsImage, mode BlendMode, x, y int) (*C.VipsImage, error) { + incOpCounter("composite") + var out *C.VipsImage + + if err := C.composite2_image(base, overlay, &out, C.int(mode), C.gint(x), C.gint(y)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +func vipsInsert(main *C.VipsImage, sub *C.VipsImage, x, y int, expand bool, background *ColorRGBA) (*C.VipsImage, error) { + incOpCounter("insert") + var out *C.VipsImage + + if background == nil { + background = &ColorRGBA{R: 0.0, G: 0.0, B: 0.0, A: 255.0} + } + + expandInt := 0 + if expand { + expandInt = 1 + } + + if err := C.insert_image(main, sub, &out, C.int(x), C.int(y), C.int(expandInt), C.double(background.R), C.double(background.G), C.double(background.B), C.double(background.A)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-join +func vipsJoin(input1 *C.VipsImage, input2 *C.VipsImage, dir Direction) (*C.VipsImage, error) { + incOpCounter("join") + var out *C.VipsImage + + defer C.g_object_unref(C.gpointer(input1)) + defer C.g_object_unref(C.gpointer(input2)) + if err := C.join(input1, input2, &out, C.int(dir)); err != 0 { + return nil, handleVipsError() + } + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-arrayjoin +func vipsArrayJoin(inputs []*C.VipsImage, across int) (*C.VipsImage, error) { + incOpCounter("arrayjoin") + var out *C.VipsImage + + if err := C.arrayjoin(&inputs[0], &out, C.int(len(inputs)), C.int(across)); err != 0 { + return nil, handleVipsError() + } + return out, nil +} + +// https://www.libvips.org/API/current/libvips-conversion.html#vips-replicate +func vipsReplicate(in *C.VipsImage, across int, down int) (*C.VipsImage, error) { + incOpCounter("replicate") + var out *C.VipsImage + + if err := C.replicate(in, &out, C.int(across), C.int(down)); err != 0 { + return nil, handleImageError(out) + } + return out, nil +} + +// https://www.libvips.org/API/current/libvips-conversion.html#vips-grid +func vipsGrid(in *C.VipsImage, tileHeight, across, down int) (*C.VipsImage, error) { + incOpCounter("grid") + var out *C.VipsImage + + if err := C.grid(in, &out, C.int(tileHeight), C.int(across), C.int(down)); err != 0 { + return nil, handleImageError(out) + } + return out, nil +} + +func vipsGamma(image *C.VipsImage, gamma float64) (*C.VipsImage, error) { + incOpCounter("gamma") + var out *C.VipsImage + + if err := C.adjust_gamma(image, &out, C.double(gamma)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/conversion.h b/vendor/github.com/davidbyttow/govips/v2/vips/conversion.h new file mode 100644 index 000000000..ac92c5965 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/conversion.h @@ -0,0 +1,70 @@ +// https://libvips.github.io/libvips/API/current/libvips-conversion.html + +#include +#include + +int copy_image_changing_interpretation(VipsImage *in, VipsImage **out, + VipsInterpretation interpretation); +int copy_image_changing_resolution(VipsImage *in, VipsImage **out, double xres, + double yres); +int copy_image(VipsImage *in, VipsImage **out); + +int embed_image(VipsImage *in, VipsImage **out, int left, int top, int width, + int height, int extend); +int embed_image_background(VipsImage *in, VipsImage **out, int left, int top, int width, + int height, double r, double g, double b, double a); +int embed_multi_page_image(VipsImage *in, VipsImage **out, int left, int top, int width, + int height, int extend); +int embed_multi_page_image_background(VipsImage *in, VipsImage **out, int left, int top, + int width, int height, double r, double g, double b, double a); + +int flip_image(VipsImage *in, VipsImage **out, int direction); + +int recomb_image(VipsImage *in, VipsImage **out, VipsImage *m); + +int extract_image_area(VipsImage *in, VipsImage **out, int left, int top, + int width, int height); +int extract_area_multi_page(VipsImage *in, VipsImage **out, int left, int top, + int width, int height); + +int extract_band(VipsImage *in, VipsImage **out, int band, int num); + +int rot_image(VipsImage *in, VipsImage **out, VipsAngle angle); +int autorot_image(VipsImage *in, VipsImage **out); + +int zoom_image(VipsImage *in, VipsImage **out, int xfac, int yfac); +int smartcrop(VipsImage *in, VipsImage **out, int width, int height, + int interesting); +int crop(VipsImage *in, VipsImage **out, int left, int top, + int width, int height); + +int bandjoin(VipsImage **in, VipsImage **out, int n); +int bandjoin_const(VipsImage *in, VipsImage **out, double constants[], int n); +int similarity(VipsImage *in, VipsImage **out, double scale, double angle, + double r, double g, double b, double a, double idx, double idy, + double odx, double ody); +int flatten_image(VipsImage *in, VipsImage **out, double r, double g, double b); +int add_alpha(VipsImage *in, VipsImage **out); +int premultiply_alpha(VipsImage *in, VipsImage **out); +int unpremultiply_alpha(VipsImage *in, VipsImage **out); +int cast(VipsImage *in, VipsImage **out, int bandFormat); +double max_alpha(VipsImage *in); + +int composite_image(VipsImage **in, VipsImage **out, int n, int *mode, int *x, + int *y); +int composite2_image(VipsImage *base, VipsImage *overlay, VipsImage **out, + int mode, gint x, gint y); + +int insert_image(VipsImage *main, VipsImage *sub, VipsImage **out, int x, int y, + int expand, double r, double g, double b, double a); + +int join(VipsImage *in1, VipsImage *in2, VipsImage **out, int direction); +int arrayjoin(VipsImage **in, VipsImage **out, int n, int across); + +int is_16bit(VipsInterpretation interpretation); + +int replicate(VipsImage *in, VipsImage **out, int across, int down); + +int grid(VipsImage *in, VipsImage **out, int tileHeight, int across, int down); + +int adjust_gamma(VipsImage *in, VipsImage **out, double g); diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/convolution.c b/vendor/github.com/davidbyttow/govips/v2/vips/convolution.c new file mode 100644 index 000000000..48a9472bb --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/convolution.c @@ -0,0 +1,14 @@ +#include "convolution.h" + +int gaussian_blur_image(VipsImage *in, VipsImage **out, double sigma, double min_ampl) { + return vips_gaussblur(in, out, sigma, "min_ampl", min_ampl, NULL); +} + +int sharpen_image(VipsImage *in, VipsImage **out, double sigma, double x1, + double m2) { + return vips_sharpen(in, out, "sigma", sigma, "x1", x1, "m2", m2, NULL); +} + +int sobel_image(VipsImage *in, VipsImage **out) { + return vips_sobel(in, out, NULL); +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/convolution.go b/vendor/github.com/davidbyttow/govips/v2/vips/convolution.go new file mode 100644 index 000000000..23622767f --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/convolution.go @@ -0,0 +1,40 @@ +package vips + +// #include "convolution.h" +import "C" + +// https://libvips.github.io/libvips/API/current/libvips-convolution.html#vips-gaussblur +func vipsGaussianBlur(in *C.VipsImage, sigma, minAmpl float64) (*C.VipsImage, error) { + incOpCounter("gaussblur") + var out *C.VipsImage + + if err := C.gaussian_blur_image(in, &out, C.double(sigma), C.double(minAmpl)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-convolution.html#vips-sharpen +func vipsSharpen(in *C.VipsImage, sigma float64, x1 float64, m2 float64) (*C.VipsImage, error) { + incOpCounter("sharpen") + var out *C.VipsImage + + if err := C.sharpen_image(in, &out, C.double(sigma), C.double(x1), C.double(m2)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-convolution.html#vips-sobel +func vipsSobel(in *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("sobel") + var out *C.VipsImage + + if err := C.sobel_image(in, &out); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/convolution.h b/vendor/github.com/davidbyttow/govips/v2/vips/convolution.h new file mode 100644 index 000000000..30a525ee8 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/convolution.h @@ -0,0 +1,9 @@ +// https://libvips.github.io/libvips/API/current/libvips-convolution.html + +#include +#include + +int gaussian_blur_image(VipsImage *in, VipsImage **out, double sigma, double min_ampl); +int sharpen_image(VipsImage *in, VipsImage **out, double sigma, double x1, + double m2); +int sobel_image(VipsImage *in, VipsImage **out); diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/create.c b/vendor/github.com/davidbyttow/govips/v2/vips/create.c new file mode 100644 index 000000000..1ffb7a34c --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/create.c @@ -0,0 +1,24 @@ +// clang-format off +// include order matters +#include "lang.h" +#include "create.h" +// clang-format on + +// https://libvips.github.io/libvips/API/current/libvips-create.html#vips-xyz +int xyz(VipsImage **out, int width, int height) { + return vips_xyz(out, width, height, NULL); +} + +// http://libvips.github.io/libvips/API/current/libvips-create.html#vips-black +int black(VipsImage **out, int width, int height) { + return vips_black(out, width, height, NULL); +} + +// https://libvips.github.io/libvips/API/current/libvips-create.html#vips-identity +int identity(VipsImage **out, int ushort) { + if (ushort > 0) { + return vips_identity(out, "ushort", TRUE, NULL); + } else { + return vips_identity(out, NULL); + } +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/create.go b/vendor/github.com/davidbyttow/govips/v2/vips/create.go new file mode 100644 index 000000000..3532cf535 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/create.go @@ -0,0 +1,37 @@ +package vips + +// #include "create.h" +import "C" + +// https://libvips.github.io/libvips/API/current/libvips-create.html#vips-xyz +func vipsXYZ(width int, height int) (*C.VipsImage, error) { + var out *C.VipsImage + + if err := C.xyz(&out, C.int(width), C.int(height)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// http://libvips.github.io/libvips/API/current/libvips-create.html#vips-black +func vipsBlack(width int, height int) (*C.VipsImage, error) { + var out *C.VipsImage + + if err := C.black(&out, C.int(width), C.int(height)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-create.html#vips-identity +func vipsIdentity(ushort bool) (*C.VipsImage, error) { + var out *C.VipsImage + ushortInt := C.int(boolToInt(ushort)) + if err := C.identity(&out, ushortInt); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/create.h b/vendor/github.com/davidbyttow/govips/v2/vips/create.h new file mode 100644 index 000000000..245d11fba --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/create.h @@ -0,0 +1,12 @@ +// https://libvips.github.io/libvips/API/current/libvips-create.html + +// clang-format off +// include order matters +#include +#include +#include +// clang-format on + +int xyz(VipsImage **out, int width, int height); +int black(VipsImage **out, int width, int height); +int identity(VipsImage **out, int ushort); diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/draw.c b/vendor/github.com/davidbyttow/govips/v2/vips/draw.c new file mode 100644 index 000000000..024e18c1c --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/draw.c @@ -0,0 +1,24 @@ +#include "draw.h" + +#include "conversion.h" + +int draw_rect(VipsImage *in, double r, double g, double b, double a, int left, + int top, int width, int height, int fill) { + if (is_16bit(in->Type)) { + r = 65535 * r / 255; + g = 65535 * g / 255; + b = 65535 * b / 255; + a = 65535 * a / 255; + } + + double background[3] = {r, g, b}; + double backgroundRGBA[4] = {r, g, b, a}; + + if (in->Bands <= 3) { + return vips_draw_rect(in, background, 3, left, top, width, height, "fill", + fill, NULL); + } else { + return vips_draw_rect(in, backgroundRGBA, 4, left, top, width, height, + "fill", fill, NULL); + } +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/draw.go b/vendor/github.com/davidbyttow/govips/v2/vips/draw.go new file mode 100644 index 000000000..d2ab2ad9e --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/draw.go @@ -0,0 +1,21 @@ +package vips + +// #include "draw.h" +import "C" + +// https://libvips.github.io/libvips/API/current/libvips-draw.html#vips-draw-rect +func vipsDrawRect(in *C.VipsImage, color ColorRGBA, left int, top int, width int, height int, fill bool) error { + incOpCounter("draw_rect") + + fillBit := 0 + if fill { + fillBit = 1 + } + + if err := C.draw_rect(in, C.double(color.R), C.double(color.G), C.double(color.B), C.double(color.A), + C.int(left), C.int(top), C.int(width), C.int(height), C.int(fillBit)); err != 0 { + return handleImageError(in) + } + + return nil +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/draw.h b/vendor/github.com/davidbyttow/govips/v2/vips/draw.h new file mode 100644 index 000000000..a1c5b6f96 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/draw.h @@ -0,0 +1,7 @@ +// https://libvips.github.io/libvips/API/current/libvips-draw.html + +#include +#include + +int draw_rect(VipsImage *in, double r, double g, double b, double a, int left, + int top, int width, int height, int fill); diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/error.go b/vendor/github.com/davidbyttow/govips/v2/vips/error.go new file mode 100644 index 000000000..08eb41e78 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/error.go @@ -0,0 +1,39 @@ +package vips + +// #include +import "C" + +import ( + "errors" + "fmt" + dbg "runtime/debug" + "unsafe" +) + +var ( + // ErrUnsupportedImageFormat when image type is unsupported + ErrUnsupportedImageFormat = errors.New("unsupported image format") +) + +func handleImageError(out *C.VipsImage) error { + if out != nil { + clearImage(out) + } + + return handleVipsError() +} + +func handleSaveBufferError(out unsafe.Pointer) error { + if out != nil { + gFreePointer(out) + } + + return handleVipsError() +} + +func handleVipsError() error { + s := C.GoString(C.vips_error_buffer()) + C.vips_error_clear() + + return fmt.Errorf("%v\nStack:\n%s", s, dbg.Stack()) +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/foreign.c b/vendor/github.com/davidbyttow/govips/v2/vips/foreign.c new file mode 100644 index 000000000..23f9d27d5 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/foreign.c @@ -0,0 +1,578 @@ +#include "foreign.h" + +#include "lang.h" + +void set_bool_param(Param *p, gboolean b) { + p->type = PARAM_TYPE_BOOL; + p->value.b = b; + p->is_set = TRUE; +} + +void set_int_param(Param *p, gint i) { + p->type = PARAM_TYPE_INT; + p->value.i = i; + p->is_set = TRUE; +} + +void set_double_param(Param *p, gdouble d) { + p->type = PARAM_TYPE_DOUBLE; + p->value.d = d; + p->is_set = TRUE; +} + +int load_image_buffer(LoadParams *params, void *buf, size_t len, + VipsImage **out) { + int code = 1; + ImageType imageType = params->inputFormat; + + if (imageType == JPEG) { + // shrink: int, fail: bool, autorotate: bool + code = vips_jpegload_buffer(buf, len, out, "fail", params->fail, + "autorotate", params->autorotate, "shrink", + params->jpegShrink, NULL); + } else if (imageType == PNG) { + code = vips_pngload_buffer(buf, len, out, NULL); + } else if (imageType == WEBP) { + // page: int, n: int, scale: double + code = vips_webpload_buffer(buf, len, out, "page", params->page, "n", + params->n, NULL); + } else if (imageType == TIFF) { + // page: int, n: int, autorotate: bool, subifd: int + code = + vips_tiffload_buffer(buf, len, out, "page", params->page, "n", + params->n, "autorotate", params->autorotate, NULL); + } else if (imageType == GIF) { + // page: int, n: int, scale: double + code = vips_gifload_buffer(buf, len, out, "page", params->page, "n", + params->n, NULL); + } else if (imageType == PDF) { + // page: int, n: int, dpi: double, scale: double, background: color + code = vips_pdfload_buffer(buf, len, out, "page", params->page, "n", + params->n, "dpi", params->dpi, NULL); + } else if (imageType == SVG) { + // dpi: double, scale: double, unlimited: bool + code = vips_svgload_buffer(buf, len, out, "dpi", params->dpi, "unlimited", + params->svgUnlimited, NULL); + } else if (imageType == HEIF) { + // added autorotate on load as currently it addresses orientation issues + // https://github.com/libvips/libvips/pull/1680 + // page: int, n: int, thumbnail: bool + code = vips_heifload_buffer(buf, len, out, "page", params->page, "n", + params->n, "thumbnail", params->heifThumbnail, + "autorotate", TRUE, NULL); + } else if (imageType == MAGICK) { + // page: int, n: int, density: string + code = vips_magickload_buffer(buf, len, out, "page", params->page, "n", + params->n, NULL); + } else if (imageType == AVIF) { + code = vips_heifload_buffer(buf, len, out, "page", params->page, "n", + params->n, "thumbnail", params->heifThumbnail, + "autorotate", params->autorotate, NULL); + + } + #if (VIPS_MAJOR_VERSION >= 8) && (VIPS_MINOR_VERSION >= 11) + else if (imageType == JP2K) { + code = vips_jp2kload_buffer(buf, len, out, "page", params->page, NULL); + } + #endif + + return code; +} + +#define MAYBE_SET_BOOL(OP, PARAM, NAME) \ + if (PARAM.is_set) { \ + vips_object_set(VIPS_OBJECT(OP), NAME, PARAM.value.b, NULL); \ + } + +#define MAYBE_SET_INT(OP, PARAM, NAME) \ + if (PARAM.is_set) { \ + vips_object_set(VIPS_OBJECT(OP), NAME, PARAM.value.i, NULL); \ + } + +#define MAYBE_SET_DOUBLE(OP, PARAM, NAME) \ + if (PARAM.is_set) { \ + vips_object_set(VIPS_OBJECT(OP), NAME, PARAM.value.d, NULL); \ + } + +typedef int (*SetLoadOptionsFn)(VipsOperation *operation, LoadParams *params); + +int set_jpegload_options(VipsOperation *operation, LoadParams *params) { + MAYBE_SET_BOOL(operation, params->autorotate, "autorotate"); + MAYBE_SET_BOOL(operation, params->fail, "fail"); + MAYBE_SET_INT(operation, params->jpegShrink, "shrink"); + return 0; +} + +int set_pngload_options(VipsOperation *operation, LoadParams *params) { + MAYBE_SET_BOOL(operation, params->fail, "fail"); + return 0; +} + +int set_webpload_options(VipsOperation *operation, LoadParams *params) { + MAYBE_SET_INT(operation, params->page, "page"); + MAYBE_SET_INT(operation, params->n, "n"); + return 0; +} + +int set_tiffload_options(VipsOperation *operation, LoadParams *params) { + MAYBE_SET_BOOL(operation, params->autorotate, "autorotate"); + MAYBE_SET_INT(operation, params->page, "page"); + MAYBE_SET_INT(operation, params->n, "n"); + return 0; +} + +int set_gifload_options(VipsOperation *operation, LoadParams *params) { + MAYBE_SET_INT(operation, params->page, "page"); + MAYBE_SET_INT(operation, params->n, "n"); + return 0; +} + +int set_pdfload_options(VipsOperation *operation, LoadParams *params) { + MAYBE_SET_INT(operation, params->page, "page"); + MAYBE_SET_INT(operation, params->n, "n"); + MAYBE_SET_DOUBLE(operation, params->dpi, "dpi"); + return 0; +} + +int set_svgload_options(VipsOperation *operation, LoadParams *params) { + MAYBE_SET_BOOL(operation, params->svgUnlimited, "unlimited"); + MAYBE_SET_DOUBLE(operation, params->dpi, "dpi"); + return 0; +} + +int set_heifload_options(VipsOperation *operation, LoadParams *params) { + MAYBE_SET_BOOL(operation, params->autorotate, "autorotate"); + MAYBE_SET_BOOL(operation, params->heifThumbnail, "thumbnail"); + MAYBE_SET_INT(operation, params->page, "page"); + MAYBE_SET_INT(operation, params->n, "n"); + return 0; +} + +int set_jp2kload_options(VipsOperation *operation, LoadParams *params) { + MAYBE_SET_INT(operation, params->page, "page"); + return 0; +} + +int set_jxlload_options(VipsOperation *operation, LoadParams *params) { + // nothing need to do + return 0; +} + +int set_magickload_options(VipsOperation *operation, LoadParams *params) { + MAYBE_SET_INT(operation, params->page, "page"); + MAYBE_SET_INT(operation, params->n, "n"); + return 0; +} + +int load_buffer(const char *operationName, void *buf, size_t len, + LoadParams *params, SetLoadOptionsFn setLoadOptions) { + VipsBlob *blob = vips_blob_new(NULL, buf, len); + + VipsOperation *operation = vips_operation_new(operationName); + if (!operation) { + return 1; + } + + if (vips_object_set(VIPS_OBJECT(operation), "buffer", blob, NULL)) { + vips_area_unref(VIPS_AREA(blob)); + return 1; + } + + vips_area_unref(VIPS_AREA(blob)); + + if (setLoadOptions(operation, params)) { + vips_object_unref_outputs(VIPS_OBJECT(operation)); + g_object_unref(operation); + return 1; + } + + if (vips_cache_operation_buildp(&operation)) { + vips_object_unref_outputs(VIPS_OBJECT(operation)); + g_object_unref(operation); + return 1; + } + + g_object_get(VIPS_OBJECT(operation), "out", ¶ms->outputImage, NULL); + + vips_object_unref_outputs(VIPS_OBJECT(operation)); + g_object_unref(operation); + + return 0; +} + +typedef int (*SetSaveOptionsFn)(VipsOperation *operation, SaveParams *params); + +int save_buffer(const char *operationName, SaveParams *params, + SetSaveOptionsFn setSaveOptions) { + VipsBlob *blob; + VipsOperation *operation = vips_operation_new(operationName); + if (!operation) { + return 1; + } + + if (vips_object_set(VIPS_OBJECT(operation), "in", params->inputImage, NULL)) { + return 1; + } + + if (setSaveOptions(operation, params)) { + g_object_unref(operation); + return 1; + } + + if (vips_cache_operation_buildp(&operation)) { + vips_object_unref_outputs(VIPS_OBJECT(operation)); + g_object_unref(operation); + return 1; + } + + g_object_get(VIPS_OBJECT(operation), "buffer", &blob, NULL); + g_object_unref(operation); + + VipsArea *area = VIPS_AREA(blob); + + params->outputBuffer = (char *)(area->data); + params->outputLen = area->length; + area->free_fn = NULL; + vips_area_unref(area); + + return 0; +} + +// https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-jpegsave-buffer +int set_jpegsave_options(VipsOperation *operation, SaveParams *params) { + int ret = vips_object_set( + VIPS_OBJECT(operation), "strip", params->stripMetadata, "optimize_coding", + params->jpegOptimizeCoding, "interlace", params->interlace, + "subsample_mode", params->jpegSubsample, "trellis_quant", + params->jpegTrellisQuant, "overshoot_deringing", + params->jpegOvershootDeringing, "optimize_scans", + params->jpegOptimizeScans, "quant_table", params->jpegQuantTable, NULL); + + if (!ret && params->quality) { + ret = vips_object_set(VIPS_OBJECT(operation), "Q", params->quality, NULL); + } + + return ret; +} + +// https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-pngsave-buffer +int set_pngsave_options(VipsOperation *operation, SaveParams *params) { + int ret = + vips_object_set(VIPS_OBJECT(operation), "strip", params->stripMetadata, + "compression", params->pngCompression, "interlace", + params->interlace, "filter", params->pngFilter, "palette", + params->pngPalette, NULL); + + if (!ret && params->quality) { + ret = vips_object_set(VIPS_OBJECT(operation), "Q", params->quality, NULL); + } + + if (!ret && params->pngDither) { + ret = vips_object_set(VIPS_OBJECT(operation), "dither", params->pngDither, NULL); + } + + if (!ret && params->pngBitdepth) { + ret = vips_object_set(VIPS_OBJECT(operation), "bitdepth", params->pngBitdepth, NULL); + } + + // TODO: Handle `profile` param. + + return ret; +} + +// https://github.com/libvips/libvips/blob/master/libvips/foreign/webpsave.c#L524 +// https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-webpsave-buffer +int set_webpsave_options(VipsOperation *operation, SaveParams *params) { + int ret = + vips_object_set(VIPS_OBJECT(operation), + "strip", params->stripMetadata, + "lossless", params->webpLossless, + "near_lossless", params->webpNearLossless, + "reduction_effort", params->webpReductionEffort, + "profile", params->webpIccProfile ? params->webpIccProfile : "none", + "min_size", params->webpMinSize, + "kmin", params->webpKMin, + "kmax", params->webpKMax, + NULL); + + if (!ret && params->quality) { + ret = vips_object_set(VIPS_OBJECT(operation), "Q", params->quality, NULL); + } + + return ret; +} + +// https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-tiffsave-buffer +int set_tiffsave_options(VipsOperation *operation, SaveParams *params) { + int ret = vips_object_set( + VIPS_OBJECT(operation), "strip", params->stripMetadata, "compression", + params->tiffCompression, "predictor", params->tiffPredictor, "pyramid", + params->tiffPyramid, "tile_height", params->tiffTileHeight, "tile_width", + params->tiffTileWidth, "tile", params->tiffTile, NULL); + + if (!ret && params->quality) { + ret = vips_object_set(VIPS_OBJECT(operation), "Q", params->quality, NULL); + } + + return ret; +} + +// https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-magicksave-buffer +int set_magicksave_options(VipsOperation *operation, SaveParams *params) { + int ret = vips_object_set(VIPS_OBJECT(operation), "format", "GIF", "bitdepth", params->gifBitdepth, NULL); + + if (!ret && params->quality) { + ret = vips_object_set(VIPS_OBJECT(operation), "quality", params->quality, + NULL); + } + return ret; +} + +// https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-gifsave-buffer +int set_gifsave_options(VipsOperation *operation, SaveParams *params) { + int ret = 0; + // See for argument values: https://www.libvips.org/API/current/VipsForeignSave.html#vips-gifsave + if (params->gifDither > 0.0 && params->gifDither <= 10) { + ret = vips_object_set(VIPS_OBJECT(operation), "dither", params->gifDither, NULL); + } + if (params->gifEffort >= 1 && params->gifEffort <= 10) { + ret = vips_object_set(VIPS_OBJECT(operation), "effort", params->gifEffort, NULL); + } + if (params->gifBitdepth >= 1 && params->gifBitdepth <= 8) { + ret = vips_object_set(VIPS_OBJECT(operation), "bitdepth", params->gifBitdepth, NULL); + } + return ret; +} + +// https://github.com/libvips/libvips/blob/master/libvips/foreign/heifsave.c#L653 +int set_heifsave_options(VipsOperation *operation, SaveParams *params) { + int ret = vips_object_set(VIPS_OBJECT(operation), "lossless", + params->heifLossless, NULL); + +#if (VIPS_MAJOR_VERSION >= 8) && (VIPS_MINOR_VERSION >= 13) + if (!ret && params->heifBitdepth && params->heifEffort) { + ret = vips_object_set(VIPS_OBJECT(operation), "bitdepth", + params->heifBitdepth, "effort", params->heifEffort, + NULL); + } +#else + if (!ret && params->heifEffort) { + ret = vips_object_set(VIPS_OBJECT(operation), "speed", params->heifEffort, + NULL); + } +#endif + + if (!ret && params->quality) { + ret = vips_object_set(VIPS_OBJECT(operation), "Q", params->quality, NULL); + } + + return ret; +} + +// https://github.com/libvips/libvips/blob/master/libvips/foreign/heifsave.c#L653 +int set_avifsave_options(VipsOperation *operation, SaveParams *params) { + int ret = vips_object_set(VIPS_OBJECT(operation), "strip", params->stripMetadata, "compression", + VIPS_FOREIGN_HEIF_COMPRESSION_AV1, "lossless", + params->heifLossless, NULL); + +#if (VIPS_MAJOR_VERSION >= 8) && (VIPS_MINOR_VERSION >= 13) + if (!ret && params->heifBitdepth && params->heifEffort) { + ret = vips_object_set(VIPS_OBJECT(operation), "bitdepth", + params->heifBitdepth, "effort", params->heifEffort, + NULL); + } +#else + if (!ret && params->heifEffort) { + ret = vips_object_set(VIPS_OBJECT(operation), "speed", params->heifEffort, + NULL); + } +#endif + + if (!ret && params->quality) { + ret = vips_object_set(VIPS_OBJECT(operation), "Q", params->quality, NULL); + } + + return ret; +} + +int set_jp2ksave_options(VipsOperation *operation, SaveParams *params) { + int ret = vips_object_set( + VIPS_OBJECT(operation), "subsample_mode", params->jpegSubsample, + "tile_height", params->jp2kTileHeight, "tile_width", params->jp2kTileWidth, + "lossless", params->jp2kLossless, NULL); + + if (!ret && params->quality) { + ret = vips_object_set(VIPS_OBJECT(operation), "Q", params->quality, NULL); + } + + return ret; +} + +int set_jxlsave_options(VipsOperation *operation, SaveParams *params) { + int ret = vips_object_set( + VIPS_OBJECT(operation), "tier", params->jxlTier, + "distance", params->jxlDistance, "effort", params->jxlEffort, + "lossless", params->jxlLossless, NULL); + + if (!ret && params->quality) { + ret = vips_object_set(VIPS_OBJECT(operation), "Q", params->quality, NULL); + } + + return ret; +} + +int load_from_buffer(LoadParams *params, void *buf, size_t len) { + switch (params->inputFormat) { + case JPEG: + return load_buffer("jpegload_buffer", buf, len, params, + set_jpegload_options); + case PNG: + return load_buffer("pngload_buffer", buf, len, params, + set_pngload_options); + case WEBP: + return load_buffer("webpload_buffer", buf, len, params, + set_webpload_options); + case HEIF: + return load_buffer("heifload_buffer", buf, len, params, + set_heifload_options); + case TIFF: + return load_buffer("tiffload_buffer", buf, len, params, + set_tiffload_options); + case SVG: + return load_buffer("svgload_buffer", buf, len, params, + set_svgload_options); + case GIF: + return load_buffer("gifload_buffer", buf, len, params, + set_gifload_options); + case PDF: + return load_buffer("pdfload_buffer", buf, len, params, + set_pdfload_options); + case MAGICK: + return load_buffer("magickload_buffer", buf, len, params, + set_magickload_options); + case AVIF: + return load_buffer("heifload_buffer", buf, len, params, + set_heifload_options); + case JP2K: + return load_buffer("jp2kload_buffer", buf, len, params, + set_jp2kload_options); + case JXL: + return load_buffer("jxlload_buffer", buf, len, params, + set_jxlload_options); + default: + g_warning("Unsupported input type given: %d", params->inputFormat); + } + return 1; +} + +int save_to_buffer(SaveParams *params) { + switch (params->outputFormat) { + case JPEG: + return save_buffer("jpegsave_buffer", params, set_jpegsave_options); + case PNG: + return save_buffer("pngsave_buffer", params, set_pngsave_options); + case WEBP: + return save_buffer("webpsave_buffer", params, set_webpsave_options); + case HEIF: + return save_buffer("heifsave_buffer", params, set_heifsave_options); + case TIFF: + return save_buffer("tiffsave_buffer", params, set_tiffsave_options); + case GIF: +#if (VIPS_MAJOR_VERSION >= 8) && (VIPS_MINOR_VERSION >= 12) + return save_buffer("gifsave_buffer", params, set_gifsave_options); +#else + return save_buffer("magicksave_buffer", params, set_magicksave_options); +#endif + case AVIF: + return save_buffer("heifsave_buffer", params, set_avifsave_options); + case JP2K: + return save_buffer("jp2ksave_buffer", params, set_jp2ksave_options); + case JXL: + return save_buffer("jxlsave_buffer", params, set_jxlsave_options); + default: + g_warning("Unsupported output type given: %d", params->outputFormat); + } + return 1; +} + +LoadParams create_load_params(ImageType inputFormat) { + Param defaultParam = {}; + LoadParams p = { + .inputFormat = inputFormat, + .inputBlob = NULL, + .outputImage = NULL, + .autorotate = defaultParam, + .fail = defaultParam, + .page = defaultParam, + .n = defaultParam, + .dpi = defaultParam, + .jpegShrink = defaultParam, + .heifThumbnail = defaultParam, + .svgUnlimited = defaultParam, + }; + return p; +} + +// TODO: Change to same pattern as ImportParams + +static SaveParams defaultSaveParams = { + .inputImage = NULL, + .outputBuffer = NULL, + .outputFormat = JPEG, + .outputLen = 0, + + .interlace = FALSE, + .quality = 0, + .stripMetadata = FALSE, + + .jpegOptimizeCoding = FALSE, + .jpegSubsample = VIPS_FOREIGN_JPEG_SUBSAMPLE_ON, + .jpegTrellisQuant = FALSE, + .jpegOvershootDeringing = FALSE, + .jpegOptimizeScans = FALSE, + .jpegQuantTable = 0, + + .pngCompression = 6, + .pngPalette = FALSE, + .pngBitdepth = 0, + .pngDither = 0, + .pngFilter = VIPS_FOREIGN_PNG_FILTER_NONE, + + .gifDither = 0.0, + .gifEffort = 0, + .gifBitdepth = 0, + + .webpLossless = FALSE, + .webpNearLossless = FALSE, + .webpReductionEffort = 4, + .webpIccProfile = NULL, + .webpKMax = 0, + .webpKMin = 0, + .webpMinSize = FALSE, + + .heifBitdepth = 8, + .heifLossless = FALSE, + .heifEffort = 5, + + .tiffCompression = VIPS_FOREIGN_TIFF_COMPRESSION_LZW, + .tiffPredictor = VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL, + .tiffPyramid = FALSE, + .tiffTile = FALSE, + .tiffTileHeight = 256, + .tiffTileWidth = 256, + + .jp2kLossless = FALSE, + .jp2kTileHeight = 512, + .jp2kTileWidth = 512, + + .jxlTier = 0, + .jxlDistance = 1.0, + .jxlEffort = 7, + .jxlLossless = FALSE, + }; + +SaveParams create_save_params(ImageType outputFormat) { + SaveParams params = defaultSaveParams; + params.outputFormat = outputFormat; + return params; +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/foreign.go b/vendor/github.com/davidbyttow/govips/v2/vips/foreign.go new file mode 100644 index 000000000..695617f53 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/foreign.go @@ -0,0 +1,502 @@ +package vips + +// #include "foreign.h" +import "C" +import ( + "bytes" + "encoding/xml" + "fmt" + "image/png" + "math" + "runtime" + "unsafe" + + "golang.org/x/image/bmp" + "golang.org/x/net/html/charset" +) + +// SubsampleMode correlates to a libvips subsample mode +type SubsampleMode int + +// SubsampleMode enum correlating to libvips subsample modes +const ( + VipsForeignSubsampleAuto SubsampleMode = C.VIPS_FOREIGN_JPEG_SUBSAMPLE_AUTO + VipsForeignSubsampleOn SubsampleMode = C.VIPS_FOREIGN_JPEG_SUBSAMPLE_ON + VipsForeignSubsampleOff SubsampleMode = C.VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF + VipsForeignSubsampleLast SubsampleMode = C.VIPS_FOREIGN_JPEG_SUBSAMPLE_LAST +) + +// ImageType represents an image type +type ImageType int + +// ImageType enum +const ( + ImageTypeUnknown ImageType = C.UNKNOWN + ImageTypeGIF ImageType = C.GIF + ImageTypeJPEG ImageType = C.JPEG + ImageTypeMagick ImageType = C.MAGICK + ImageTypePDF ImageType = C.PDF + ImageTypePNG ImageType = C.PNG + ImageTypeSVG ImageType = C.SVG + ImageTypeTIFF ImageType = C.TIFF + ImageTypeWEBP ImageType = C.WEBP + ImageTypeHEIF ImageType = C.HEIF + ImageTypeBMP ImageType = C.BMP + ImageTypeAVIF ImageType = C.AVIF + ImageTypeJP2K ImageType = C.JP2K + ImageTypeJXL ImageType = C.JXL +) + +var imageTypeExtensionMap = map[ImageType]string{ + ImageTypeGIF: ".gif", + ImageTypeJPEG: ".jpeg", + ImageTypeMagick: ".magick", + ImageTypePDF: ".pdf", + ImageTypePNG: ".png", + ImageTypeSVG: ".svg", + ImageTypeTIFF: ".tiff", + ImageTypeWEBP: ".webp", + ImageTypeHEIF: ".heic", + ImageTypeBMP: ".bmp", + ImageTypeAVIF: ".avif", + ImageTypeJP2K: ".jp2", + ImageTypeJXL: ".jxl", +} + +// ImageTypes defines the various image types supported by govips +var ImageTypes = map[ImageType]string{ + ImageTypeGIF: "gif", + ImageTypeJPEG: "jpeg", + ImageTypeMagick: "magick", + ImageTypePDF: "pdf", + ImageTypePNG: "png", + ImageTypeSVG: "svg", + ImageTypeTIFF: "tiff", + ImageTypeWEBP: "webp", + ImageTypeHEIF: "heif", + ImageTypeBMP: "bmp", + ImageTypeAVIF: "heif", + ImageTypeJP2K: "jp2k", + ImageTypeJXL: "jxl", +} + +// TiffCompression represents method for compressing a tiff at export +type TiffCompression int + +// TiffCompression enum +const ( + TiffCompressionNone TiffCompression = C.VIPS_FOREIGN_TIFF_COMPRESSION_NONE + TiffCompressionJpeg TiffCompression = C.VIPS_FOREIGN_TIFF_COMPRESSION_JPEG + TiffCompressionDeflate TiffCompression = C.VIPS_FOREIGN_TIFF_COMPRESSION_DEFLATE + TiffCompressionPackbits TiffCompression = C.VIPS_FOREIGN_TIFF_COMPRESSION_PACKBITS + TiffCompressionFax4 TiffCompression = C.VIPS_FOREIGN_TIFF_COMPRESSION_CCITTFAX4 + TiffCompressionLzw TiffCompression = C.VIPS_FOREIGN_TIFF_COMPRESSION_LZW + TiffCompressionWebp TiffCompression = C.VIPS_FOREIGN_TIFF_COMPRESSION_WEBP + TiffCompressionZstd TiffCompression = C.VIPS_FOREIGN_TIFF_COMPRESSION_ZSTD +) + +// TiffPredictor represents method for compressing a tiff at export +type TiffPredictor int + +// TiffPredictor enum +const ( + TiffPredictorNone TiffPredictor = C.VIPS_FOREIGN_TIFF_PREDICTOR_NONE + TiffPredictorHorizontal TiffPredictor = C.VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL + TiffPredictorFloat TiffPredictor = C.VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT +) + +// PngFilter represents filter algorithms that can be applied before compression. +// See https://www.w3.org/TR/PNG-Filters.html +type PngFilter int + +// PngFilter enum +const ( + PngFilterNone PngFilter = C.VIPS_FOREIGN_PNG_FILTER_NONE + PngFilterSub PngFilter = C.VIPS_FOREIGN_PNG_FILTER_SUB + PngFilterUo PngFilter = C.VIPS_FOREIGN_PNG_FILTER_UP + PngFilterAvg PngFilter = C.VIPS_FOREIGN_PNG_FILTER_AVG + PngFilterPaeth PngFilter = C.VIPS_FOREIGN_PNG_FILTER_PAETH + PngFilterAll PngFilter = C.VIPS_FOREIGN_PNG_FILTER_ALL +) + +// FileExt returns the canonical extension for the ImageType +func (i ImageType) FileExt() string { + if ext, ok := imageTypeExtensionMap[i]; ok { + return ext + } + return "" +} + +// IsTypeSupported checks whether given image type is supported by govips +func IsTypeSupported(imageType ImageType) bool { + startupIfNeeded() + + return supportedImageTypes[imageType] +} + +// DetermineImageType attempts to determine the image type of the given buffer +func DetermineImageType(buf []byte) ImageType { + if len(buf) < 12 { + return ImageTypeUnknown + } else if isJPEG(buf) { + return ImageTypeJPEG + } else if isPNG(buf) { + return ImageTypePNG + } else if isGIF(buf) { + return ImageTypeGIF + } else if isTIFF(buf) { + return ImageTypeTIFF + } else if isWEBP(buf) { + return ImageTypeWEBP + } else if isAVIF(buf) { + return ImageTypeAVIF + } else if isHEIF(buf) { + return ImageTypeHEIF + } else if isSVG(buf) { + return ImageTypeSVG + } else if isPDF(buf) { + return ImageTypePDF + } else if isBMP(buf) { + return ImageTypeBMP + } else if isJP2K(buf) { + return ImageTypeJP2K + } else if isJXL(buf) { + return ImageTypeJXL + } else { + return ImageTypeUnknown + } +} + +var jpeg = []byte("\xFF\xD8\xFF") + +func isJPEG(buf []byte) bool { + return bytes.HasPrefix(buf, jpeg) +} + +var gifHeader = []byte("\x47\x49\x46") + +func isGIF(buf []byte) bool { + return bytes.HasPrefix(buf, gifHeader) +} + +var pngHeader = []byte("\x89\x50\x4E\x47") + +func isPNG(buf []byte) bool { + return bytes.HasPrefix(buf, pngHeader) +} + +var tifII = []byte("\x49\x49\x2A\x00") +var tifMM = []byte("\x4D\x4D\x00\x2A") + +func isTIFF(buf []byte) bool { + return bytes.HasPrefix(buf, tifII) || bytes.HasPrefix(buf, tifMM) +} + +var webpHeader = []byte("\x57\x45\x42\x50") + +func isWEBP(buf []byte) bool { + return bytes.Equal(buf[8:12], webpHeader) +} + +// https://github.com/strukturag/libheif/blob/master/libheif/heif.cc +var ftyp = []byte("ftyp") +var heic = []byte("heic") +var mif1 = []byte("mif1") +var msf1 = []byte("msf1") +var avif = []byte("avif") + +func isHEIF(buf []byte) bool { + return bytes.Equal(buf[4:8], ftyp) && (bytes.Equal(buf[8:12], heic) || + bytes.Equal(buf[8:12], mif1) || + bytes.Equal(buf[8:12], msf1)) || + isAVIF(buf) +} + +func isAVIF(buf []byte) bool { + return bytes.Equal(buf[4:8], ftyp) && bytes.Equal(buf[8:12], avif) +} + +var svg = []byte(" + +#include +#include +// clang-format n + +#ifndef BOOL +#define BOOL int +#endif + +typedef enum types { + UNKNOWN = 0, + JPEG, + WEBP, + PNG, + TIFF, + GIF, + PDF, + SVG, + MAGICK, + HEIF, + BMP, + AVIF, + JP2K, + JXL +} ImageType; + +typedef enum ParamType { + PARAM_TYPE_NULL, + PARAM_TYPE_BOOL, + PARAM_TYPE_INT, + PARAM_TYPE_DOUBLE, +} ParamType; + +typedef struct Param { + ParamType type; + + union Value { + gboolean b; + gint i; + gdouble d; + } value; + + gboolean is_set; + +} Param; + +void set_bool_param(Param *p, gboolean b); +void set_int_param(Param *p, gint i); +void set_double_param(Param *p, gdouble d); + +typedef struct LoadParams { + ImageType inputFormat; + VipsBlob *inputBlob; + VipsImage *outputImage; + + Param autorotate; + Param fail; + Param page; + Param n; + Param dpi; + Param jpegShrink; + Param heifThumbnail; + Param svgUnlimited; + +} LoadParams; + +LoadParams create_load_params(ImageType inputFormat); +int load_from_buffer(LoadParams *params, void *buf, size_t len); + +typedef struct SaveParams { + VipsImage *inputImage; + void *outputBuffer; + ImageType outputFormat; + size_t outputLen; + + BOOL stripMetadata; + int quality; + BOOL interlace; + + // JPEG + BOOL jpegOptimizeCoding; + VipsForeignJpegSubsample jpegSubsample; + BOOL jpegTrellisQuant; + BOOL jpegOvershootDeringing; + BOOL jpegOptimizeScans; + int jpegQuantTable; + + // PNG + int pngCompression; + VipsForeignPngFilter pngFilter; + BOOL pngPalette; + double pngDither; + int pngBitdepth; + + // GIF (with CGIF) + double gifDither; + int gifEffort; + int gifBitdepth; + + // WEBP + BOOL webpLossless; + BOOL webpNearLossless; + int webpReductionEffort; + char *webpIccProfile; + BOOL webpMinSize; + int webpKMin; + int webpKMax; + + // HEIF - https://github.com/libvips/libvips/blob/master/libvips/foreign/heifsave.c#L71 + int heifBitdepth; // Bitdepth to save at for >8 bit images + BOOL heifLossless; // Lossless compression + int heifEffort; // CPU effort (0 - 9) + + // TIFF + VipsForeignTiffCompression tiffCompression; + VipsForeignTiffPredictor tiffPredictor; + BOOL tiffPyramid; + BOOL tiffTile; + int tiffTileHeight; + int tiffTileWidth; + + // JPEG2000 + BOOL jp2kLossless; + int jp2kTileWidth; + int jp2kTileHeight; + + // JXL + int jxlTier; + double jxlDistance; + int jxlEffort; + BOOL jxlLossless; +} SaveParams; + +SaveParams create_save_params(ImageType outputFormat); +int save_to_buffer(SaveParams *params); + diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/govips.c b/vendor/github.com/davidbyttow/govips/v2/vips/govips.c new file mode 100644 index 000000000..0b220b9ec --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/govips.c @@ -0,0 +1,27 @@ +#include "govips.h" + +static void govips_logging_handler(const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, gpointer user_data) { + govipsLoggingHandler((char *)log_domain, (int)log_level, (char *)message); +} + +static void null_logging_handler(const gchar *log_domain, + GLogLevelFlags log_level, const gchar *message, + gpointer user_data) {} + +void vips_set_logging_handler(void) { + g_log_set_default_handler(govips_logging_handler, NULL); +} + +void vips_unset_logging_handler(void) { + g_log_set_default_handler(null_logging_handler, NULL); +} + +/* This function skips the Govips logging handler and logs + directly to stdout. To be used only for testing and debugging. + Needed for CI because of a Go macOS bug which doesn't clean cgo callbacks on + exit. */ +void vips_default_logging_handler(void) { + g_log_set_default_handler(g_log_default_handler, NULL); +} \ No newline at end of file diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/govips.go b/vendor/github.com/davidbyttow/govips/v2/vips/govips.go new file mode 100644 index 000000000..6969b62ab --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/govips.go @@ -0,0 +1,265 @@ +// Package vips provides go bindings for libvips, a fast image processing library. +package vips + +// #cgo pkg-config: vips +// #include +// #include "govips.h" +import "C" +import ( + "fmt" + "os" + "runtime" + "strings" + "sync" +) + +const ( + defaultConcurrencyLevel = 1 + defaultMaxCacheMem = 50 * 1024 * 1024 + defaultMaxCacheSize = 100 + defaultMaxCacheFiles = 0 +) + +var ( + // Version is the full libvips version string (x.y.z) + Version = C.GoString(C.vips_version_string()) + + // MajorVersion is the libvips major component of the version string (x in x.y.z) + MajorVersion = int(C.vips_version(0)) + + // MinorVersion is the libvips minor component of the version string (y in x.y.z) + MinorVersion = int(C.vips_version(1)) + + // MicroVersion is the libvips micro component of the version string (z in x.y.z) + // Also known as patch version + MicroVersion = int(C.vips_version(2)) + + running = false + hasShutdown = false + initLock sync.Mutex + statCollectorDone chan struct{} + once sync.Once + typeLoaders = make(map[string]ImageType) + supportedImageTypes = make(map[ImageType]bool) +) + +// Config allows fine-tuning of libvips library +type Config struct { + ConcurrencyLevel int + MaxCacheFiles int + MaxCacheMem int + MaxCacheSize int + ReportLeaks bool + CacheTrace bool + CollectStats bool +} + +// Startup sets up the libvips support and ensures the versions are correct. Pass in nil for +// default configuration. +func Startup(config *Config) { + if hasShutdown { + panic("govips cannot be stopped and restarted") + } + + initLock.Lock() + defer initLock.Unlock() + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if running { + govipsLog("govips", LogLevelInfo, "warning libvips already started") + return + } + + if MajorVersion < 8 { + panic("govips requires libvips version 8.10+") + } + + if MajorVersion == 8 && MinorVersion < 10 { + panic("govips requires libvips version 8.10+") + } + + cName := C.CString("govips") + defer freeCString(cName) + + // Initialize govips logging handler and verbosity filter to historical default + if !currentLoggingOverridden { + govipsLoggingSettings(nil, LogLevelInfo) + } + + // Override default glib logging handler to intercept logging messages + enableLogging() + + err := C.vips_init(cName) + if err != 0 { + panic(fmt.Sprintf("Failed to start vips code=%v", err)) + } + + initializeICCProfiles() + + running = true + + if config != nil { + if config.CollectStats { + statCollectorDone = collectStats() + } + + C.vips_leak_set(toGboolean(config.ReportLeaks)) + + if config.ConcurrencyLevel >= 0 { + C.vips_concurrency_set(C.int(config.ConcurrencyLevel)) + } else { + C.vips_concurrency_set(defaultConcurrencyLevel) + } + + if config.MaxCacheFiles >= 0 { + C.vips_cache_set_max_files(C.int(config.MaxCacheFiles)) + } else { + C.vips_cache_set_max_files(defaultMaxCacheFiles) + } + + if config.MaxCacheMem >= 0 { + C.vips_cache_set_max_mem(C.size_t(config.MaxCacheMem)) + } else { + C.vips_cache_set_max_mem(defaultMaxCacheMem) + } + + if config.MaxCacheSize >= 0 { + C.vips_cache_set_max(C.int(config.MaxCacheSize)) + } else { + C.vips_cache_set_max(defaultMaxCacheSize) + } + + if config.CacheTrace { + C.vips_cache_set_trace(toGboolean(true)) + } + } else { + C.vips_concurrency_set(defaultConcurrencyLevel) + C.vips_cache_set_max(defaultMaxCacheSize) + C.vips_cache_set_max_mem(defaultMaxCacheMem) + C.vips_cache_set_max_files(defaultMaxCacheFiles) + } + + govipsLog("govips", LogLevelInfo, fmt.Sprintf("vips %s started with concurrency=%d cache_max_files=%d cache_max_mem=%d cache_max=%d", + Version, + int(C.vips_concurrency_get()), + int(C.vips_cache_get_max_files()), + int(C.vips_cache_get_max_mem()), + int(C.vips_cache_get_max()))) + + initTypes() +} + +func enableLogging() { + C.vips_set_logging_handler() +} + +func disableLogging() { + C.vips_unset_logging_handler() +} + +// consoleLogging overrides the Govips logging handler and makes glib +// use its default logging handler which outputs everything to console. +// Needed for CI unit testing due to a macOS bug in Go (doesn't clean cgo callbacks on exit) +func consoleLogging() { + C.vips_default_logging_handler() +} + +// Shutdown libvips +func Shutdown() { + hasShutdown = true + + if statCollectorDone != nil { + statCollectorDone <- struct{}{} + } + + initLock.Lock() + defer initLock.Unlock() + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if !running { + govipsLog("govips", LogLevelInfo, "warning libvips not started") + return + } + + os.RemoveAll(temporaryDirectory) + + C.vips_shutdown() + disableLogging() + running = false +} + +// ShutdownThread clears the cache for for the given thread. This needs to be +// called when a thread using vips exits. +func ShutdownThread() { + C.vips_thread_shutdown() +} + +// ClearCache drops the whole operation cache, handy for leak tracking. +func ClearCache() { + C.vips_cache_drop_all() +} + +// PrintCache prints the whole operation cache to stdout for debugging purposes. +func PrintCache() { + C.vips_cache_print() +} + +// PrintObjectReport outputs all of the current internal objects in libvips +func PrintObjectReport(label string) { + govipsLog("govips", LogLevelInfo, fmt.Sprintf("\n=======================================\nvips live objects: %s...\n", label)) + C.vips_object_print_all() + govipsLog("govips", LogLevelInfo, "=======================================\n\n") +} + +// MemoryStats is a data structure that houses various memory statistics from ReadVipsMemStats() +type MemoryStats struct { + Mem int64 + MemHigh int64 + Files int64 + Allocs int64 +} + +// ReadVipsMemStats returns various memory statistics such as allocated memory and open files. +func ReadVipsMemStats(stats *MemoryStats) { + stats.Mem = int64(C.vips_tracked_get_mem()) + stats.MemHigh = int64(C.vips_tracked_get_mem_highwater()) + stats.Allocs = int64(C.vips_tracked_get_allocs()) + stats.Files = int64(C.vips_tracked_get_files()) +} + +func startupIfNeeded() { + if !running { + govipsLog("govips", LogLevelInfo, "libvips was forcibly started automatically, consider calling Startup/Shutdown yourself") + Startup(nil) + } +} + +// InitTypes initializes caches and figures out which image types are supported +func initTypes() { + once.Do(func() { + cType := C.CString("VipsOperation") + defer freeCString(cType) + + for k, v := range ImageTypes { + name := strings.ToLower("VipsForeignLoad" + v) + typeLoaders[name] = k + typeLoaders[name+"buffer"] = k + + cFunc := C.CString(v + "load") + //noinspection GoDeferInLoop + defer freeCString(cFunc) + + ret := C.vips_type_find(cType, cFunc) + + supportedImageTypes[k] = int(ret) != 0 + + if supportedImageTypes[k] { + govipsLog("govips", LogLevelInfo, fmt.Sprintf("registered image type loader type=%s", v)) + } + } + }) +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/govips.h b/vendor/github.com/davidbyttow/govips/v2/vips/govips.h new file mode 100644 index 000000000..31f42b04c --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/govips.h @@ -0,0 +1,26 @@ + +// clang-format off +// include order matters +#include +#include +#include +// clang-format on + +#if (VIPS_MAJOR_VERSION < 8) +error_requires_version_8 +#endif + + extern void + govipsLoggingHandler(char *log_domain, int log_level, char *message); + +static void govips_logging_handler(const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, gpointer user_data); + +static void null_logging_handler(const gchar *log_domain, + GLogLevelFlags log_level, const gchar *message, + gpointer user_data); + +void vips_set_logging_handler(void); +void vips_unset_logging_handler(void); +void vips_default_logging_handler(void); \ No newline at end of file diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/header.c b/vendor/github.com/davidbyttow/govips/v2/vips/header.c new file mode 100644 index 000000000..6744c840d --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/header.c @@ -0,0 +1,122 @@ +#include "header.h" + +#include + +unsigned long has_icc_profile(VipsImage *in) { + return vips_image_get_typeof(in, VIPS_META_ICC_NAME); +} + +unsigned long get_icc_profile(VipsImage *in, const void **data, + size_t *dataLength) { + return image_get_blob(in, VIPS_META_ICC_NAME, data, dataLength); +} + +gboolean remove_icc_profile(VipsImage *in) { + return vips_image_remove(in, VIPS_META_ICC_NAME); +} + +unsigned long has_iptc(VipsImage *in) { + return vips_image_get_typeof(in, VIPS_META_IPTC_NAME); +} + +char **image_get_fields(VipsImage *in) { return vips_image_get_fields(in); } + +void image_set_string(VipsImage *in, const char *name, const char *str) { + vips_image_set_string(in, name, str); +} + +unsigned long image_get_string(VipsImage *in, const char *name, + const char **out) { + return vips_image_get_string(in, name, out); +} + +unsigned long image_get_as_string(VipsImage *in, const char *name, char **out) { + return vips_image_get_as_string(in, name, out); +} + +void remove_field(VipsImage *in, char *field) { vips_image_remove(in, field); } + +int get_meta_orientation(VipsImage *in) { + int orientation = 0; + if (vips_image_get_typeof(in, VIPS_META_ORIENTATION) != 0) { + vips_image_get_int(in, VIPS_META_ORIENTATION, &orientation); + } + + return orientation; +} + +void remove_meta_orientation(VipsImage *in) { + vips_image_remove(in, VIPS_META_ORIENTATION); +} + +void set_meta_orientation(VipsImage *in, int orientation) { + vips_image_set_int(in, VIPS_META_ORIENTATION, orientation); +} + +// https://libvips.github.io/libvips/API/current/libvips-header.html#vips-image-get-n-pages +int get_image_n_pages(VipsImage *in) { + int n_pages = 0; + n_pages = vips_image_get_n_pages(in); + return n_pages; +} + +void set_image_n_pages(VipsImage *in, int n_pages) { + vips_image_set_int(in, VIPS_META_N_PAGES, n_pages); +} + +// https://www.libvips.org/API/current/libvips-header.html#vips-image-get-page-height +int get_page_height(VipsImage *in) { + int page_height = 0; + page_height = vips_image_get_page_height(in); + return page_height; +} + +void set_page_height(VipsImage *in, int height) { + vips_image_set_int(in, VIPS_META_PAGE_HEIGHT, height); +} + +int get_meta_loader(const VipsImage *in, const char **out) { + return vips_image_get_string(in, VIPS_META_LOADER, out); +} + +int get_image_delay(VipsImage *in, int **out) { + return vips_image_get_array_int(in, "delay", out, NULL); +} + +void set_image_delay(VipsImage *in, const int *array, int n) { + return vips_image_set_array_int(in, "delay", array, n); +} + +void image_set_double(VipsImage *in, const char *name, double i) { + vips_image_set_double(in, name, i); +} + +unsigned long image_get_double(VipsImage *in, const char *name, double *out) { + return vips_image_get_double(in, name, out); +} + +void image_set_int(VipsImage *in, const char *name, int i) { + vips_image_set_int(in, name, i); +} + +unsigned long image_get_int(VipsImage *in, const char *name, int *out) { + return vips_image_get_int(in, name, out); +} + +void image_set_blob(VipsImage *in, const char *name, const void *data, + size_t dataLength) { + vips_image_set_blob_copy(in, name, data, dataLength); +} + +unsigned long image_get_blob(VipsImage *in, const char *name, const void **data, + size_t *dataLength) { + if (vips_image_get_typeof(in, name) == 0) { + return 0; + } + + if (vips_image_get_blob(in, name, data, dataLength)) { + return -1; + } + + return 0; +} \ No newline at end of file diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/header.go b/vendor/github.com/davidbyttow/govips/v2/vips/header.go new file mode 100644 index 000000000..a85e47c51 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/header.go @@ -0,0 +1,289 @@ +package vips + +// #include "header.h" +import "C" +import ( + "strings" + "unsafe" +) + +func vipsHasICCProfile(in *C.VipsImage) bool { + return int(C.has_icc_profile(in)) != 0 +} + +func vipsGetICCProfile(in *C.VipsImage) ([]byte, bool) { + var bufPtr unsafe.Pointer + var dataLength C.size_t + + if int(C.get_icc_profile(in, &bufPtr, &dataLength)) != 0 { + return nil, false + } + + buf := C.GoBytes(bufPtr, C.int(dataLength)) + return buf, true +} + +func vipsRemoveICCProfile(in *C.VipsImage) bool { + return fromGboolean(C.remove_icc_profile(in)) +} + +func vipsHasIPTC(in *C.VipsImage) bool { + return int(C.has_iptc(in)) != 0 +} + +func vipsImageGetFields(in *C.VipsImage) (fields []string) { + const maxFields = 256 + + rawFields := C.image_get_fields(in) + defer C.g_strfreev(rawFields) + + cFields := (*[maxFields]*C.char)(unsafe.Pointer(rawFields))[:maxFields:maxFields] + + for _, field := range cFields { + if field == nil { + break + } + fields = append(fields, C.GoString(field)) + } + return +} + +func vipsImageGetExifData(in *C.VipsImage) map[string]string { + fields := vipsImageGetFields(in) + + exifData := map[string]string{} + for _, field := range fields { + if strings.HasPrefix(field, "exif") { + exifData[field] = vipsImageGetString(in, field) + } + } + + return exifData +} + +func vipsRemoveMetadata(in *C.VipsImage, keep ...string) { + fields := vipsImageGetFields(in) + + retain := append(keep, technicalMetadata...) + + for _, field := range fields { + if contains(retain, field) { + continue + } + + cField := C.CString(field) + + C.remove_field(in, cField) + + C.free(unsafe.Pointer(cField)) + } +} + +var technicalMetadata = []string{ + C.VIPS_META_ICC_NAME, + C.VIPS_META_ORIENTATION, + C.VIPS_META_N_PAGES, + C.VIPS_META_PAGE_HEIGHT, +} + +func contains(a []string, x string) bool { + for _, n := range a { + if x == n { + return true + } + } + return false +} + +func vipsGetMetaOrientation(in *C.VipsImage) int { + return int(C.get_meta_orientation(in)) +} + +func vipsRemoveMetaOrientation(in *C.VipsImage) { + C.remove_meta_orientation(in) +} + +func vipsSetMetaOrientation(in *C.VipsImage, orientation int) { + C.set_meta_orientation(in, C.int(orientation)) +} + +func vipsGetImageNPages(in *C.VipsImage) int { + return int(C.get_image_n_pages(in)) +} + +func vipsSetImageNPages(in *C.VipsImage, pages int) { + C.set_image_n_pages(in, C.int(pages)) +} + +func vipsGetPageHeight(in *C.VipsImage) int { + return int(C.get_page_height(in)) +} + +func vipsSetPageHeight(in *C.VipsImage, height int) { + C.set_page_height(in, C.int(height)) +} + +func vipsImageGetMetaLoader(in *C.VipsImage) (string, bool) { + var out *C.char + defer freeCString(out) + code := int(C.get_meta_loader(in, &out)) + return C.GoString(out), code == 0 +} + +func vipsImageGetDelay(in *C.VipsImage, n int) ([]int, error) { + incOpCounter("imageGetDelay") + var out *C.int + defer gFreePointer(unsafe.Pointer(out)) + + if err := C.get_image_delay(in, &out); err != 0 { + return nil, handleVipsError() + } + return fromCArrayInt(out, n), nil +} + +func vipsImageSetDelay(in *C.VipsImage, data []C.int) error { + incOpCounter("imageSetDelay") + if n := len(data); n > 0 { + C.set_image_delay(in, &data[0], C.int(n)) + } + return nil +} + +// vipsDetermineImageTypeFromMetaLoader determine the image type from vips-loader metadata +func vipsDetermineImageTypeFromMetaLoader(in *C.VipsImage) ImageType { + vipsLoader, ok := vipsImageGetMetaLoader(in) + if vipsLoader == "" || !ok { + return ImageTypeUnknown + } + if strings.HasPrefix(vipsLoader, "jpeg") { + return ImageTypeJPEG + } + if strings.HasPrefix(vipsLoader, "png") { + return ImageTypePNG + } + if strings.HasPrefix(vipsLoader, "gif") { + return ImageTypeGIF + } + if strings.HasPrefix(vipsLoader, "svg") { + return ImageTypeSVG + } + if strings.HasPrefix(vipsLoader, "webp") { + return ImageTypeWEBP + } + if strings.HasPrefix(vipsLoader, "jp2k") { + return ImageTypeJP2K + } + if strings.HasPrefix(vipsLoader, "jxl") { + return ImageTypeJXL + } + if strings.HasPrefix(vipsLoader, "magick") { + return ImageTypeMagick + } + if strings.HasPrefix(vipsLoader, "tiff") { + return ImageTypeTIFF + } + if strings.HasPrefix(vipsLoader, "heif") { + return ImageTypeHEIF + } + if strings.HasPrefix(vipsLoader, "pdf") { + return ImageTypePDF + } + return ImageTypeUnknown +} + +func vipsImageSetBlob(in *C.VipsImage, name string, data []byte) { + cData := unsafe.Pointer(&data) + cDataLength := C.size_t(len(data)) + + cField := C.CString(name) + defer freeCString(cField) + C.image_set_blob(in, cField, cData, cDataLength) +} + +func vipsImageGetBlob(in *C.VipsImage, name string) []byte { + var bufPtr unsafe.Pointer + var dataLength C.size_t + + cField := C.CString(name) + defer freeCString(cField) + if int(C.image_get_blob(in, cField, &bufPtr, &dataLength)) != 0 { + return nil + } + + buf := C.GoBytes(bufPtr, C.int(dataLength)) + return buf +} + +func vipsImageSetDouble(in *C.VipsImage, name string, f float64) { + cField := C.CString(name) + defer freeCString(cField) + + cDouble := C.double(f) + C.image_set_double(in, cField, cDouble) +} + +func vipsImageGetDouble(in *C.VipsImage, name string) float64 { + cField := C.CString(name) + defer freeCString(cField) + + var cDouble C.double + if int(C.image_get_double(in, cField, &cDouble)) == 0 { + return float64(cDouble) + } + + return 0 +} + +func vipsImageSetInt(in *C.VipsImage, name string, i int) { + cField := C.CString(name) + defer freeCString(cField) + + cInt := C.int(i) + C.image_set_int(in, cField, cInt) +} + +func vipsImageGetInt(in *C.VipsImage, name string) int { + cField := C.CString(name) + defer freeCString(cField) + + var cInt C.int + if int(C.image_get_int(in, cField, &cInt)) == 0 { + return int(cInt) + } + + return 0 +} + +func vipsImageSetString(in *C.VipsImage, name string, str string) { + cField := C.CString(name) + defer freeCString(cField) + + cStr := C.CString(str) + defer freeCString(cStr) + + C.image_set_string(in, cField, cStr) +} + +func vipsImageGetString(in *C.VipsImage, name string) string { + cField := C.CString(name) + defer freeCString(cField) + var cFieldValue *C.char + defer freeCString(cFieldValue) + if int(C.image_get_string(in, cField, &cFieldValue)) == 0 { + return C.GoString(cFieldValue) + } + + return "" +} + +func vipsImageGetAsString(in *C.VipsImage, name string) string { + cField := C.CString(name) + defer freeCString(cField) + var cFieldValue *C.char + defer freeCString(cFieldValue) + if int(C.image_get_as_string(in, cField, &cFieldValue)) == 0 { + return C.GoString(cFieldValue) + } + + return "" +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/header.h b/vendor/github.com/davidbyttow/govips/v2/vips/header.h new file mode 100644 index 000000000..9a4983e2e --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/header.h @@ -0,0 +1,41 @@ +// https://libvips.github.io/libvips/API/current/libvips-header.html + +#include +#include + +unsigned long has_icc_profile(VipsImage *in); +unsigned long get_icc_profile(VipsImage *in, const void **data, + size_t *dataLength); +int remove_icc_profile(VipsImage *in); + +unsigned long has_iptc(VipsImage *in); +char **image_get_fields(VipsImage *in); + +void image_set_string(VipsImage *in, const char *name, const char *str); +unsigned long image_get_string(VipsImage *in, const char *name, + const char **out); +unsigned long image_get_as_string(VipsImage *in, const char *name, char **out); + +void remove_field(VipsImage *in, char *field); + +int get_meta_orientation(VipsImage *in); +void remove_meta_orientation(VipsImage *in); +void set_meta_orientation(VipsImage *in, int orientation); +int get_image_n_pages(VipsImage *in); +void set_image_n_pages(VipsImage *in, int n_pages); +int get_page_height(VipsImage *in); +void set_page_height(VipsImage *in, int height); +int get_meta_loader(const VipsImage *in, const char **out); +int get_image_delay(VipsImage *in, int **out); +void set_image_delay(VipsImage *in, const int *array, int n); + +void image_set_blob(VipsImage *in, const char *name, const void *data, + size_t dataLength); +unsigned long image_get_blob(VipsImage *in, const char *name, const void **data, + size_t *dataLength); + +void image_set_double(VipsImage *in, const char *name, double i); +unsigned long image_get_double(VipsImage *in, const char *name, double *out); + +void image_set_int(VipsImage *in, const char *name, int i); +unsigned long image_get_int(VipsImage *in, const char *name, int *out); \ No newline at end of file diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/icc_profiles.go b/vendor/github.com/davidbyttow/govips/v2/vips/icc_profiles.go new file mode 100644 index 000000000..f10c42923 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/icc_profiles.go @@ -0,0 +1,675 @@ +package vips + +import ( + "fmt" + "os" + "path/filepath" +) + +var ( + // ATTRIBUTION: + // The following micro icc profile taken from: https://github.com/saucecontrol/Compact-ICC-Profiles. + // Read more (very interesting): https://photosauce.net/blog/post/making-a-minimal-srgb-icc-profile-part-1-trim-the-fat-abuse-the-spec + sRGBV2MicroICCProfile = []byte{ + 0x00, 0x00, 0x01, 0xc8, 0x6c, 0x63, 0x6d, 0x73, 0x02, 0x10, 0x00, 0x00, + 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20, + 0x07, 0xe2, 0x00, 0x03, 0x00, 0x14, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x1d, + 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x61, 0x77, 0x73, 0x63, 0x74, 0x72, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x68, 0x61, 0x6e, 0x64, + 0x9d, 0x91, 0x00, 0x3d, 0x40, 0x80, 0xb0, 0x3d, 0x40, 0x74, 0x2c, 0x81, + 0x9e, 0xa5, 0x22, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x5f, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x0c, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x14, + 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x54, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x00, 0x60, + 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x00, 0x60, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x00, 0x60, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x75, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x43, 0x30, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf3, 0x54, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xc9, + 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa0, + 0x00, 0x00, 0x38, 0xf2, 0x00, 0x00, 0x03, 0x8f, 0x58, 0x59, 0x5a, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x96, 0x00, 0x00, 0xb7, 0x89, + 0x00, 0x00, 0x18, 0xda, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x24, 0xa0, 0x00, 0x00, 0x0f, 0x85, 0x00, 0x00, 0xb6, 0xc4, + 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, + 0x00, 0x00, 0x00, 0x7c, 0x00, 0xf8, 0x01, 0x9c, 0x02, 0x75, 0x03, 0x83, + 0x04, 0xc9, 0x06, 0x4e, 0x08, 0x12, 0x0a, 0x18, 0x0c, 0x62, 0x0e, 0xf4, + 0x11, 0xcf, 0x14, 0xf6, 0x18, 0x6a, 0x1c, 0x2e, 0x20, 0x43, 0x24, 0xac, + 0x29, 0x6a, 0x2e, 0x7e, 0x33, 0xeb, 0x39, 0xb3, 0x3f, 0xd6, 0x46, 0x57, + 0x4d, 0x36, 0x54, 0x76, 0x5c, 0x17, 0x64, 0x1d, 0x6c, 0x86, 0x75, 0x56, + 0x7e, 0x8d, 0x88, 0x2c, 0x92, 0x36, 0x9c, 0xab, 0xa7, 0x8c, 0xb2, 0xdb, + 0xbe, 0x99, 0xca, 0xc7, 0xd7, 0x65, 0xe4, 0x77, 0xf1, 0xf9, 0xff, 0xff, + } + + // ATTRIBUTION: + // The following micro icc profile taken from: https://github.com/saucecontrol/Compact-ICC-Profiles. + // Read more (very interesting): https://photosauce.net/blog/post/making-a-minimal-srgb-icc-profile-part-1-trim-the-fat-abuse-the-spec + sGrayV2MicroICCProfile = []byte{ + 0x00, 0x00, 0x01, 0x50, 0x6c, 0x63, 0x6d, 0x73, 0x02, 0x10, 0x00, 0x00, + 0x6d, 0x6e, 0x74, 0x72, 0x47, 0x52, 0x41, 0x59, 0x58, 0x59, 0x5a, 0x20, + 0x07, 0xe2, 0x00, 0x03, 0x00, 0x14, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x1d, + 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x61, 0x77, 0x73, 0x63, 0x74, 0x72, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x68, 0x61, 0x6e, 0x64, + 0x05, 0xd2, 0x02, 0xa7, 0xf9, 0xdd, 0x47, 0x94, 0xc7, 0x4f, 0x4c, 0x5f, + 0x26, 0x82, 0x3a, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x5f, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x0c, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x14, + 0x6b, 0x54, 0x52, 0x43, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x60, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x75, 0x47, 0x72, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x43, 0x30, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf3, 0x54, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xc9, + 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, + 0x00, 0x00, 0x00, 0x7c, 0x00, 0xf8, 0x01, 0x9c, 0x02, 0x75, 0x03, 0x83, + 0x04, 0xc9, 0x06, 0x4e, 0x08, 0x12, 0x0a, 0x18, 0x0c, 0x62, 0x0e, 0xf4, + 0x11, 0xcf, 0x14, 0xf6, 0x18, 0x6a, 0x1c, 0x2e, 0x20, 0x43, 0x24, 0xac, + 0x29, 0x6a, 0x2e, 0x7e, 0x33, 0xeb, 0x39, 0xb3, 0x3f, 0xd6, 0x46, 0x57, + 0x4d, 0x36, 0x54, 0x76, 0x5c, 0x17, 0x64, 0x1d, 0x6c, 0x86, 0x75, 0x56, + 0x7e, 0x8d, 0x88, 0x2c, 0x92, 0x36, 0x9c, 0xab, 0xa7, 0x8c, 0xb2, 0xdb, + 0xbe, 0x99, 0xca, 0xc7, 0xd7, 0x65, 0xe4, 0x77, 0xf1, 0xf9, 0xff, 0xff, + } + + sRGBIEC6196621ICCProfile = []byte{ + 0x00, 0x00, 0x0b, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20, + 0x07, 0xd9, 0x00, 0x03, 0x00, 0x1b, 0x00, 0x15, 0x00, 0x24, 0x00, 0x1f, + 0x61, 0x63, 0x73, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x00, 0x00, 0x00, 0x00, + 0x29, 0xf8, 0x3d, 0xde, 0xaf, 0xf2, 0x55, 0xae, 0x78, 0x42, 0xfa, 0xe4, + 0xca, 0x83, 0x39, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x00, 0x79, + 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xd4, 0x00, 0x00, 0x08, 0x0c, + 0x64, 0x6d, 0x64, 0x64, 0x00, 0x00, 0x09, 0xe0, 0x00, 0x00, 0x00, 0x88, + 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x0a, 0x68, 0x00, 0x00, 0x00, 0x14, + 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xd4, 0x00, 0x00, 0x08, 0x0c, + 0x6c, 0x75, 0x6d, 0x69, 0x00, 0x00, 0x0a, 0x7c, 0x00, 0x00, 0x00, 0x14, + 0x6d, 0x65, 0x61, 0x73, 0x00, 0x00, 0x0a, 0x90, 0x00, 0x00, 0x00, 0x24, + 0x62, 0x6b, 0x70, 0x74, 0x00, 0x00, 0x0a, 0xb4, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x0a, 0xc8, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xd4, 0x00, 0x00, 0x08, 0x0c, + 0x74, 0x65, 0x63, 0x68, 0x00, 0x00, 0x0a, 0xdc, 0x00, 0x00, 0x00, 0x0c, + 0x76, 0x75, 0x65, 0x64, 0x00, 0x00, 0x0a, 0xe8, 0x00, 0x00, 0x00, 0x87, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x0b, 0x70, 0x00, 0x00, 0x00, 0x14, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x0b, 0x84, 0x00, 0x00, 0x00, 0x37, + 0x63, 0x68, 0x61, 0x64, 0x00, 0x00, 0x0b, 0xbc, 0x00, 0x00, 0x00, 0x2c, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, + 0x36, 0x2d, 0x32, 0x2d, 0x31, 0x20, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x20, + 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x24, 0xa0, 0x00, 0x00, 0x0f, 0x84, 0x00, 0x00, 0xb6, 0xcf, + 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x14, 0x00, 0x19, + 0x00, 0x1e, 0x00, 0x23, 0x00, 0x28, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37, + 0x00, 0x3b, 0x00, 0x40, 0x00, 0x45, 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x54, + 0x00, 0x59, 0x00, 0x5e, 0x00, 0x63, 0x00, 0x68, 0x00, 0x6d, 0x00, 0x72, + 0x00, 0x77, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8b, 0x00, 0x90, + 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9f, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xae, + 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc1, 0x00, 0xc6, 0x00, 0xcb, + 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xdb, 0x00, 0xe0, 0x00, 0xe5, 0x00, 0xeb, + 0x00, 0xf0, 0x00, 0xf6, 0x00, 0xfb, 0x01, 0x01, 0x01, 0x07, 0x01, 0x0d, + 0x01, 0x13, 0x01, 0x19, 0x01, 0x1f, 0x01, 0x25, 0x01, 0x2b, 0x01, 0x32, + 0x01, 0x38, 0x01, 0x3e, 0x01, 0x45, 0x01, 0x4c, 0x01, 0x52, 0x01, 0x59, + 0x01, 0x60, 0x01, 0x67, 0x01, 0x6e, 0x01, 0x75, 0x01, 0x7c, 0x01, 0x83, + 0x01, 0x8b, 0x01, 0x92, 0x01, 0x9a, 0x01, 0xa1, 0x01, 0xa9, 0x01, 0xb1, + 0x01, 0xb9, 0x01, 0xc1, 0x01, 0xc9, 0x01, 0xd1, 0x01, 0xd9, 0x01, 0xe1, + 0x01, 0xe9, 0x01, 0xf2, 0x01, 0xfa, 0x02, 0x03, 0x02, 0x0c, 0x02, 0x14, + 0x02, 0x1d, 0x02, 0x26, 0x02, 0x2f, 0x02, 0x38, 0x02, 0x41, 0x02, 0x4b, + 0x02, 0x54, 0x02, 0x5d, 0x02, 0x67, 0x02, 0x71, 0x02, 0x7a, 0x02, 0x84, + 0x02, 0x8e, 0x02, 0x98, 0x02, 0xa2, 0x02, 0xac, 0x02, 0xb6, 0x02, 0xc1, + 0x02, 0xcb, 0x02, 0xd5, 0x02, 0xe0, 0x02, 0xeb, 0x02, 0xf5, 0x03, 0x00, + 0x03, 0x0b, 0x03, 0x16, 0x03, 0x21, 0x03, 0x2d, 0x03, 0x38, 0x03, 0x43, + 0x03, 0x4f, 0x03, 0x5a, 0x03, 0x66, 0x03, 0x72, 0x03, 0x7e, 0x03, 0x8a, + 0x03, 0x96, 0x03, 0xa2, 0x03, 0xae, 0x03, 0xba, 0x03, 0xc7, 0x03, 0xd3, + 0x03, 0xe0, 0x03, 0xec, 0x03, 0xf9, 0x04, 0x06, 0x04, 0x13, 0x04, 0x20, + 0x04, 0x2d, 0x04, 0x3b, 0x04, 0x48, 0x04, 0x55, 0x04, 0x63, 0x04, 0x71, + 0x04, 0x7e, 0x04, 0x8c, 0x04, 0x9a, 0x04, 0xa8, 0x04, 0xb6, 0x04, 0xc4, + 0x04, 0xd3, 0x04, 0xe1, 0x04, 0xf0, 0x04, 0xfe, 0x05, 0x0d, 0x05, 0x1c, + 0x05, 0x2b, 0x05, 0x3a, 0x05, 0x49, 0x05, 0x58, 0x05, 0x67, 0x05, 0x77, + 0x05, 0x86, 0x05, 0x96, 0x05, 0xa6, 0x05, 0xb5, 0x05, 0xc5, 0x05, 0xd5, + 0x05, 0xe5, 0x05, 0xf6, 0x06, 0x06, 0x06, 0x16, 0x06, 0x27, 0x06, 0x37, + 0x06, 0x48, 0x06, 0x59, 0x06, 0x6a, 0x06, 0x7b, 0x06, 0x8c, 0x06, 0x9d, + 0x06, 0xaf, 0x06, 0xc0, 0x06, 0xd1, 0x06, 0xe3, 0x06, 0xf5, 0x07, 0x07, + 0x07, 0x19, 0x07, 0x2b, 0x07, 0x3d, 0x07, 0x4f, 0x07, 0x61, 0x07, 0x74, + 0x07, 0x86, 0x07, 0x99, 0x07, 0xac, 0x07, 0xbf, 0x07, 0xd2, 0x07, 0xe5, + 0x07, 0xf8, 0x08, 0x0b, 0x08, 0x1f, 0x08, 0x32, 0x08, 0x46, 0x08, 0x5a, + 0x08, 0x6e, 0x08, 0x82, 0x08, 0x96, 0x08, 0xaa, 0x08, 0xbe, 0x08, 0xd2, + 0x08, 0xe7, 0x08, 0xfb, 0x09, 0x10, 0x09, 0x25, 0x09, 0x3a, 0x09, 0x4f, + 0x09, 0x64, 0x09, 0x79, 0x09, 0x8f, 0x09, 0xa4, 0x09, 0xba, 0x09, 0xcf, + 0x09, 0xe5, 0x09, 0xfb, 0x0a, 0x11, 0x0a, 0x27, 0x0a, 0x3d, 0x0a, 0x54, + 0x0a, 0x6a, 0x0a, 0x81, 0x0a, 0x98, 0x0a, 0xae, 0x0a, 0xc5, 0x0a, 0xdc, + 0x0a, 0xf3, 0x0b, 0x0b, 0x0b, 0x22, 0x0b, 0x39, 0x0b, 0x51, 0x0b, 0x69, + 0x0b, 0x80, 0x0b, 0x98, 0x0b, 0xb0, 0x0b, 0xc8, 0x0b, 0xe1, 0x0b, 0xf9, + 0x0c, 0x12, 0x0c, 0x2a, 0x0c, 0x43, 0x0c, 0x5c, 0x0c, 0x75, 0x0c, 0x8e, + 0x0c, 0xa7, 0x0c, 0xc0, 0x0c, 0xd9, 0x0c, 0xf3, 0x0d, 0x0d, 0x0d, 0x26, + 0x0d, 0x40, 0x0d, 0x5a, 0x0d, 0x74, 0x0d, 0x8e, 0x0d, 0xa9, 0x0d, 0xc3, + 0x0d, 0xde, 0x0d, 0xf8, 0x0e, 0x13, 0x0e, 0x2e, 0x0e, 0x49, 0x0e, 0x64, + 0x0e, 0x7f, 0x0e, 0x9b, 0x0e, 0xb6, 0x0e, 0xd2, 0x0e, 0xee, 0x0f, 0x09, + 0x0f, 0x25, 0x0f, 0x41, 0x0f, 0x5e, 0x0f, 0x7a, 0x0f, 0x96, 0x0f, 0xb3, + 0x0f, 0xcf, 0x0f, 0xec, 0x10, 0x09, 0x10, 0x26, 0x10, 0x43, 0x10, 0x61, + 0x10, 0x7e, 0x10, 0x9b, 0x10, 0xb9, 0x10, 0xd7, 0x10, 0xf5, 0x11, 0x13, + 0x11, 0x31, 0x11, 0x4f, 0x11, 0x6d, 0x11, 0x8c, 0x11, 0xaa, 0x11, 0xc9, + 0x11, 0xe8, 0x12, 0x07, 0x12, 0x26, 0x12, 0x45, 0x12, 0x64, 0x12, 0x84, + 0x12, 0xa3, 0x12, 0xc3, 0x12, 0xe3, 0x13, 0x03, 0x13, 0x23, 0x13, 0x43, + 0x13, 0x63, 0x13, 0x83, 0x13, 0xa4, 0x13, 0xc5, 0x13, 0xe5, 0x14, 0x06, + 0x14, 0x27, 0x14, 0x49, 0x14, 0x6a, 0x14, 0x8b, 0x14, 0xad, 0x14, 0xce, + 0x14, 0xf0, 0x15, 0x12, 0x15, 0x34, 0x15, 0x56, 0x15, 0x78, 0x15, 0x9b, + 0x15, 0xbd, 0x15, 0xe0, 0x16, 0x03, 0x16, 0x26, 0x16, 0x49, 0x16, 0x6c, + 0x16, 0x8f, 0x16, 0xb2, 0x16, 0xd6, 0x16, 0xfa, 0x17, 0x1d, 0x17, 0x41, + 0x17, 0x65, 0x17, 0x89, 0x17, 0xae, 0x17, 0xd2, 0x17, 0xf7, 0x18, 0x1b, + 0x18, 0x40, 0x18, 0x65, 0x18, 0x8a, 0x18, 0xaf, 0x18, 0xd5, 0x18, 0xfa, + 0x19, 0x20, 0x19, 0x45, 0x19, 0x6b, 0x19, 0x91, 0x19, 0xb7, 0x19, 0xdd, + 0x1a, 0x04, 0x1a, 0x2a, 0x1a, 0x51, 0x1a, 0x77, 0x1a, 0x9e, 0x1a, 0xc5, + 0x1a, 0xec, 0x1b, 0x14, 0x1b, 0x3b, 0x1b, 0x63, 0x1b, 0x8a, 0x1b, 0xb2, + 0x1b, 0xda, 0x1c, 0x02, 0x1c, 0x2a, 0x1c, 0x52, 0x1c, 0x7b, 0x1c, 0xa3, + 0x1c, 0xcc, 0x1c, 0xf5, 0x1d, 0x1e, 0x1d, 0x47, 0x1d, 0x70, 0x1d, 0x99, + 0x1d, 0xc3, 0x1d, 0xec, 0x1e, 0x16, 0x1e, 0x40, 0x1e, 0x6a, 0x1e, 0x94, + 0x1e, 0xbe, 0x1e, 0xe9, 0x1f, 0x13, 0x1f, 0x3e, 0x1f, 0x69, 0x1f, 0x94, + 0x1f, 0xbf, 0x1f, 0xea, 0x20, 0x15, 0x20, 0x41, 0x20, 0x6c, 0x20, 0x98, + 0x20, 0xc4, 0x20, 0xf0, 0x21, 0x1c, 0x21, 0x48, 0x21, 0x75, 0x21, 0xa1, + 0x21, 0xce, 0x21, 0xfb, 0x22, 0x27, 0x22, 0x55, 0x22, 0x82, 0x22, 0xaf, + 0x22, 0xdd, 0x23, 0x0a, 0x23, 0x38, 0x23, 0x66, 0x23, 0x94, 0x23, 0xc2, + 0x23, 0xf0, 0x24, 0x1f, 0x24, 0x4d, 0x24, 0x7c, 0x24, 0xab, 0x24, 0xda, + 0x25, 0x09, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, 0x25, 0xc7, 0x25, 0xf7, + 0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xb7, 0x26, 0xe8, 0x27, 0x18, + 0x27, 0x49, 0x27, 0x7a, 0x27, 0xab, 0x27, 0xdc, 0x28, 0x0d, 0x28, 0x3f, + 0x28, 0x71, 0x28, 0xa2, 0x28, 0xd4, 0x29, 0x06, 0x29, 0x38, 0x29, 0x6b, + 0x29, 0x9d, 0x29, 0xd0, 0x2a, 0x02, 0x2a, 0x35, 0x2a, 0x68, 0x2a, 0x9b, + 0x2a, 0xcf, 0x2b, 0x02, 0x2b, 0x36, 0x2b, 0x69, 0x2b, 0x9d, 0x2b, 0xd1, + 0x2c, 0x05, 0x2c, 0x39, 0x2c, 0x6e, 0x2c, 0xa2, 0x2c, 0xd7, 0x2d, 0x0c, + 0x2d, 0x41, 0x2d, 0x76, 0x2d, 0xab, 0x2d, 0xe1, 0x2e, 0x16, 0x2e, 0x4c, + 0x2e, 0x82, 0x2e, 0xb7, 0x2e, 0xee, 0x2f, 0x24, 0x2f, 0x5a, 0x2f, 0x91, + 0x2f, 0xc7, 0x2f, 0xfe, 0x30, 0x35, 0x30, 0x6c, 0x30, 0xa4, 0x30, 0xdb, + 0x31, 0x12, 0x31, 0x4a, 0x31, 0x82, 0x31, 0xba, 0x31, 0xf2, 0x32, 0x2a, + 0x32, 0x63, 0x32, 0x9b, 0x32, 0xd4, 0x33, 0x0d, 0x33, 0x46, 0x33, 0x7f, + 0x33, 0xb8, 0x33, 0xf1, 0x34, 0x2b, 0x34, 0x65, 0x34, 0x9e, 0x34, 0xd8, + 0x35, 0x13, 0x35, 0x4d, 0x35, 0x87, 0x35, 0xc2, 0x35, 0xfd, 0x36, 0x37, + 0x36, 0x72, 0x36, 0xae, 0x36, 0xe9, 0x37, 0x24, 0x37, 0x60, 0x37, 0x9c, + 0x37, 0xd7, 0x38, 0x14, 0x38, 0x50, 0x38, 0x8c, 0x38, 0xc8, 0x39, 0x05, + 0x39, 0x42, 0x39, 0x7f, 0x39, 0xbc, 0x39, 0xf9, 0x3a, 0x36, 0x3a, 0x74, + 0x3a, 0xb2, 0x3a, 0xef, 0x3b, 0x2d, 0x3b, 0x6b, 0x3b, 0xaa, 0x3b, 0xe8, + 0x3c, 0x27, 0x3c, 0x65, 0x3c, 0xa4, 0x3c, 0xe3, 0x3d, 0x22, 0x3d, 0x61, + 0x3d, 0xa1, 0x3d, 0xe0, 0x3e, 0x20, 0x3e, 0x60, 0x3e, 0xa0, 0x3e, 0xe0, + 0x3f, 0x21, 0x3f, 0x61, 0x3f, 0xa2, 0x3f, 0xe2, 0x40, 0x23, 0x40, 0x64, + 0x40, 0xa6, 0x40, 0xe7, 0x41, 0x29, 0x41, 0x6a, 0x41, 0xac, 0x41, 0xee, + 0x42, 0x30, 0x42, 0x72, 0x42, 0xb5, 0x42, 0xf7, 0x43, 0x3a, 0x43, 0x7d, + 0x43, 0xc0, 0x44, 0x03, 0x44, 0x47, 0x44, 0x8a, 0x44, 0xce, 0x45, 0x12, + 0x45, 0x55, 0x45, 0x9a, 0x45, 0xde, 0x46, 0x22, 0x46, 0x67, 0x46, 0xab, + 0x46, 0xf0, 0x47, 0x35, 0x47, 0x7b, 0x47, 0xc0, 0x48, 0x05, 0x48, 0x4b, + 0x48, 0x91, 0x48, 0xd7, 0x49, 0x1d, 0x49, 0x63, 0x49, 0xa9, 0x49, 0xf0, + 0x4a, 0x37, 0x4a, 0x7d, 0x4a, 0xc4, 0x4b, 0x0c, 0x4b, 0x53, 0x4b, 0x9a, + 0x4b, 0xe2, 0x4c, 0x2a, 0x4c, 0x72, 0x4c, 0xba, 0x4d, 0x02, 0x4d, 0x4a, + 0x4d, 0x93, 0x4d, 0xdc, 0x4e, 0x25, 0x4e, 0x6e, 0x4e, 0xb7, 0x4f, 0x00, + 0x4f, 0x49, 0x4f, 0x93, 0x4f, 0xdd, 0x50, 0x27, 0x50, 0x71, 0x50, 0xbb, + 0x51, 0x06, 0x51, 0x50, 0x51, 0x9b, 0x51, 0xe6, 0x52, 0x31, 0x52, 0x7c, + 0x52, 0xc7, 0x53, 0x13, 0x53, 0x5f, 0x53, 0xaa, 0x53, 0xf6, 0x54, 0x42, + 0x54, 0x8f, 0x54, 0xdb, 0x55, 0x28, 0x55, 0x75, 0x55, 0xc2, 0x56, 0x0f, + 0x56, 0x5c, 0x56, 0xa9, 0x56, 0xf7, 0x57, 0x44, 0x57, 0x92, 0x57, 0xe0, + 0x58, 0x2f, 0x58, 0x7d, 0x58, 0xcb, 0x59, 0x1a, 0x59, 0x69, 0x59, 0xb8, + 0x5a, 0x07, 0x5a, 0x56, 0x5a, 0xa6, 0x5a, 0xf5, 0x5b, 0x45, 0x5b, 0x95, + 0x5b, 0xe5, 0x5c, 0x35, 0x5c, 0x86, 0x5c, 0xd6, 0x5d, 0x27, 0x5d, 0x78, + 0x5d, 0xc9, 0x5e, 0x1a, 0x5e, 0x6c, 0x5e, 0xbd, 0x5f, 0x0f, 0x5f, 0x61, + 0x5f, 0xb3, 0x60, 0x05, 0x60, 0x57, 0x60, 0xaa, 0x60, 0xfc, 0x61, 0x4f, + 0x61, 0xa2, 0x61, 0xf5, 0x62, 0x49, 0x62, 0x9c, 0x62, 0xf0, 0x63, 0x43, + 0x63, 0x97, 0x63, 0xeb, 0x64, 0x40, 0x64, 0x94, 0x64, 0xe9, 0x65, 0x3d, + 0x65, 0x92, 0x65, 0xe7, 0x66, 0x3d, 0x66, 0x92, 0x66, 0xe8, 0x67, 0x3d, + 0x67, 0x93, 0x67, 0xe9, 0x68, 0x3f, 0x68, 0x96, 0x68, 0xec, 0x69, 0x43, + 0x69, 0x9a, 0x69, 0xf1, 0x6a, 0x48, 0x6a, 0x9f, 0x6a, 0xf7, 0x6b, 0x4f, + 0x6b, 0xa7, 0x6b, 0xff, 0x6c, 0x57, 0x6c, 0xaf, 0x6d, 0x08, 0x6d, 0x60, + 0x6d, 0xb9, 0x6e, 0x12, 0x6e, 0x6b, 0x6e, 0xc4, 0x6f, 0x1e, 0x6f, 0x78, + 0x6f, 0xd1, 0x70, 0x2b, 0x70, 0x86, 0x70, 0xe0, 0x71, 0x3a, 0x71, 0x95, + 0x71, 0xf0, 0x72, 0x4b, 0x72, 0xa6, 0x73, 0x01, 0x73, 0x5d, 0x73, 0xb8, + 0x74, 0x14, 0x74, 0x70, 0x74, 0xcc, 0x75, 0x28, 0x75, 0x85, 0x75, 0xe1, + 0x76, 0x3e, 0x76, 0x9b, 0x76, 0xf8, 0x77, 0x56, 0x77, 0xb3, 0x78, 0x11, + 0x78, 0x6e, 0x78, 0xcc, 0x79, 0x2a, 0x79, 0x89, 0x79, 0xe7, 0x7a, 0x46, + 0x7a, 0xa5, 0x7b, 0x04, 0x7b, 0x63, 0x7b, 0xc2, 0x7c, 0x21, 0x7c, 0x81, + 0x7c, 0xe1, 0x7d, 0x41, 0x7d, 0xa1, 0x7e, 0x01, 0x7e, 0x62, 0x7e, 0xc2, + 0x7f, 0x23, 0x7f, 0x84, 0x7f, 0xe5, 0x80, 0x47, 0x80, 0xa8, 0x81, 0x0a, + 0x81, 0x6b, 0x81, 0xcd, 0x82, 0x30, 0x82, 0x92, 0x82, 0xf4, 0x83, 0x57, + 0x83, 0xba, 0x84, 0x1d, 0x84, 0x80, 0x84, 0xe3, 0x85, 0x47, 0x85, 0xab, + 0x86, 0x0e, 0x86, 0x72, 0x86, 0xd7, 0x87, 0x3b, 0x87, 0x9f, 0x88, 0x04, + 0x88, 0x69, 0x88, 0xce, 0x89, 0x33, 0x89, 0x99, 0x89, 0xfe, 0x8a, 0x64, + 0x8a, 0xca, 0x8b, 0x30, 0x8b, 0x96, 0x8b, 0xfc, 0x8c, 0x63, 0x8c, 0xca, + 0x8d, 0x31, 0x8d, 0x98, 0x8d, 0xff, 0x8e, 0x66, 0x8e, 0xce, 0x8f, 0x36, + 0x8f, 0x9e, 0x90, 0x06, 0x90, 0x6e, 0x90, 0xd6, 0x91, 0x3f, 0x91, 0xa8, + 0x92, 0x11, 0x92, 0x7a, 0x92, 0xe3, 0x93, 0x4d, 0x93, 0xb6, 0x94, 0x20, + 0x94, 0x8a, 0x94, 0xf4, 0x95, 0x5f, 0x95, 0xc9, 0x96, 0x34, 0x96, 0x9f, + 0x97, 0x0a, 0x97, 0x75, 0x97, 0xe0, 0x98, 0x4c, 0x98, 0xb8, 0x99, 0x24, + 0x99, 0x90, 0x99, 0xfc, 0x9a, 0x68, 0x9a, 0xd5, 0x9b, 0x42, 0x9b, 0xaf, + 0x9c, 0x1c, 0x9c, 0x89, 0x9c, 0xf7, 0x9d, 0x64, 0x9d, 0xd2, 0x9e, 0x40, + 0x9e, 0xae, 0x9f, 0x1d, 0x9f, 0x8b, 0x9f, 0xfa, 0xa0, 0x69, 0xa0, 0xd8, + 0xa1, 0x47, 0xa1, 0xb6, 0xa2, 0x26, 0xa2, 0x96, 0xa3, 0x06, 0xa3, 0x76, + 0xa3, 0xe6, 0xa4, 0x56, 0xa4, 0xc7, 0xa5, 0x38, 0xa5, 0xa9, 0xa6, 0x1a, + 0xa6, 0x8b, 0xa6, 0xfd, 0xa7, 0x6e, 0xa7, 0xe0, 0xa8, 0x52, 0xa8, 0xc4, + 0xa9, 0x37, 0xa9, 0xa9, 0xaa, 0x1c, 0xaa, 0x8f, 0xab, 0x02, 0xab, 0x75, + 0xab, 0xe9, 0xac, 0x5c, 0xac, 0xd0, 0xad, 0x44, 0xad, 0xb8, 0xae, 0x2d, + 0xae, 0xa1, 0xaf, 0x16, 0xaf, 0x8b, 0xb0, 0x00, 0xb0, 0x75, 0xb0, 0xea, + 0xb1, 0x60, 0xb1, 0xd6, 0xb2, 0x4b, 0xb2, 0xc2, 0xb3, 0x38, 0xb3, 0xae, + 0xb4, 0x25, 0xb4, 0x9c, 0xb5, 0x13, 0xb5, 0x8a, 0xb6, 0x01, 0xb6, 0x79, + 0xb6, 0xf0, 0xb7, 0x68, 0xb7, 0xe0, 0xb8, 0x59, 0xb8, 0xd1, 0xb9, 0x4a, + 0xb9, 0xc2, 0xba, 0x3b, 0xba, 0xb5, 0xbb, 0x2e, 0xbb, 0xa7, 0xbc, 0x21, + 0xbc, 0x9b, 0xbd, 0x15, 0xbd, 0x8f, 0xbe, 0x0a, 0xbe, 0x84, 0xbe, 0xff, + 0xbf, 0x7a, 0xbf, 0xf5, 0xc0, 0x70, 0xc0, 0xec, 0xc1, 0x67, 0xc1, 0xe3, + 0xc2, 0x5f, 0xc2, 0xdb, 0xc3, 0x58, 0xc3, 0xd4, 0xc4, 0x51, 0xc4, 0xce, + 0xc5, 0x4b, 0xc5, 0xc8, 0xc6, 0x46, 0xc6, 0xc3, 0xc7, 0x41, 0xc7, 0xbf, + 0xc8, 0x3d, 0xc8, 0xbc, 0xc9, 0x3a, 0xc9, 0xb9, 0xca, 0x38, 0xca, 0xb7, + 0xcb, 0x36, 0xcb, 0xb6, 0xcc, 0x35, 0xcc, 0xb5, 0xcd, 0x35, 0xcd, 0xb5, + 0xce, 0x36, 0xce, 0xb6, 0xcf, 0x37, 0xcf, 0xb8, 0xd0, 0x39, 0xd0, 0xba, + 0xd1, 0x3c, 0xd1, 0xbe, 0xd2, 0x3f, 0xd2, 0xc1, 0xd3, 0x44, 0xd3, 0xc6, + 0xd4, 0x49, 0xd4, 0xcb, 0xd5, 0x4e, 0xd5, 0xd1, 0xd6, 0x55, 0xd6, 0xd8, + 0xd7, 0x5c, 0xd7, 0xe0, 0xd8, 0x64, 0xd8, 0xe8, 0xd9, 0x6c, 0xd9, 0xf1, + 0xda, 0x76, 0xda, 0xfb, 0xdb, 0x80, 0xdc, 0x05, 0xdc, 0x8a, 0xdd, 0x10, + 0xdd, 0x96, 0xde, 0x1c, 0xde, 0xa2, 0xdf, 0x29, 0xdf, 0xaf, 0xe0, 0x36, + 0xe0, 0xbd, 0xe1, 0x44, 0xe1, 0xcc, 0xe2, 0x53, 0xe2, 0xdb, 0xe3, 0x63, + 0xe3, 0xeb, 0xe4, 0x73, 0xe4, 0xfc, 0xe5, 0x84, 0xe6, 0x0d, 0xe6, 0x96, + 0xe7, 0x1f, 0xe7, 0xa9, 0xe8, 0x32, 0xe8, 0xbc, 0xe9, 0x46, 0xe9, 0xd0, + 0xea, 0x5b, 0xea, 0xe5, 0xeb, 0x70, 0xeb, 0xfb, 0xec, 0x86, 0xed, 0x11, + 0xed, 0x9c, 0xee, 0x28, 0xee, 0xb4, 0xef, 0x40, 0xef, 0xcc, 0xf0, 0x58, + 0xf0, 0xe5, 0xf1, 0x72, 0xf1, 0xff, 0xf2, 0x8c, 0xf3, 0x19, 0xf3, 0xa7, + 0xf4, 0x34, 0xf4, 0xc2, 0xf5, 0x50, 0xf5, 0xde, 0xf6, 0x6d, 0xf6, 0xfb, + 0xf7, 0x8a, 0xf8, 0x19, 0xf8, 0xa8, 0xf9, 0x38, 0xf9, 0xc7, 0xfa, 0x57, + 0xfa, 0xe7, 0xfb, 0x77, 0xfc, 0x07, 0xfc, 0x98, 0xfd, 0x29, 0xfd, 0xba, + 0xfe, 0x4b, 0xfe, 0xdc, 0xff, 0x6d, 0xff, 0xff, 0x64, 0x65, 0x73, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x49, 0x45, 0x43, 0x20, + 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2d, 0x31, 0x20, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20, 0x43, 0x6f, + 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x53, 0x70, 0x61, 0x63, 0x65, 0x20, 0x2d, + 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x99, + 0x00, 0x00, 0xb7, 0x85, 0x00, 0x00, 0x18, 0xda, 0x58, 0x59, 0x5a, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6d, 0x65, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x16, 0x00, 0x00, 0x03, 0x33, 0x00, 0x00, 0x02, 0xa4, + 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa2, + 0x00, 0x00, 0x38, 0xf5, 0x00, 0x00, 0x03, 0x90, 0x73, 0x69, 0x67, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x54, 0x20, 0x64, 0x65, 0x73, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x52, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x56, 0x69, 0x65, 0x77, 0x69, 0x6e, + 0x67, 0x20, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x69, 0x6e, 0x20, 0x49, 0x45, 0x43, 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, + 0x2d, 0x32, 0x2d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x74, 0x65, 0x78, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, + 0x74, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x43, 0x6f, + 0x6e, 0x73, 0x6f, 0x72, 0x74, 0x69, 0x75, 0x6d, 0x2c, 0x20, 0x32, 0x30, + 0x30, 0x39, 0x00, 0x00, 0x73, 0x66, 0x33, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x0c, 0x44, 0x00, 0x00, 0x05, 0xdf, 0xff, 0xff, 0xf3, 0x26, + 0x00, 0x00, 0x07, 0x94, 0x00, 0x00, 0xfd, 0x8f, 0xff, 0xff, 0xfb, 0xa1, + 0xff, 0xff, 0xfd, 0xa2, 0x00, 0x00, 0x03, 0xdb, 0x00, 0x00, 0xc0, 0x75, + } + + genericGrayGamma22ICCProfile = []byte{ + 0x00, 0x00, 0x0e, 0x04, 0x61, 0x70, 0x70, 0x6c, 0x02, 0x00, 0x00, 0x00, + 0x6d, 0x6e, 0x74, 0x72, 0x47, 0x52, 0x41, 0x59, 0x58, 0x59, 0x5a, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x63, 0x73, 0x70, 0x41, 0x50, 0x50, 0x4c, 0x00, 0x00, 0x00, 0x00, + 0x6e, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x61, 0x70, 0x70, 0x6c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x6b, 0x54, 0x52, 0x43, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x08, 0x0c, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x08, 0xcc, 0x00, 0x00, 0x00, 0x14, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x08, 0xe0, 0x00, 0x00, 0x00, 0x23, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x00, 0x79, + 0x64, 0x73, 0x63, 0x6d, 0x00, 0x00, 0x09, 0x80, 0x00, 0x00, 0x04, 0x82, + 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x14, 0x00, 0x19, + 0x00, 0x1e, 0x00, 0x23, 0x00, 0x28, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37, + 0x00, 0x3b, 0x00, 0x40, 0x00, 0x45, 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x54, + 0x00, 0x59, 0x00, 0x5e, 0x00, 0x63, 0x00, 0x68, 0x00, 0x6d, 0x00, 0x72, + 0x00, 0x77, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8b, 0x00, 0x90, + 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9f, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xae, + 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc1, 0x00, 0xc6, 0x00, 0xcb, + 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xdb, 0x00, 0xe0, 0x00, 0xe5, 0x00, 0xeb, + 0x00, 0xf0, 0x00, 0xf6, 0x00, 0xfb, 0x01, 0x01, 0x01, 0x07, 0x01, 0x0d, + 0x01, 0x13, 0x01, 0x19, 0x01, 0x1f, 0x01, 0x25, 0x01, 0x2b, 0x01, 0x32, + 0x01, 0x38, 0x01, 0x3e, 0x01, 0x45, 0x01, 0x4c, 0x01, 0x52, 0x01, 0x59, + 0x01, 0x60, 0x01, 0x67, 0x01, 0x6e, 0x01, 0x75, 0x01, 0x7c, 0x01, 0x83, + 0x01, 0x8b, 0x01, 0x92, 0x01, 0x9a, 0x01, 0xa1, 0x01, 0xa9, 0x01, 0xb1, + 0x01, 0xb9, 0x01, 0xc1, 0x01, 0xc9, 0x01, 0xd1, 0x01, 0xd9, 0x01, 0xe1, + 0x01, 0xe9, 0x01, 0xf2, 0x01, 0xfa, 0x02, 0x03, 0x02, 0x0c, 0x02, 0x14, + 0x02, 0x1d, 0x02, 0x26, 0x02, 0x2f, 0x02, 0x38, 0x02, 0x41, 0x02, 0x4b, + 0x02, 0x54, 0x02, 0x5d, 0x02, 0x67, 0x02, 0x71, 0x02, 0x7a, 0x02, 0x84, + 0x02, 0x8e, 0x02, 0x98, 0x02, 0xa2, 0x02, 0xac, 0x02, 0xb6, 0x02, 0xc1, + 0x02, 0xcb, 0x02, 0xd5, 0x02, 0xe0, 0x02, 0xeb, 0x02, 0xf5, 0x03, 0x00, + 0x03, 0x0b, 0x03, 0x16, 0x03, 0x21, 0x03, 0x2d, 0x03, 0x38, 0x03, 0x43, + 0x03, 0x4f, 0x03, 0x5a, 0x03, 0x66, 0x03, 0x72, 0x03, 0x7e, 0x03, 0x8a, + 0x03, 0x96, 0x03, 0xa2, 0x03, 0xae, 0x03, 0xba, 0x03, 0xc7, 0x03, 0xd3, + 0x03, 0xe0, 0x03, 0xec, 0x03, 0xf9, 0x04, 0x06, 0x04, 0x13, 0x04, 0x20, + 0x04, 0x2d, 0x04, 0x3b, 0x04, 0x48, 0x04, 0x55, 0x04, 0x63, 0x04, 0x71, + 0x04, 0x7e, 0x04, 0x8c, 0x04, 0x9a, 0x04, 0xa8, 0x04, 0xb6, 0x04, 0xc4, + 0x04, 0xd3, 0x04, 0xe1, 0x04, 0xf0, 0x04, 0xfe, 0x05, 0x0d, 0x05, 0x1c, + 0x05, 0x2b, 0x05, 0x3a, 0x05, 0x49, 0x05, 0x58, 0x05, 0x67, 0x05, 0x77, + 0x05, 0x86, 0x05, 0x96, 0x05, 0xa6, 0x05, 0xb5, 0x05, 0xc5, 0x05, 0xd5, + 0x05, 0xe5, 0x05, 0xf6, 0x06, 0x06, 0x06, 0x16, 0x06, 0x27, 0x06, 0x37, + 0x06, 0x48, 0x06, 0x59, 0x06, 0x6a, 0x06, 0x7b, 0x06, 0x8c, 0x06, 0x9d, + 0x06, 0xaf, 0x06, 0xc0, 0x06, 0xd1, 0x06, 0xe3, 0x06, 0xf5, 0x07, 0x07, + 0x07, 0x19, 0x07, 0x2b, 0x07, 0x3d, 0x07, 0x4f, 0x07, 0x61, 0x07, 0x74, + 0x07, 0x86, 0x07, 0x99, 0x07, 0xac, 0x07, 0xbf, 0x07, 0xd2, 0x07, 0xe5, + 0x07, 0xf8, 0x08, 0x0b, 0x08, 0x1f, 0x08, 0x32, 0x08, 0x46, 0x08, 0x5a, + 0x08, 0x6e, 0x08, 0x82, 0x08, 0x96, 0x08, 0xaa, 0x08, 0xbe, 0x08, 0xd2, + 0x08, 0xe7, 0x08, 0xfb, 0x09, 0x10, 0x09, 0x25, 0x09, 0x3a, 0x09, 0x4f, + 0x09, 0x64, 0x09, 0x79, 0x09, 0x8f, 0x09, 0xa4, 0x09, 0xba, 0x09, 0xcf, + 0x09, 0xe5, 0x09, 0xfb, 0x0a, 0x11, 0x0a, 0x27, 0x0a, 0x3d, 0x0a, 0x54, + 0x0a, 0x6a, 0x0a, 0x81, 0x0a, 0x98, 0x0a, 0xae, 0x0a, 0xc5, 0x0a, 0xdc, + 0x0a, 0xf3, 0x0b, 0x0b, 0x0b, 0x22, 0x0b, 0x39, 0x0b, 0x51, 0x0b, 0x69, + 0x0b, 0x80, 0x0b, 0x98, 0x0b, 0xb0, 0x0b, 0xc8, 0x0b, 0xe1, 0x0b, 0xf9, + 0x0c, 0x12, 0x0c, 0x2a, 0x0c, 0x43, 0x0c, 0x5c, 0x0c, 0x75, 0x0c, 0x8e, + 0x0c, 0xa7, 0x0c, 0xc0, 0x0c, 0xd9, 0x0c, 0xf3, 0x0d, 0x0d, 0x0d, 0x26, + 0x0d, 0x40, 0x0d, 0x5a, 0x0d, 0x74, 0x0d, 0x8e, 0x0d, 0xa9, 0x0d, 0xc3, + 0x0d, 0xde, 0x0d, 0xf8, 0x0e, 0x13, 0x0e, 0x2e, 0x0e, 0x49, 0x0e, 0x64, + 0x0e, 0x7f, 0x0e, 0x9b, 0x0e, 0xb6, 0x0e, 0xd2, 0x0e, 0xee, 0x0f, 0x09, + 0x0f, 0x25, 0x0f, 0x41, 0x0f, 0x5e, 0x0f, 0x7a, 0x0f, 0x96, 0x0f, 0xb3, + 0x0f, 0xcf, 0x0f, 0xec, 0x10, 0x09, 0x10, 0x26, 0x10, 0x43, 0x10, 0x61, + 0x10, 0x7e, 0x10, 0x9b, 0x10, 0xb9, 0x10, 0xd7, 0x10, 0xf5, 0x11, 0x13, + 0x11, 0x31, 0x11, 0x4f, 0x11, 0x6d, 0x11, 0x8c, 0x11, 0xaa, 0x11, 0xc9, + 0x11, 0xe8, 0x12, 0x07, 0x12, 0x26, 0x12, 0x45, 0x12, 0x64, 0x12, 0x84, + 0x12, 0xa3, 0x12, 0xc3, 0x12, 0xe3, 0x13, 0x03, 0x13, 0x23, 0x13, 0x43, + 0x13, 0x63, 0x13, 0x83, 0x13, 0xa4, 0x13, 0xc5, 0x13, 0xe5, 0x14, 0x06, + 0x14, 0x27, 0x14, 0x49, 0x14, 0x6a, 0x14, 0x8b, 0x14, 0xad, 0x14, 0xce, + 0x14, 0xf0, 0x15, 0x12, 0x15, 0x34, 0x15, 0x56, 0x15, 0x78, 0x15, 0x9b, + 0x15, 0xbd, 0x15, 0xe0, 0x16, 0x03, 0x16, 0x26, 0x16, 0x49, 0x16, 0x6c, + 0x16, 0x8f, 0x16, 0xb2, 0x16, 0xd6, 0x16, 0xfa, 0x17, 0x1d, 0x17, 0x41, + 0x17, 0x65, 0x17, 0x89, 0x17, 0xae, 0x17, 0xd2, 0x17, 0xf7, 0x18, 0x1b, + 0x18, 0x40, 0x18, 0x65, 0x18, 0x8a, 0x18, 0xaf, 0x18, 0xd5, 0x18, 0xfa, + 0x19, 0x20, 0x19, 0x45, 0x19, 0x6b, 0x19, 0x91, 0x19, 0xb7, 0x19, 0xdd, + 0x1a, 0x04, 0x1a, 0x2a, 0x1a, 0x51, 0x1a, 0x77, 0x1a, 0x9e, 0x1a, 0xc5, + 0x1a, 0xec, 0x1b, 0x14, 0x1b, 0x3b, 0x1b, 0x63, 0x1b, 0x8a, 0x1b, 0xb2, + 0x1b, 0xda, 0x1c, 0x02, 0x1c, 0x2a, 0x1c, 0x52, 0x1c, 0x7b, 0x1c, 0xa3, + 0x1c, 0xcc, 0x1c, 0xf5, 0x1d, 0x1e, 0x1d, 0x47, 0x1d, 0x70, 0x1d, 0x99, + 0x1d, 0xc3, 0x1d, 0xec, 0x1e, 0x16, 0x1e, 0x40, 0x1e, 0x6a, 0x1e, 0x94, + 0x1e, 0xbe, 0x1e, 0xe9, 0x1f, 0x13, 0x1f, 0x3e, 0x1f, 0x69, 0x1f, 0x94, + 0x1f, 0xbf, 0x1f, 0xea, 0x20, 0x15, 0x20, 0x41, 0x20, 0x6c, 0x20, 0x98, + 0x20, 0xc4, 0x20, 0xf0, 0x21, 0x1c, 0x21, 0x48, 0x21, 0x75, 0x21, 0xa1, + 0x21, 0xce, 0x21, 0xfb, 0x22, 0x27, 0x22, 0x55, 0x22, 0x82, 0x22, 0xaf, + 0x22, 0xdd, 0x23, 0x0a, 0x23, 0x38, 0x23, 0x66, 0x23, 0x94, 0x23, 0xc2, + 0x23, 0xf0, 0x24, 0x1f, 0x24, 0x4d, 0x24, 0x7c, 0x24, 0xab, 0x24, 0xda, + 0x25, 0x09, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, 0x25, 0xc7, 0x25, 0xf7, + 0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xb7, 0x26, 0xe8, 0x27, 0x18, + 0x27, 0x49, 0x27, 0x7a, 0x27, 0xab, 0x27, 0xdc, 0x28, 0x0d, 0x28, 0x3f, + 0x28, 0x71, 0x28, 0xa2, 0x28, 0xd4, 0x29, 0x06, 0x29, 0x38, 0x29, 0x6b, + 0x29, 0x9d, 0x29, 0xd0, 0x2a, 0x02, 0x2a, 0x35, 0x2a, 0x68, 0x2a, 0x9b, + 0x2a, 0xcf, 0x2b, 0x02, 0x2b, 0x36, 0x2b, 0x69, 0x2b, 0x9d, 0x2b, 0xd1, + 0x2c, 0x05, 0x2c, 0x39, 0x2c, 0x6e, 0x2c, 0xa2, 0x2c, 0xd7, 0x2d, 0x0c, + 0x2d, 0x41, 0x2d, 0x76, 0x2d, 0xab, 0x2d, 0xe1, 0x2e, 0x16, 0x2e, 0x4c, + 0x2e, 0x82, 0x2e, 0xb7, 0x2e, 0xee, 0x2f, 0x24, 0x2f, 0x5a, 0x2f, 0x91, + 0x2f, 0xc7, 0x2f, 0xfe, 0x30, 0x35, 0x30, 0x6c, 0x30, 0xa4, 0x30, 0xdb, + 0x31, 0x12, 0x31, 0x4a, 0x31, 0x82, 0x31, 0xba, 0x31, 0xf2, 0x32, 0x2a, + 0x32, 0x63, 0x32, 0x9b, 0x32, 0xd4, 0x33, 0x0d, 0x33, 0x46, 0x33, 0x7f, + 0x33, 0xb8, 0x33, 0xf1, 0x34, 0x2b, 0x34, 0x65, 0x34, 0x9e, 0x34, 0xd8, + 0x35, 0x13, 0x35, 0x4d, 0x35, 0x87, 0x35, 0xc2, 0x35, 0xfd, 0x36, 0x37, + 0x36, 0x72, 0x36, 0xae, 0x36, 0xe9, 0x37, 0x24, 0x37, 0x60, 0x37, 0x9c, + 0x37, 0xd7, 0x38, 0x14, 0x38, 0x50, 0x38, 0x8c, 0x38, 0xc8, 0x39, 0x05, + 0x39, 0x42, 0x39, 0x7f, 0x39, 0xbc, 0x39, 0xf9, 0x3a, 0x36, 0x3a, 0x74, + 0x3a, 0xb2, 0x3a, 0xef, 0x3b, 0x2d, 0x3b, 0x6b, 0x3b, 0xaa, 0x3b, 0xe8, + 0x3c, 0x27, 0x3c, 0x65, 0x3c, 0xa4, 0x3c, 0xe3, 0x3d, 0x22, 0x3d, 0x61, + 0x3d, 0xa1, 0x3d, 0xe0, 0x3e, 0x20, 0x3e, 0x60, 0x3e, 0xa0, 0x3e, 0xe0, + 0x3f, 0x21, 0x3f, 0x61, 0x3f, 0xa2, 0x3f, 0xe2, 0x40, 0x23, 0x40, 0x64, + 0x40, 0xa6, 0x40, 0xe7, 0x41, 0x29, 0x41, 0x6a, 0x41, 0xac, 0x41, 0xee, + 0x42, 0x30, 0x42, 0x72, 0x42, 0xb5, 0x42, 0xf7, 0x43, 0x3a, 0x43, 0x7d, + 0x43, 0xc0, 0x44, 0x03, 0x44, 0x47, 0x44, 0x8a, 0x44, 0xce, 0x45, 0x12, + 0x45, 0x55, 0x45, 0x9a, 0x45, 0xde, 0x46, 0x22, 0x46, 0x67, 0x46, 0xab, + 0x46, 0xf0, 0x47, 0x35, 0x47, 0x7b, 0x47, 0xc0, 0x48, 0x05, 0x48, 0x4b, + 0x48, 0x91, 0x48, 0xd7, 0x49, 0x1d, 0x49, 0x63, 0x49, 0xa9, 0x49, 0xf0, + 0x4a, 0x37, 0x4a, 0x7d, 0x4a, 0xc4, 0x4b, 0x0c, 0x4b, 0x53, 0x4b, 0x9a, + 0x4b, 0xe2, 0x4c, 0x2a, 0x4c, 0x72, 0x4c, 0xba, 0x4d, 0x02, 0x4d, 0x4a, + 0x4d, 0x93, 0x4d, 0xdc, 0x4e, 0x25, 0x4e, 0x6e, 0x4e, 0xb7, 0x4f, 0x00, + 0x4f, 0x49, 0x4f, 0x93, 0x4f, 0xdd, 0x50, 0x27, 0x50, 0x71, 0x50, 0xbb, + 0x51, 0x06, 0x51, 0x50, 0x51, 0x9b, 0x51, 0xe6, 0x52, 0x31, 0x52, 0x7c, + 0x52, 0xc7, 0x53, 0x13, 0x53, 0x5f, 0x53, 0xaa, 0x53, 0xf6, 0x54, 0x42, + 0x54, 0x8f, 0x54, 0xdb, 0x55, 0x28, 0x55, 0x75, 0x55, 0xc2, 0x56, 0x0f, + 0x56, 0x5c, 0x56, 0xa9, 0x56, 0xf7, 0x57, 0x44, 0x57, 0x92, 0x57, 0xe0, + 0x58, 0x2f, 0x58, 0x7d, 0x58, 0xcb, 0x59, 0x1a, 0x59, 0x69, 0x59, 0xb8, + 0x5a, 0x07, 0x5a, 0x56, 0x5a, 0xa6, 0x5a, 0xf5, 0x5b, 0x45, 0x5b, 0x95, + 0x5b, 0xe5, 0x5c, 0x35, 0x5c, 0x86, 0x5c, 0xd6, 0x5d, 0x27, 0x5d, 0x78, + 0x5d, 0xc9, 0x5e, 0x1a, 0x5e, 0x6c, 0x5e, 0xbd, 0x5f, 0x0f, 0x5f, 0x61, + 0x5f, 0xb3, 0x60, 0x05, 0x60, 0x57, 0x60, 0xaa, 0x60, 0xfc, 0x61, 0x4f, + 0x61, 0xa2, 0x61, 0xf5, 0x62, 0x49, 0x62, 0x9c, 0x62, 0xf0, 0x63, 0x43, + 0x63, 0x97, 0x63, 0xeb, 0x64, 0x40, 0x64, 0x94, 0x64, 0xe9, 0x65, 0x3d, + 0x65, 0x92, 0x65, 0xe7, 0x66, 0x3d, 0x66, 0x92, 0x66, 0xe8, 0x67, 0x3d, + 0x67, 0x93, 0x67, 0xe9, 0x68, 0x3f, 0x68, 0x96, 0x68, 0xec, 0x69, 0x43, + 0x69, 0x9a, 0x69, 0xf1, 0x6a, 0x48, 0x6a, 0x9f, 0x6a, 0xf7, 0x6b, 0x4f, + 0x6b, 0xa7, 0x6b, 0xff, 0x6c, 0x57, 0x6c, 0xaf, 0x6d, 0x08, 0x6d, 0x60, + 0x6d, 0xb9, 0x6e, 0x12, 0x6e, 0x6b, 0x6e, 0xc4, 0x6f, 0x1e, 0x6f, 0x78, + 0x6f, 0xd1, 0x70, 0x2b, 0x70, 0x86, 0x70, 0xe0, 0x71, 0x3a, 0x71, 0x95, + 0x71, 0xf0, 0x72, 0x4b, 0x72, 0xa6, 0x73, 0x01, 0x73, 0x5d, 0x73, 0xb8, + 0x74, 0x14, 0x74, 0x70, 0x74, 0xcc, 0x75, 0x28, 0x75, 0x85, 0x75, 0xe1, + 0x76, 0x3e, 0x76, 0x9b, 0x76, 0xf8, 0x77, 0x56, 0x77, 0xb3, 0x78, 0x11, + 0x78, 0x6e, 0x78, 0xcc, 0x79, 0x2a, 0x79, 0x89, 0x79, 0xe7, 0x7a, 0x46, + 0x7a, 0xa5, 0x7b, 0x04, 0x7b, 0x63, 0x7b, 0xc2, 0x7c, 0x21, 0x7c, 0x81, + 0x7c, 0xe1, 0x7d, 0x41, 0x7d, 0xa1, 0x7e, 0x01, 0x7e, 0x62, 0x7e, 0xc2, + 0x7f, 0x23, 0x7f, 0x84, 0x7f, 0xe5, 0x80, 0x47, 0x80, 0xa8, 0x81, 0x0a, + 0x81, 0x6b, 0x81, 0xcd, 0x82, 0x30, 0x82, 0x92, 0x82, 0xf4, 0x83, 0x57, + 0x83, 0xba, 0x84, 0x1d, 0x84, 0x80, 0x84, 0xe3, 0x85, 0x47, 0x85, 0xab, + 0x86, 0x0e, 0x86, 0x72, 0x86, 0xd7, 0x87, 0x3b, 0x87, 0x9f, 0x88, 0x04, + 0x88, 0x69, 0x88, 0xce, 0x89, 0x33, 0x89, 0x99, 0x89, 0xfe, 0x8a, 0x64, + 0x8a, 0xca, 0x8b, 0x30, 0x8b, 0x96, 0x8b, 0xfc, 0x8c, 0x63, 0x8c, 0xca, + 0x8d, 0x31, 0x8d, 0x98, 0x8d, 0xff, 0x8e, 0x66, 0x8e, 0xce, 0x8f, 0x36, + 0x8f, 0x9e, 0x90, 0x06, 0x90, 0x6e, 0x90, 0xd6, 0x91, 0x3f, 0x91, 0xa8, + 0x92, 0x11, 0x92, 0x7a, 0x92, 0xe3, 0x93, 0x4d, 0x93, 0xb6, 0x94, 0x20, + 0x94, 0x8a, 0x94, 0xf4, 0x95, 0x5f, 0x95, 0xc9, 0x96, 0x34, 0x96, 0x9f, + 0x97, 0x0a, 0x97, 0x75, 0x97, 0xe0, 0x98, 0x4c, 0x98, 0xb8, 0x99, 0x24, + 0x99, 0x90, 0x99, 0xfc, 0x9a, 0x68, 0x9a, 0xd5, 0x9b, 0x42, 0x9b, 0xaf, + 0x9c, 0x1c, 0x9c, 0x89, 0x9c, 0xf7, 0x9d, 0x64, 0x9d, 0xd2, 0x9e, 0x40, + 0x9e, 0xae, 0x9f, 0x1d, 0x9f, 0x8b, 0x9f, 0xfa, 0xa0, 0x69, 0xa0, 0xd8, + 0xa1, 0x47, 0xa1, 0xb6, 0xa2, 0x26, 0xa2, 0x96, 0xa3, 0x06, 0xa3, 0x76, + 0xa3, 0xe6, 0xa4, 0x56, 0xa4, 0xc7, 0xa5, 0x38, 0xa5, 0xa9, 0xa6, 0x1a, + 0xa6, 0x8b, 0xa6, 0xfd, 0xa7, 0x6e, 0xa7, 0xe0, 0xa8, 0x52, 0xa8, 0xc4, + 0xa9, 0x37, 0xa9, 0xa9, 0xaa, 0x1c, 0xaa, 0x8f, 0xab, 0x02, 0xab, 0x75, + 0xab, 0xe9, 0xac, 0x5c, 0xac, 0xd0, 0xad, 0x44, 0xad, 0xb8, 0xae, 0x2d, + 0xae, 0xa1, 0xaf, 0x16, 0xaf, 0x8b, 0xb0, 0x00, 0xb0, 0x75, 0xb0, 0xea, + 0xb1, 0x60, 0xb1, 0xd6, 0xb2, 0x4b, 0xb2, 0xc2, 0xb3, 0x38, 0xb3, 0xae, + 0xb4, 0x25, 0xb4, 0x9c, 0xb5, 0x13, 0xb5, 0x8a, 0xb6, 0x01, 0xb6, 0x79, + 0xb6, 0xf0, 0xb7, 0x68, 0xb7, 0xe0, 0xb8, 0x59, 0xb8, 0xd1, 0xb9, 0x4a, + 0xb9, 0xc2, 0xba, 0x3b, 0xba, 0xb5, 0xbb, 0x2e, 0xbb, 0xa7, 0xbc, 0x21, + 0xbc, 0x9b, 0xbd, 0x15, 0xbd, 0x8f, 0xbe, 0x0a, 0xbe, 0x84, 0xbe, 0xff, + 0xbf, 0x7a, 0xbf, 0xf5, 0xc0, 0x70, 0xc0, 0xec, 0xc1, 0x67, 0xc1, 0xe3, + 0xc2, 0x5f, 0xc2, 0xdb, 0xc3, 0x58, 0xc3, 0xd4, 0xc4, 0x51, 0xc4, 0xce, + 0xc5, 0x4b, 0xc5, 0xc8, 0xc6, 0x46, 0xc6, 0xc3, 0xc7, 0x41, 0xc7, 0xbf, + 0xc8, 0x3d, 0xc8, 0xbc, 0xc9, 0x3a, 0xc9, 0xb9, 0xca, 0x38, 0xca, 0xb7, + 0xcb, 0x36, 0xcb, 0xb6, 0xcc, 0x35, 0xcc, 0xb5, 0xcd, 0x35, 0xcd, 0xb5, + 0xce, 0x36, 0xce, 0xb6, 0xcf, 0x37, 0xcf, 0xb8, 0xd0, 0x39, 0xd0, 0xba, + 0xd1, 0x3c, 0xd1, 0xbe, 0xd2, 0x3f, 0xd2, 0xc1, 0xd3, 0x44, 0xd3, 0xc6, + 0xd4, 0x49, 0xd4, 0xcb, 0xd5, 0x4e, 0xd5, 0xd1, 0xd6, 0x55, 0xd6, 0xd8, + 0xd7, 0x5c, 0xd7, 0xe0, 0xd8, 0x64, 0xd8, 0xe8, 0xd9, 0x6c, 0xd9, 0xf1, + 0xda, 0x76, 0xda, 0xfb, 0xdb, 0x80, 0xdc, 0x05, 0xdc, 0x8a, 0xdd, 0x10, + 0xdd, 0x96, 0xde, 0x1c, 0xde, 0xa2, 0xdf, 0x29, 0xdf, 0xaf, 0xe0, 0x36, + 0xe0, 0xbd, 0xe1, 0x44, 0xe1, 0xcc, 0xe2, 0x53, 0xe2, 0xdb, 0xe3, 0x63, + 0xe3, 0xeb, 0xe4, 0x73, 0xe4, 0xfc, 0xe5, 0x84, 0xe6, 0x0d, 0xe6, 0x96, + 0xe7, 0x1f, 0xe7, 0xa9, 0xe8, 0x32, 0xe8, 0xbc, 0xe9, 0x46, 0xe9, 0xd0, + 0xea, 0x5b, 0xea, 0xe5, 0xeb, 0x70, 0xeb, 0xfb, 0xec, 0x86, 0xed, 0x11, + 0xed, 0x9c, 0xee, 0x28, 0xee, 0xb4, 0xef, 0x40, 0xef, 0xcc, 0xf0, 0x58, + 0xf0, 0xe5, 0xf1, 0x72, 0xf1, 0xff, 0xf2, 0x8c, 0xf3, 0x19, 0xf3, 0xa7, + 0xf4, 0x34, 0xf4, 0xc2, 0xf5, 0x50, 0xf5, 0xde, 0xf6, 0x6d, 0xf6, 0xfb, + 0xf7, 0x8a, 0xf8, 0x19, 0xf8, 0xa8, 0xf9, 0x38, 0xf9, 0xc7, 0xfa, 0x57, + 0xfa, 0xe7, 0xfb, 0x77, 0xfc, 0x07, 0xfc, 0x98, 0xfd, 0x29, 0xfd, 0xba, + 0xfe, 0x4b, 0xfe, 0xdc, 0xff, 0x6d, 0xff, 0xff, 0x58, 0x59, 0x5a, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x51, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x16, 0xcc, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x41, 0x70, + 0x70, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x2c, 0x20, 0x32, 0x30, + 0x30, 0x38, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x20, + 0x47, 0x72, 0x61, 0x79, 0x20, 0x47, 0x61, 0x6d, 0x6d, 0x61, 0x20, 0x32, + 0x2e, 0x32, 0x20, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x6c, 0x75, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0c, + 0x65, 0x6e, 0x55, 0x53, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xdc, + 0x65, 0x73, 0x45, 0x53, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x01, 0x18, + 0x64, 0x61, 0x44, 0x4b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x02, 0x2a, + 0x64, 0x65, 0x44, 0x45, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x01, 0xde, + 0x66, 0x69, 0x46, 0x49, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x02, 0xa0, + 0x66, 0x72, 0x46, 0x55, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x02, 0x62, + 0x69, 0x74, 0x49, 0x54, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x03, 0x70, + 0x6e, 0x6c, 0x4e, 0x4c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x64, + 0x6e, 0x62, 0x4e, 0x4f, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x03, 0xc4, + 0x70, 0x74, 0x42, 0x52, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x03, 0x26, + 0x73, 0x76, 0x53, 0x45, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x02, 0x2a, + 0x6a, 0x61, 0x4a, 0x50, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x03, 0xfe, + 0x6b, 0x6f, 0x4b, 0x52, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x04, 0x24, + 0x7a, 0x68, 0x54, 0x57, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x04, 0x46, + 0x7a, 0x68, 0x43, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x04, 0x64, + 0x72, 0x75, 0x52, 0x55, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x01, 0xa4, + 0x70, 0x6c, 0x50, 0x4c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0xe6, + 0x00, 0x47, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, + 0x00, 0x63, 0x00, 0x20, 0x00, 0x47, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, + 0x00, 0x20, 0x00, 0x47, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, + 0x00, 0x20, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x20, 0x00, 0x50, + 0x00, 0x72, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, + 0x00, 0x50, 0x00, 0x65, 0x00, 0x72, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, + 0x00, 0x20, 0x00, 0x67, 0x00, 0x65, 0x00, 0x6e, 0x00, 0xe9, 0x00, 0x72, + 0x00, 0x69, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x64, 0x00, 0x65, + 0x00, 0x20, 0x00, 0x67, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, + 0x00, 0x20, 0x00, 0x64, 0x00, 0x65, 0x00, 0x20, 0x00, 0x67, 0x00, 0x72, + 0x00, 0x69, 0x00, 0x73, 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32, + 0x00, 0x2c, 0x00, 0x32, 0x00, 0x41, 0x00, 0x6c, 0x00, 0x67, 0x00, 0x65, + 0x00, 0x6d, 0x00, 0x65, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x67, + 0x00, 0x72, 0x00, 0x69, 0x00, 0x6a, 0x00, 0x73, 0x00, 0x20, 0x00, 0x67, + 0x00, 0x61, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x20, 0x00, 0x32, + 0x00, 0x2c, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, + 0x00, 0x66, 0x00, 0x69, 0x00, 0x65, 0x00, 0x6c, 0x04, 0x1e, 0x04, 0x31, + 0x04, 0x49, 0x04, 0x30, 0x04, 0x4f, 0x00, 0x20, 0x04, 0x41, 0x04, 0x35, + 0x04, 0x40, 0x04, 0x30, 0x04, 0x4f, 0x00, 0x20, 0x04, 0x33, 0x04, 0x30, + 0x04, 0x3c, 0x04, 0x3c, 0x04, 0x30, 0x00, 0x20, 0x00, 0x32, 0x00, 0x2c, + 0x00, 0x32, 0x00, 0x2d, 0x04, 0x3f, 0x04, 0x40, 0x04, 0x3e, 0x04, 0x44, + 0x04, 0x38, 0x04, 0x3b, 0x04, 0x4c, 0x00, 0x41, 0x00, 0x6c, 0x00, 0x6c, + 0x00, 0x67, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x69, 0x00, 0x6e, + 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x47, 0x00, 0x72, 0x00, 0x61, + 0x00, 0x75, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x66, 0x00, 0x65, + 0x00, 0x6e, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x69, + 0x00, 0x6c, 0x00, 0x20, 0x00, 0x47, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x6d, + 0x00, 0x61, 0x00, 0x20, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x47, + 0x00, 0x65, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x73, + 0x00, 0x6b, 0x00, 0x20, 0x00, 0x67, 0x00, 0x72, 0x00, 0xe5, 0x00, 0x20, + 0x00, 0x32, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x20, 0x00, 0x67, 0x00, 0x61, + 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, + 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6f, + 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x67, 0x00, 0xe9, + 0x00, 0x6e, 0x00, 0xe9, 0x00, 0x72, 0x00, 0x69, 0x00, 0x71, 0x00, 0x75, + 0x00, 0x65, 0x00, 0x20, 0x00, 0x67, 0x00, 0x72, 0x00, 0x69, 0x00, 0x73, + 0x00, 0x20, 0x00, 0x67, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, + 0x00, 0x20, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x59, 0x00, 0x6c, + 0x00, 0x65, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x20, + 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x61, + 0x00, 0x6e, 0x00, 0x20, 0x00, 0x67, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x6d, + 0x00, 0x61, 0x00, 0x20, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x20, + 0x00, 0x2d, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x69, + 0x00, 0x69, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x4f, 0x00, 0x67, 0x00, 0xf3, + 0x00, 0x6c, 0x00, 0x6e, 0x00, 0x79, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, + 0x00, 0x6f, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x73, + 0x00, 0x7a, 0x00, 0x61, 0x00, 0x72, 0x00, 0x6f, 0x01, 0x5b, 0x00, 0x63, + 0x00, 0x69, 0x00, 0x20, 0x00, 0x67, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x6d, + 0x00, 0x61, 0x00, 0x20, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x50, + 0x00, 0x65, 0x00, 0x72, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x20, + 0x00, 0x47, 0x00, 0x65, 0x00, 0x6e, 0x00, 0xe9, 0x00, 0x72, 0x00, 0x69, + 0x00, 0x63, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x64, 0x00, 0x61, 0x00, 0x20, + 0x00, 0x47, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x20, 0x00, 0x64, + 0x00, 0x65, 0x00, 0x20, 0x00, 0x43, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x7a, + 0x00, 0x61, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x32, + 0x00, 0x50, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, + 0x00, 0x6f, 0x00, 0x20, 0x00, 0x67, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x65, + 0x00, 0x72, 0x00, 0x69, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x64, + 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x20, 0x00, 0x67, + 0x00, 0x61, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x20, 0x00, 0x64, + 0x00, 0x65, 0x00, 0x69, 0x00, 0x20, 0x00, 0x67, 0x00, 0x72, 0x00, 0x69, + 0x00, 0x67, 0x00, 0x69, 0x00, 0x20, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x32, + 0x00, 0x47, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, + 0x00, 0x73, 0x00, 0x6b, 0x00, 0x20, 0x00, 0x67, 0x00, 0x72, 0x00, 0xe5, + 0x00, 0x20, 0x00, 0x67, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, + 0x00, 0x20, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x70, + 0x00, 0x72, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, 0x4e, 0x00, + 0x82, 0x2c, 0x30, 0xb0, 0x30, 0xec, 0x30, 0xa4, 0x30, 0xac, 0x30, 0xf3, + 0x30, 0xde, 0x00, 0x20, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x20, + 0x30, 0xd7, 0x30, 0xed, 0x30, 0xd5, 0x30, 0xa1, 0x30, 0xa4, 0x30, 0xeb, + 0xc7, 0x7c, 0xbc, 0x18, 0x00, 0x20, 0xd6, 0x8c, 0xc0, 0xc9, 0x00, 0x20, + 0xac, 0x10, 0xb9, 0xc8, 0x00, 0x20, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x32, + 0x00, 0x20, 0xd5, 0x04, 0xb8, 0x5c, 0xd3, 0x0c, 0xc7, 0x7c, 0x90, 0x1a, + 0x75, 0x28, 0x70, 0x70, 0x96, 0x8e, 0x51, 0x49, 0x5e, 0xa6, 0x00, 0x20, + 0x00, 0x32, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x20, 0x82, 0x72, 0x5f, 0x69, + 0x63, 0xcf, 0x8f, 0xf0, 0x90, 0x1a, 0x75, 0x28, 0x70, 0x70, 0x5e, 0xa6, + 0x7c, 0xfb, 0x65, 0x70, 0x00, 0x20, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x32, + 0x00, 0x20, 0x63, 0xcf, 0x8f, 0xf0, 0x65, 0x87, 0x4e, 0xf6, 0x00, 0x00, + } + + temporaryDirectory = temporaryDirectoryOrPanic() + SRGBV2MicroICCProfilePath = filepath.Join(temporaryDirectory, "srgb_v2_micro.icc") + SGrayV2MicroICCProfilePath = filepath.Join(temporaryDirectory, "sgray_v2_micro.icc") + SRGBIEC6196621ICCProfilePath = filepath.Join(temporaryDirectory, "srgb_iec61966_2_1.icc") + GenericGrayGamma22ICCProfilePath = filepath.Join(temporaryDirectory, "generic_gray_gamma_2_2.icc") +) + +func initializeICCProfiles() { + storeIccProfile(SRGBV2MicroICCProfilePath, sRGBV2MicroICCProfile) + storeIccProfile(SGrayV2MicroICCProfilePath, sGrayV2MicroICCProfile) + storeIccProfile(SRGBIEC6196621ICCProfilePath, sRGBIEC6196621ICCProfile) + storeIccProfile(GenericGrayGamma22ICCProfilePath, genericGrayGamma22ICCProfile) +} + +func storeIccProfile(path string, data []byte) { + err := os.WriteFile(path, data, 0600) + if err != nil { + panic(fmt.Sprintf("Couldn't store temporary file for ICC profile in '%v': %v", path, err.Error())) + } +} + +func temporaryDirectoryOrPanic() string { + temporaryDirectory, err := os.MkdirTemp("", "govips-") + if err != nil { + panic(fmt.Sprintf("Couldn't create temporary directory: %v", err.Error())) + } + + return temporaryDirectory +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/image.c b/vendor/github.com/davidbyttow/govips/v2/vips/image.c new file mode 100644 index 000000000..6352f0d18 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/image.c @@ -0,0 +1,9 @@ +#include "image.h" + +int has_alpha_channel(VipsImage *image) { return vips_image_hasalpha(image); } + +void clear_image(VipsImage **image) { + // Reference-counting in libvips: https://www.libvips.org/API/current/using-from-c.html#using-C-ref + // https://docs.gtk.org/gobject/method.Object.unref.html + if (G_IS_OBJECT(*image)) g_object_unref(*image); +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/image.go b/vendor/github.com/davidbyttow/govips/v2/vips/image.go new file mode 100644 index 000000000..82d5fac26 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/image.go @@ -0,0 +1,2162 @@ +package vips + +// #include "image.h" +import "C" + +import ( + "bytes" + "errors" + "fmt" + "image" + "io" + "os" + "runtime" + "strconv" + "strings" + "sync" + "unsafe" +) + +const GaussBlurDefaultMinAMpl = 0.2 + +// PreMultiplicationState stores the pre-multiplication band format of the image +type PreMultiplicationState struct { + bandFormat BandFormat +} + +// ImageRef contains a libvips image and manages its lifecycle. +type ImageRef struct { + // NOTE: We keep a reference to this so that the input buffer is + // never garbage collected during processing. Some image loaders use random + // access transcoding and therefore need the original buffer to be in memory. + buf []byte + image *C.VipsImage + format ImageType + originalFormat ImageType + lock sync.Mutex + preMultiplication *PreMultiplicationState + optimizedIccProfile string +} + +// ImageMetadata is a data structure holding the width, height, orientation and other metadata of the picture. +type ImageMetadata struct { + Format ImageType + Width int + Height int + Colorspace Interpretation + Orientation int + Pages int +} + +type Parameter struct { + value interface{} + isSet bool +} + +func (p *Parameter) IsSet() bool { + return p.isSet +} + +func (p *Parameter) set(v interface{}) { + p.value = v + p.isSet = true +} + +type BoolParameter struct { + Parameter +} + +func (p *BoolParameter) Set(v bool) { + p.set(v) +} + +func (p *BoolParameter) Get() bool { + return p.value.(bool) +} + +type IntParameter struct { + Parameter +} + +func (p *IntParameter) Set(v int) { + p.set(v) +} + +func (p *IntParameter) Get() int { + return p.value.(int) +} + +type Float64Parameter struct { + Parameter +} + +func (p *Float64Parameter) Set(v float64) { + p.set(v) +} + +func (p *Float64Parameter) Get() float64 { + return p.value.(float64) +} + +// ImportParams are options for loading an image. Some are type-specific. +// For default loading, use NewImportParams() or specify nil +type ImportParams struct { + AutoRotate BoolParameter + FailOnError BoolParameter + Page IntParameter + NumPages IntParameter + Density IntParameter + + JpegShrinkFactor IntParameter + HeifThumbnail BoolParameter + SvgUnlimited BoolParameter +} + +// NewImportParams creates default ImportParams +func NewImportParams() *ImportParams { + p := &ImportParams{} + p.FailOnError.Set(true) + return p +} + +// OptionString convert import params to option_string +func (i *ImportParams) OptionString() string { + var values []string + if v := i.NumPages; v.IsSet() { + values = append(values, "n="+strconv.Itoa(v.Get())) + } + if v := i.Page; v.IsSet() { + values = append(values, "page="+strconv.Itoa(v.Get())) + } + if v := i.Density; v.IsSet() { + values = append(values, "dpi="+strconv.Itoa(v.Get())) + } + if v := i.FailOnError; v.IsSet() { + values = append(values, "fail="+boolToStr(v.Get())) + } + if v := i.JpegShrinkFactor; v.IsSet() { + values = append(values, "shrink="+strconv.Itoa(v.Get())) + } + if v := i.AutoRotate; v.IsSet() { + values = append(values, "autorotate="+boolToStr(v.Get())) + } + if v := i.SvgUnlimited; v.IsSet() { + values = append(values, "unlimited="+boolToStr(v.Get())) + } + if v := i.HeifThumbnail; v.IsSet() { + values = append(values, "thumbnail="+boolToStr(v.Get())) + } + return strings.Join(values, ",") +} + +func boolToStr(v bool) string { + if v { + return "TRUE" + } + return "FALSE" +} + +// ExportParams are options when exporting an image to file or buffer. +// Deprecated: Use format-specific params +type ExportParams struct { + Format ImageType + Quality int + Compression int + Interlaced bool + Lossless bool + Effort int + StripMetadata bool + OptimizeCoding bool // jpeg param + SubsampleMode SubsampleMode // jpeg param + TrellisQuant bool // jpeg param + OvershootDeringing bool // jpeg param + OptimizeScans bool // jpeg param + QuantTable int // jpeg param + Speed int // avif param +} + +// NewDefaultExportParams creates default values for an export when image type is not JPEG, PNG or WEBP. +// By default, govips creates interlaced, lossy images with a quality of 80/100 and compression of 6/10. +// As these are default values for a wide variety of image formats, their application varies. +// Some formats use the quality parameters, some compression, etc. +// Deprecated: Use format-specific params +func NewDefaultExportParams() *ExportParams { + return &ExportParams{ + Format: ImageTypeUnknown, // defaults to the starting encoder + Quality: 80, + Compression: 6, + Interlaced: true, + Lossless: false, + Effort: 4, + } +} + +// NewDefaultJPEGExportParams creates default values for an export of a JPEG image. +// By default, govips creates interlaced JPEGs with a quality of 80/100. +// Deprecated: Use NewJpegExportParams +func NewDefaultJPEGExportParams() *ExportParams { + return &ExportParams{ + Format: ImageTypeJPEG, + Quality: 80, + Interlaced: true, + } +} + +// NewDefaultPNGExportParams creates default values for an export of a PNG image. +// By default, govips creates non-interlaced PNGs with a compression of 6/10. +// Deprecated: Use NewPngExportParams +func NewDefaultPNGExportParams() *ExportParams { + return &ExportParams{ + Format: ImageTypePNG, + Compression: 6, + Interlaced: false, + } +} + +// NewDefaultWEBPExportParams creates default values for an export of a WEBP image. +// By default, govips creates lossy images with a quality of 75/100. +// Deprecated: Use NewWebpExportParams +func NewDefaultWEBPExportParams() *ExportParams { + return &ExportParams{ + Format: ImageTypeWEBP, + Quality: 75, + Lossless: false, + Effort: 4, + } +} + +// JpegExportParams are options when exporting a JPEG to file or buffer +type JpegExportParams struct { + StripMetadata bool + Quality int + Interlace bool + OptimizeCoding bool + SubsampleMode SubsampleMode + TrellisQuant bool + OvershootDeringing bool + OptimizeScans bool + QuantTable int +} + +// NewJpegExportParams creates default values for an export of a JPEG image. +// By default, govips creates interlaced JPEGs with a quality of 80/100. +func NewJpegExportParams() *JpegExportParams { + return &JpegExportParams{ + Quality: 80, + Interlace: true, + } +} + +// PngExportParams are options when exporting a PNG to file or buffer +type PngExportParams struct { + StripMetadata bool + Compression int + Filter PngFilter + Interlace bool + Quality int + Palette bool + Dither float64 + Bitdepth int + Profile string // TODO: Use this param during save +} + +// NewPngExportParams creates default values for an export of a PNG image. +// By default, govips creates non-interlaced PNGs with a compression of 6/10. +func NewPngExportParams() *PngExportParams { + return &PngExportParams{ + Compression: 6, + Filter: PngFilterNone, + Interlace: false, + Palette: false, + } +} + +// WebpExportParams are options when exporting a WEBP to file or buffer +// see https://www.libvips.org/API/current/VipsForeignSave.html#vips-webpsave +// for details on each parameter +type WebpExportParams struct { + StripMetadata bool + Quality int + Lossless bool + NearLossless bool + ReductionEffort int + IccProfile string + MinSize bool + MinKeyFrames int + MaxKeyFrames int +} + +// NewWebpExportParams creates default values for an export of a WEBP image. +// By default, govips creates lossy images with a quality of 75/100. +func NewWebpExportParams() *WebpExportParams { + return &WebpExportParams{ + Quality: 75, + Lossless: false, + NearLossless: false, + ReductionEffort: 4, + } +} + +// TiffExportParams are options when exporting a TIFF to file or buffer +type TiffExportParams struct { + StripMetadata bool + Quality int + Compression TiffCompression + Predictor TiffPredictor +} + +// NewTiffExportParams creates default values for an export of a TIFF image. +func NewTiffExportParams() *TiffExportParams { + return &TiffExportParams{ + Quality: 80, + Compression: TiffCompressionLzw, + Predictor: TiffPredictorHorizontal, + } +} + +// GifExportParams are options when exporting a GIF to file or buffer +// Please note that if vips version is above 8.12, then `vips_gifsave_buffer` is used, and only `Dither`, `Effort`, `Bitdepth` is used. +// If vips version is below 8.12, then `vips_magicksave_buffer` is used, and only `Bitdepth`, `Quality` is used. +// StripMetadata does nothing to Gif images. +type GifExportParams struct { + StripMetadata bool + Quality int + Dither float64 + Effort int + Bitdepth int +} + +// NewGifExportParams creates default values for an export of a GIF image. +func NewGifExportParams() *GifExportParams { + return &GifExportParams{ + Quality: 75, + Effort: 7, + Bitdepth: 8, + } +} + +// HeifExportParams are options when exporting a HEIF to file or buffer +type HeifExportParams struct { + Quality int + Bitdepth int + Effort int + Lossless bool +} + +// NewHeifExportParams creates default values for an export of a HEIF image. +func NewHeifExportParams() *HeifExportParams { + return &HeifExportParams{ + Quality: 80, + Bitdepth: 8, + Effort: 5, + Lossless: false, + } +} + +// AvifExportParams are options when exporting an AVIF to file or buffer. +type AvifExportParams struct { + StripMetadata bool + Quality int + Bitdepth int + Effort int + Lossless bool + + // DEPRECATED - Use Effort instead. + Speed int +} + +// NewAvifExportParams creates default values for an export of an AVIF image. +func NewAvifExportParams() *AvifExportParams { + return &AvifExportParams{ + Quality: 80, + Bitdepth: 8, + Effort: 5, + Lossless: false, + } +} + +// Jp2kExportParams are options when exporting an JPEG2000 to file or buffer. +type Jp2kExportParams struct { + Quality int + Lossless bool + TileWidth int + TileHeight int + SubsampleMode SubsampleMode +} + +// NewJp2kExportParams creates default values for an export of an JPEG2000 image. +func NewJp2kExportParams() *Jp2kExportParams { + return &Jp2kExportParams{ + Quality: 80, + Lossless: false, + TileWidth: 512, + TileHeight: 512, + } +} + +// JxlExportParams are options when exporting an JXL to file or buffer. +type JxlExportParams struct { + Quality int + Lossless bool + Tier int + Distance float64 + Effort int +} + +// NewJxlExportParams creates default values for an export of an JXL image. +func NewJxlExportParams() *JxlExportParams { + return &JxlExportParams{ + Quality: 75, + Lossless: false, + Effort: 7, + Distance: 1.0, + } +} + +// NewImageFromReader loads an ImageRef from the given reader +func NewImageFromReader(r io.Reader) (*ImageRef, error) { + buf, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + return NewImageFromBuffer(buf) +} + +// NewImageFromFile loads an image from file and creates a new ImageRef +func NewImageFromFile(file string) (*ImageRef, error) { + return LoadImageFromFile(file, nil) +} + +// LoadImageFromFile loads an image from file and creates a new ImageRef +func LoadImageFromFile(file string, params *ImportParams) (*ImageRef, error) { + buf, err := os.ReadFile(file) + if err != nil { + return nil, err + } + + govipsLog("govips", LogLevelDebug, fmt.Sprintf("creating imageRef from file %s", file)) + return LoadImageFromBuffer(buf, params) +} + +// NewImageFromBuffer loads an image buffer and creates a new Image +func NewImageFromBuffer(buf []byte) (*ImageRef, error) { + return LoadImageFromBuffer(buf, nil) +} + +// LoadImageFromBuffer loads an image buffer and creates a new Image +func LoadImageFromBuffer(buf []byte, params *ImportParams) (*ImageRef, error) { + startupIfNeeded() + + if params == nil { + params = NewImportParams() + } + + vipsImage, currentFormat, originalFormat, err := vipsLoadFromBuffer(buf, params) + if err != nil { + return nil, err + } + + ref := newImageRef(vipsImage, currentFormat, originalFormat, buf) + + govipsLog("govips", LogLevelDebug, fmt.Sprintf("created imageRef %p", ref)) + return ref, nil +} + +// NewThumbnailFromFile loads an image from file and creates a new ImageRef with thumbnail crop +func NewThumbnailFromFile(file string, width, height int, crop Interesting) (*ImageRef, error) { + return LoadThumbnailFromFile(file, width, height, crop, SizeBoth, nil) +} + +// NewThumbnailFromBuffer loads an image buffer and creates a new Image with thumbnail crop +func NewThumbnailFromBuffer(buf []byte, width, height int, crop Interesting) (*ImageRef, error) { + return LoadThumbnailFromBuffer(buf, width, height, crop, SizeBoth, nil) +} + +// NewThumbnailWithSizeFromFile loads an image from file and creates a new ImageRef with thumbnail crop and size +func NewThumbnailWithSizeFromFile(file string, width, height int, crop Interesting, size Size) (*ImageRef, error) { + return LoadThumbnailFromFile(file, width, height, crop, size, nil) +} + +// LoadThumbnailFromFile loads an image from file and creates a new ImageRef with thumbnail crop and size +func LoadThumbnailFromFile(file string, width, height int, crop Interesting, size Size, params *ImportParams) (*ImageRef, error) { + startupIfNeeded() + + vipsImage, format, err := vipsThumbnailFromFile(file, width, height, crop, size, params) + if err != nil { + return nil, err + } + + ref := newImageRef(vipsImage, format, format, nil) + + govipsLog("govips", LogLevelDebug, fmt.Sprintf("created imageref %p", ref)) + return ref, nil +} + +// NewThumbnailWithSizeFromBuffer loads an image buffer and creates a new Image with thumbnail crop and size +func NewThumbnailWithSizeFromBuffer(buf []byte, width, height int, crop Interesting, size Size) (*ImageRef, error) { + return LoadThumbnailFromBuffer(buf, width, height, crop, size, nil) +} + +// LoadThumbnailFromBuffer loads an image buffer and creates a new Image with thumbnail crop and size +func LoadThumbnailFromBuffer(buf []byte, width, height int, crop Interesting, size Size, params *ImportParams) (*ImageRef, error) { + startupIfNeeded() + + vipsImage, format, err := vipsThumbnailFromBuffer(buf, width, height, crop, size, params) + if err != nil { + return nil, err + } + + ref := newImageRef(vipsImage, format, format, buf) + + govipsLog("govips", LogLevelDebug, fmt.Sprintf("created imageref %p", ref)) + return ref, nil +} + +// Metadata returns the metadata (ImageMetadata struct) of the associated ImageRef +func (r *ImageRef) Metadata() *ImageMetadata { + return &ImageMetadata{ + Format: r.Format(), + Width: r.Width(), + Height: r.Height(), + Orientation: r.Orientation(), + Colorspace: r.ColorSpace(), + Pages: r.Pages(), + } +} + +// Copy creates a new copy of the given image. +func (r *ImageRef) Copy() (*ImageRef, error) { + out, err := vipsCopyImage(r.image) + if err != nil { + return nil, err + } + + return newImageRef(out, r.format, r.originalFormat, r.buf), nil +} + +// Copy creates a new copy of the given image with the new X and Y resolution (PPI). +func (r *ImageRef) CopyChangingResolution(xres, yres float64) (*ImageRef, error) { + out, err := vipsCopyImageChangingResolution(r.image, xres, yres) + if err != nil { + return nil, err + } + + return newImageRef(out, r.format, r.originalFormat, r.buf), nil +} + +// Copy creates a new copy of the given image with the interpretation. +func (r *ImageRef) CopyChangingInterpretation(interpretation Interpretation) (*ImageRef, error) { + out, err := vipsCopyImageChangingInterpretation(r.image, interpretation) + if err != nil { + return nil, err + } + + return newImageRef(out, r.format, r.originalFormat, r.buf), nil +} + +// XYZ creates a two-band uint32 image where the elements in the first band have the value of their x coordinate +// and elements in the second band have their y coordinate. +func XYZ(width, height int) (*ImageRef, error) { + vipsImage, err := vipsXYZ(width, height) + return newImageRef(vipsImage, ImageTypeUnknown, ImageTypeUnknown, nil), err +} + +// Identity creates an identity lookup table, which will leave an image unchanged when applied with Maplut. +// Each entry in the table has a value equal to its position. +func Identity(ushort bool) (*ImageRef, error) { + img, err := vipsIdentity(ushort) + return newImageRef(img, ImageTypeUnknown, ImageTypeUnknown, nil), err +} + +// Black creates a new black image of the specified size +func Black(width, height int) (*ImageRef, error) { + vipsImage, err := vipsBlack(width, height) + imageRef := &ImageRef{ + image: vipsImage, + } + runtime.SetFinalizer(imageRef, finalizeImage) + return imageRef, err +} + +func newImageRef(vipsImage *C.VipsImage, currentFormat ImageType, originalFormat ImageType, buf []byte) *ImageRef { + imageRef := &ImageRef{ + image: vipsImage, + format: currentFormat, + originalFormat: originalFormat, + buf: buf, + } + runtime.SetFinalizer(imageRef, finalizeImage) + + return imageRef +} + +func finalizeImage(ref *ImageRef) { + govipsLog("govips", LogLevelDebug, fmt.Sprintf("closing image %p", ref)) + ref.Close() +} + +// Close manually closes the image and frees the memory. Calling Close() is optional. +// Images are automatically closed by GC. However, in high volume applications the GC +// can't keep up with the amount of memory, so you might want to manually close the images. +func (r *ImageRef) Close() { + r.lock.Lock() + + if r.image != nil { + clearImage(r.image) + r.image = nil + } + + r.buf = nil + + r.lock.Unlock() +} + +// Format returns the current format of the vips image. +func (r *ImageRef) Format() ImageType { + return r.format +} + +// OriginalFormat returns the original format of the image when loaded. +// In some cases the loaded image is converted on load, for example, a BMP is automatically converted to PNG +// This method returns the format of the original buffer, as opposed to Format() with will return the format of the +// currently held buffer content. +func (r *ImageRef) OriginalFormat() ImageType { + return r.originalFormat +} + +// Width returns the width of this image. +func (r *ImageRef) Width() int { + return int(r.image.Xsize) +} + +// Height returns the height of this image. +func (r *ImageRef) Height() int { + return int(r.image.Ysize) +} + +// Bands returns the number of bands for this image. +func (r *ImageRef) Bands() int { + return int(r.image.Bands) +} + +// HasProfile returns if the image has an ICC profile embedded. +func (r *ImageRef) HasProfile() bool { + return vipsHasICCProfile(r.image) +} + +// HasICCProfile checks whether the image has an ICC profile embedded. Alias to HasProfile +func (r *ImageRef) HasICCProfile() bool { + return r.HasProfile() +} + +// HasIPTC returns a boolean whether the image in question has IPTC data associated with it. +func (r *ImageRef) HasIPTC() bool { + return vipsHasIPTC(r.image) +} + +// HasAlpha returns if the image has an alpha layer. +func (r *ImageRef) HasAlpha() bool { + return vipsHasAlpha(r.image) +} + +// Orientation returns the orientation number as it appears in the EXIF, if present +func (r *ImageRef) Orientation() int { + return vipsGetMetaOrientation(r.image) +} + +// Deprecated: use Orientation() instead +func (r *ImageRef) GetOrientation() int { + return r.Orientation() +} + +// SetOrientation sets the orientation in the EXIF header of the associated image. +func (r *ImageRef) SetOrientation(orientation int) error { + out, err := vipsCopyImage(r.image) + if err != nil { + return err + } + + vipsSetMetaOrientation(out, orientation) + + r.setImage(out) + return nil +} + +// RemoveOrientation removes the EXIF orientation information of the image. +func (r *ImageRef) RemoveOrientation() error { + out, err := vipsCopyImage(r.image) + if err != nil { + return err + } + + vipsRemoveMetaOrientation(out) + + r.setImage(out) + return nil +} + +// ResX returns the X resolution +func (r *ImageRef) ResX() float64 { + return float64(r.image.Xres) +} + +// ResY returns the Y resolution +func (r *ImageRef) ResY() float64 { + return float64(r.image.Yres) +} + +// OffsetX returns the X offset +func (r *ImageRef) OffsetX() int { + return int(r.image.Xoffset) +} + +// OffsetY returns the Y offset +func (r *ImageRef) OffsetY() int { + return int(r.image.Yoffset) +} + +// BandFormat returns the current band format +func (r *ImageRef) BandFormat() BandFormat { + return BandFormat(int(r.image.BandFmt)) +} + +// Coding returns the image coding +func (r *ImageRef) Coding() Coding { + return Coding(int(r.image.Coding)) +} + +// Interpretation returns the current interpretation of the color space of the image. +func (r *ImageRef) Interpretation() Interpretation { + return Interpretation(int(r.image.Type)) +} + +// ColorSpace returns the interpretation of the current color space. Alias to Interpretation(). +func (r *ImageRef) ColorSpace() Interpretation { + return r.Interpretation() +} + +// IsColorSpaceSupported returns a boolean whether the image's color space is supported by libvips. +func (r *ImageRef) IsColorSpaceSupported() bool { + return vipsIsColorSpaceSupported(r.image) +} + +// Pages returns the number of pages in the Image +// For animated images this corresponds to the number of frames +func (r *ImageRef) Pages() int { + // libvips uses the same attribute (n_pages) to represent the number of pyramid layers in JP2K + // as we interpret the attribute as frames and JP2K does not support animation we override this with 1 + if r.format == ImageTypeJP2K { + return 1 + } + + return vipsGetImageNPages(r.image) +} + +// Deprecated: use Pages() instead +func (r *ImageRef) GetPages() int { + return r.Pages() +} + +// SetPages sets the number of pages in the Image +// For animated images this corresponds to the number of frames +func (r *ImageRef) SetPages(pages int) error { + out, err := vipsCopyImage(r.image) + if err != nil { + return err + } + + vipsSetImageNPages(out, pages) + + r.setImage(out) + return nil +} + +// PageHeight return the height of a single page +func (r *ImageRef) PageHeight() int { + return vipsGetPageHeight(r.image) +} + +// GetPageHeight return the height of a single page +// Deprecated use PageHeight() instead +func (r *ImageRef) GetPageHeight() int { + return vipsGetPageHeight(r.image) +} + +// SetPageHeight set the height of a page +// For animated images this is used when "unrolling" back to frames +func (r *ImageRef) SetPageHeight(height int) error { + out, err := vipsCopyImage(r.image) + if err != nil { + return err + } + + vipsSetPageHeight(out, height) + + r.setImage(out) + return nil +} + +// PageDelay get the page delay array for animation +func (r *ImageRef) PageDelay() ([]int, error) { + n := vipsGetImageNPages(r.image) + if n <= 1 { + // should not call if not multi page + return nil, nil + } + return vipsImageGetDelay(r.image, n) +} + +// SetPageDelay set the page delay array for animation +func (r *ImageRef) SetPageDelay(delay []int) error { + var data []C.int + for _, d := range delay { + data = append(data, C.int(d)) + } + return vipsImageSetDelay(r.image, data) +} + +// Export creates a byte array of the image for use. +// The function returns a byte array that can be written to a file e.g. via os.WriteFile(). +// N.B. govips does not currently have built-in support for directly exporting to a file. +// The function also returns a copy of the image metadata as well as an error. +// Deprecated: Use ExportNative or format-specific Export methods +func (r *ImageRef) Export(params *ExportParams) ([]byte, *ImageMetadata, error) { + if params == nil || params.Format == ImageTypeUnknown { + return r.ExportNative() + } + + format := params.Format + + if !IsTypeSupported(format) { + return nil, r.newMetadata(ImageTypeUnknown), fmt.Errorf("cannot save to %#v", ImageTypes[format]) + } + + switch format { + case ImageTypeGIF: + return r.ExportGIF(&GifExportParams{ + Quality: params.Quality, + }) + case ImageTypeWEBP: + return r.ExportWebp(&WebpExportParams{ + StripMetadata: params.StripMetadata, + Quality: params.Quality, + Lossless: params.Lossless, + ReductionEffort: params.Effort, + }) + case ImageTypePNG: + return r.ExportPng(&PngExportParams{ + StripMetadata: params.StripMetadata, + Compression: params.Compression, + Interlace: params.Interlaced, + }) + case ImageTypeTIFF: + compression := TiffCompressionLzw + if params.Lossless { + compression = TiffCompressionNone + } + return r.ExportTiff(&TiffExportParams{ + StripMetadata: params.StripMetadata, + Quality: params.Quality, + Compression: compression, + }) + case ImageTypeHEIF: + return r.ExportHeif(&HeifExportParams{ + Quality: params.Quality, + Lossless: params.Lossless, + }) + case ImageTypeAVIF: + return r.ExportAvif(&AvifExportParams{ + StripMetadata: params.StripMetadata, + Quality: params.Quality, + Lossless: params.Lossless, + Speed: params.Speed, + }) + case ImageTypeJXL: + return r.ExportJxl(&JxlExportParams{ + Quality: params.Quality, + Lossless: params.Lossless, + Effort: params.Effort, + }) + default: + format = ImageTypeJPEG + return r.ExportJpeg(&JpegExportParams{ + Quality: params.Quality, + StripMetadata: params.StripMetadata, + Interlace: params.Interlaced, + OptimizeCoding: params.OptimizeCoding, + SubsampleMode: params.SubsampleMode, + TrellisQuant: params.TrellisQuant, + OvershootDeringing: params.OvershootDeringing, + OptimizeScans: params.OptimizeScans, + QuantTable: params.QuantTable, + }) + } +} + +// ExportNative exports the image to a buffer based on its native format with default parameters. +func (r *ImageRef) ExportNative() ([]byte, *ImageMetadata, error) { + switch r.format { + case ImageTypeJPEG: + return r.ExportJpeg(NewJpegExportParams()) + case ImageTypePNG: + return r.ExportPng(NewPngExportParams()) + case ImageTypeWEBP: + return r.ExportWebp(NewWebpExportParams()) + case ImageTypeHEIF: + return r.ExportHeif(NewHeifExportParams()) + case ImageTypeTIFF: + return r.ExportTiff(NewTiffExportParams()) + case ImageTypeAVIF: + return r.ExportAvif(NewAvifExportParams()) + case ImageTypeJP2K: + return r.ExportJp2k(NewJp2kExportParams()) + case ImageTypeGIF: + return r.ExportGIF(NewGifExportParams()) + case ImageTypeJXL: + return r.ExportJxl(NewJxlExportParams()) + default: + return r.ExportJpeg(NewJpegExportParams()) + } +} + +// ExportJpeg exports the image as JPEG to a buffer. +func (r *ImageRef) ExportJpeg(params *JpegExportParams) ([]byte, *ImageMetadata, error) { + if params == nil { + params = NewJpegExportParams() + } + + buf, err := vipsSaveJPEGToBuffer(r.image, *params) + if err != nil { + return nil, nil, err + } + + return buf, r.newMetadata(ImageTypeJPEG), nil +} + +// ExportPng exports the image as PNG to a buffer. +func (r *ImageRef) ExportPng(params *PngExportParams) ([]byte, *ImageMetadata, error) { + if params == nil { + params = NewPngExportParams() + } + + buf, err := vipsSavePNGToBuffer(r.image, *params) + if err != nil { + return nil, nil, err + } + + return buf, r.newMetadata(ImageTypePNG), nil +} + +// ExportWebp exports the image as WEBP to a buffer. +func (r *ImageRef) ExportWebp(params *WebpExportParams) ([]byte, *ImageMetadata, error) { + if params == nil { + params = NewWebpExportParams() + } + + paramsWithIccProfile := *params + paramsWithIccProfile.IccProfile = r.optimizedIccProfile + + buf, err := vipsSaveWebPToBuffer(r.image, paramsWithIccProfile) + if err != nil { + return nil, nil, err + } + + return buf, r.newMetadata(ImageTypeWEBP), nil +} + +// ExportHeif exports the image as HEIF to a buffer. +func (r *ImageRef) ExportHeif(params *HeifExportParams) ([]byte, *ImageMetadata, error) { + if params == nil { + params = NewHeifExportParams() + } + + buf, err := vipsSaveHEIFToBuffer(r.image, *params) + if err != nil { + return nil, nil, err + } + + return buf, r.newMetadata(ImageTypeHEIF), nil +} + +// ExportTiff exports the image as TIFF to a buffer. +func (r *ImageRef) ExportTiff(params *TiffExportParams) ([]byte, *ImageMetadata, error) { + if params == nil { + params = NewTiffExportParams() + } + + buf, err := vipsSaveTIFFToBuffer(r.image, *params) + if err != nil { + return nil, nil, err + } + + return buf, r.newMetadata(ImageTypeTIFF), nil +} + +// ExportGIF exports the image as GIF to a buffer. +func (r *ImageRef) ExportGIF(params *GifExportParams) ([]byte, *ImageMetadata, error) { + if params == nil { + params = NewGifExportParams() + } + + buf, err := vipsSaveGIFToBuffer(r.image, *params) + if err != nil { + return nil, nil, err + } + + return buf, r.newMetadata(ImageTypeGIF), nil +} + +// ExportAvif exports the image as AVIF to a buffer. +func (r *ImageRef) ExportAvif(params *AvifExportParams) ([]byte, *ImageMetadata, error) { + if params == nil { + params = NewAvifExportParams() + } + + buf, err := vipsSaveAVIFToBuffer(r.image, *params) + if err != nil { + return nil, nil, err + } + + return buf, r.newMetadata(ImageTypeAVIF), nil +} + +// ExportJp2k exports the image as JPEG2000 to a buffer. +func (r *ImageRef) ExportJp2k(params *Jp2kExportParams) ([]byte, *ImageMetadata, error) { + if params == nil { + params = NewJp2kExportParams() + } + + buf, err := vipsSaveJP2KToBuffer(r.image, *params) + if err != nil { + return nil, nil, err + } + + return buf, r.newMetadata(ImageTypeJP2K), nil +} + +// ExportJxl exports the image as JPEG XL to a buffer. +func (r *ImageRef) ExportJxl(params *JxlExportParams) ([]byte, *ImageMetadata, error) { + if params == nil { + params = NewJxlExportParams() + } + + buf, err := vipsSaveJxlToBuffer(r.image, *params) + if err != nil { + return nil, nil, err + } + + return buf, r.newMetadata(ImageTypeJXL), nil +} + +// CompositeMulti composites the given overlay image on top of the associated image with provided blending mode. +func (r *ImageRef) CompositeMulti(ins []*ImageComposite) error { + out, err := vipsComposite(toVipsCompositeStructs(r, ins)) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Composite composites the given overlay image on top of the associated image with provided blending mode. +func (r *ImageRef) Composite(overlay *ImageRef, mode BlendMode, x, y int) error { + out, err := vipsComposite2(r.image, overlay.image, mode, x, y) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Insert draws the image on top of the associated image at the given coordinates. +func (r *ImageRef) Insert(sub *ImageRef, x, y int, expand bool, background *ColorRGBA) error { + out, err := vipsInsert(r.image, sub.image, x, y, expand, background) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Join joins this image with another in the direction specified +func (r *ImageRef) Join(in *ImageRef, dir Direction) error { + out, err := vipsJoin(r.image, in.image, dir) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// ArrayJoin joins an array of images together wrapping at each n images +func (r *ImageRef) ArrayJoin(images []*ImageRef, across int) error { + allImages := append([]*ImageRef{r}, images...) + inputs := make([]*C.VipsImage, len(allImages)) + for i := range inputs { + inputs[i] = allImages[i].image + } + out, err := vipsArrayJoin(inputs, across) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Mapim resamples an image using index to look up pixels +func (r *ImageRef) Mapim(index *ImageRef) error { + out, err := vipsMapim(r.image, index.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Maplut maps an image through another image acting as a LUT (Look Up Table) +func (r *ImageRef) Maplut(lut *ImageRef) error { + out, err := vipsMaplut(r.image, lut.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// ExtractBand extracts one or more bands out of the image (replacing the associated ImageRef) +func (r *ImageRef) ExtractBand(band int, num int) error { + out, err := vipsExtractBand(r.image, band, num) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// ExtractBandToImage extracts one or more bands out of the image to a new image +func (r *ImageRef) ExtractBandToImage(band int, num int) (*ImageRef, error) { + out, err := vipsExtractBand(r.image, band, num) + if err != nil { + return nil, err + } + return newImageRef(out, ImageTypeUnknown, ImageTypeUnknown, nil), nil +} + +// BandJoin joins a set of images together, bandwise. +func (r *ImageRef) BandJoin(images ...*ImageRef) error { + vipsImages := []*C.VipsImage{r.image} + for _, vipsImage := range images { + vipsImages = append(vipsImages, vipsImage.image) + } + + out, err := vipsBandJoin(vipsImages) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// BandSplit split an n-band image into n separate images.. +func (r *ImageRef) BandSplit() ([]*ImageRef, error) { + var out []*ImageRef + for i := 0; i < r.Bands(); i++ { + img, err := vipsExtractBand(r.image, i, 1) + if err != nil { + return out, err + } + out = append(out, &ImageRef{image: img}) + } + return out, nil +} + +// BandJoinConst appends a set of constant bands to an image. +func (r *ImageRef) BandJoinConst(constants []float64) error { + out, err := vipsBandJoinConst(r.image, constants) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// AddAlpha adds an alpha channel to the associated image. +func (r *ImageRef) AddAlpha() error { + if vipsHasAlpha(r.image) { + return nil + } + + out, err := vipsAddAlpha(r.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// PremultiplyAlpha premultiplies the alpha channel. +// See https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-premultiply +func (r *ImageRef) PremultiplyAlpha() error { + if r.preMultiplication != nil || !vipsHasAlpha(r.image) { + return nil + } + + band := r.BandFormat() + + out, err := vipsPremultiplyAlpha(r.image) + if err != nil { + return err + } + r.preMultiplication = &PreMultiplicationState{ + bandFormat: band, + } + r.setImage(out) + return nil +} + +// UnpremultiplyAlpha unpremultiplies any alpha channel. +// See https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-unpremultiply +func (r *ImageRef) UnpremultiplyAlpha() error { + if r.preMultiplication == nil { + return nil + } + + unpremultiplied, err := vipsUnpremultiplyAlpha(r.image) + if err != nil { + return err + } + defer clearImage(unpremultiplied) + + out, err := vipsCast(unpremultiplied, r.preMultiplication.bandFormat) + if err != nil { + return err + } + + r.preMultiplication = nil + r.setImage(out) + return nil +} + +// Cast converts the image to a target band format +func (r *ImageRef) Cast(format BandFormat) error { + out, err := vipsCast(r.image, format) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Add calculates a sum of the image + addend and stores it back in the image +func (r *ImageRef) Add(addend *ImageRef) error { + out, err := vipsAdd(r.image, addend.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Multiply calculates the product of the image * multiplier and stores it back in the image +func (r *ImageRef) Multiply(multiplier *ImageRef) error { + out, err := vipsMultiply(r.image, multiplier.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Divide calculates the product of the image / denominator and stores it back in the image +func (r *ImageRef) Divide(denominator *ImageRef) error { + out, err := vipsDivide(r.image, denominator.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Linear passes an image through a linear transformation (i.e. output = input * a + b). +// See https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-linear +func (r *ImageRef) Linear(a, b []float64) error { + if len(a) != len(b) { + return errors.New("a and b must be of same length") + } + + out, err := vipsLinear(r.image, a, b, len(a)) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Linear1 runs Linear() with a single constant. +// See https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-linear1 +func (r *ImageRef) Linear1(a, b float64) error { + out, err := vipsLinear1(r.image, a, b) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Adjusts the image's gamma value. +// See https://www.libvips.org/API/current/libvips-conversion.html#vips-gamma +func (r *ImageRef) Gamma(gamma float64) error { + out, err := vipsGamma(r.image, gamma) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// GetRotationAngleFromExif returns the angle which the image is currently rotated in. +// First returned value is the angle and second is a boolean indicating whether image is flipped. +// This is based on the EXIF orientation tag standard. +// If no proper orientation number is provided, the picture will be assumed to be upright. +func GetRotationAngleFromExif(orientation int) (Angle, bool) { + switch orientation { + case 0, 1, 2: + return Angle0, orientation == 2 + case 3, 4: + return Angle180, orientation == 4 + case 5, 8: + return Angle90, orientation == 5 + case 6, 7: + return Angle270, orientation == 7 + } + + return Angle0, false +} + +// AutoRotate rotates the image upright based on the EXIF Orientation tag. +// It also resets the orientation information in the EXIF tag to be 1 (i.e. upright). +// N.B. libvips does not flip images currently (i.e. no support for orientations 2, 4, 5 and 7). +// N.B. due to the HEIF image standard, HEIF images are always autorotated by default on load. +// Thus, calling AutoRotate for HEIF images is not needed. +// todo: use https://www.libvips.org/API/current/libvips-conversion.html#vips-autorot-remove-angle +func (r *ImageRef) AutoRotate() error { + out, err := vipsAutoRotate(r.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// ExtractArea crops the image to a specified area +func (r *ImageRef) ExtractArea(left, top, width, height int) error { + if r.Height() > r.PageHeight() { + // use animated extract area if more than 1 pages loaded + out, err := vipsExtractAreaMultiPage(r.image, left, top, width, height) + if err != nil { + return err + } + r.setImage(out) + } else { + out, err := vipsExtractArea(r.image, left, top, width, height) + if err != nil { + return err + } + r.setImage(out) + } + return nil +} + +// GetICCProfile retrieves the ICC profile data (if any) from the image. +func (r *ImageRef) GetICCProfile() []byte { + bytes, _ := vipsGetICCProfile(r.image) + return bytes +} + +// RemoveICCProfile removes the ICC Profile information from the image. +// Typically, browsers and other software assume images without profile to be in the sRGB color space. +func (r *ImageRef) RemoveICCProfile() error { + out, err := vipsCopyImage(r.image) + if err != nil { + return err + } + + vipsRemoveICCProfile(out) + + r.setImage(out) + return nil +} + +// TransformICCProfileWithFallback transforms from the embedded ICC profile of the image to the ICC profile at the given path. +// The fallback ICC profile is used if the image does not have an embedded ICC profile. +func (r *ImageRef) TransformICCProfileWithFallback(targetProfilePath, fallbackProfilePath string) error { + depth := 16 + if r.BandFormat() == BandFormatUchar || r.BandFormat() == BandFormatChar || r.BandFormat() == BandFormatNotSet { + depth = 8 + } + + out, err := vipsICCTransform(r.image, targetProfilePath, fallbackProfilePath, IntentPerceptual, depth, true) + if err != nil { + govipsLog("govips", LogLevelError, fmt.Sprintf("failed to do icc transform: %v", err.Error())) + return err + } + + r.setImage(out) + return nil +} + +// TransformICCProfile transforms from the embedded ICC profile of the image to the icc profile at the given path. +func (r *ImageRef) TransformICCProfile(outputProfilePath string) error { + return r.TransformICCProfileWithFallback(outputProfilePath, SRGBIEC6196621ICCProfilePath) +} + +// OptimizeICCProfile optimizes the ICC color profile of the image. +// For two color channel images, it sets a grayscale profile. +// For color images, it sets a CMYK or non-CMYK profile based on the image metadata. +func (r *ImageRef) OptimizeICCProfile() error { + inputProfile := r.determineInputICCProfile() + if !r.HasICCProfile() && (inputProfile == "") { + // No embedded ICC profile in the input image and no input profile determined, nothing to do. + return nil + } + + r.optimizedIccProfile = SRGBV2MicroICCProfilePath + if r.Bands() <= 2 { + r.optimizedIccProfile = SGrayV2MicroICCProfilePath + } + + embedded := r.HasICCProfile() && (inputProfile == "") + + depth := 16 + if r.BandFormat() == BandFormatUchar || r.BandFormat() == BandFormatChar || r.BandFormat() == BandFormatNotSet { + depth = 8 + } + + out, err := vipsICCTransform(r.image, r.optimizedIccProfile, inputProfile, IntentPerceptual, depth, embedded) + if err != nil { + govipsLog("govips", LogLevelError, fmt.Sprintf("failed to do icc transform: %v", err.Error())) + return err + } + + r.setImage(out) + return nil +} + +// RemoveMetadata removes the EXIF metadata from the image. +// N.B. this function won't remove the ICC profile, orientation and pages metadata +// because govips needs it to correctly display the image. +func (r *ImageRef) RemoveMetadata(keep ...string) error { + out, err := vipsCopyImage(r.image) + if err != nil { + return err + } + + vipsRemoveMetadata(out, keep...) + + r.setImage(out) + + return nil +} + +func (r *ImageRef) ImageFields() []string { + return r.GetFields() +} + +func (r *ImageRef) GetFields() []string { + return vipsImageGetFields(r.image) +} + +func (r *ImageRef) SetBlob(name string, data []byte) { + vipsImageSetBlob(r.image, name, data) +} + +func (r *ImageRef) GetBlob(name string) []byte { + return vipsImageGetBlob(r.image, name) +} + +func (r *ImageRef) SetDouble(name string, f float64) { + vipsImageSetDouble(r.image, name, f) +} + +func (r *ImageRef) GetDouble(name string) float64 { + return vipsImageGetDouble(r.image, name) +} + +func (r *ImageRef) SetInt(name string, i int) { + vipsImageSetInt(r.image, name, i) +} + +func (r *ImageRef) GetInt(name string) int { + return vipsImageGetInt(r.image, name) +} + +func (r *ImageRef) SetString(name string, str string) { + vipsImageSetString(r.image, name, str) +} + +func (r *ImageRef) GetString(name string) string { + return vipsImageGetString(r.image, name) +} + +func (r *ImageRef) GetAsString(name string) string { + return vipsImageGetAsString(r.image, name) +} + +func (r *ImageRef) HasExif() bool { + for _, field := range r.ImageFields() { + if strings.HasPrefix(field, "exif-") { + return true + } + } + + return false +} + +func (r *ImageRef) GetExif() map[string]string { + return vipsImageGetExifData(r.image) +} + +// ToColorSpace changes the color space of the image to the interpretation supplied as the parameter. +func (r *ImageRef) ToColorSpace(interpretation Interpretation) error { + out, err := vipsToColorSpace(r.image, interpretation) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Flatten removes the alpha channel from the image and replaces it with the background color +func (r *ImageRef) Flatten(backgroundColor *Color) error { + out, err := vipsFlatten(r.image, backgroundColor) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// GaussianBlur blurs the image +// add support minAmpl +func (r *ImageRef) GaussianBlur(sigmas ...float64) error { + var ( + sigma = sigmas[0] + minAmpl = GaussBlurDefaultMinAMpl + ) + if len(sigmas) >= 2 { + minAmpl = sigmas[1] + } + out, err := vipsGaussianBlur(r.image, sigma, minAmpl) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Sharpen sharpens the image +// sigma: sigma of the gaussian +// x1: flat/jaggy threshold +// m2: slope for jaggy areas +func (r *ImageRef) Sharpen(sigma float64, x1 float64, m2 float64) error { + out, err := vipsSharpen(r.image, sigma, x1, m2) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Apply Sobel edge detector to the image. +func (r *ImageRef) Sobel() error { + out, err := vipsSobel(r.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Modulate the colors +func (r *ImageRef) Modulate(brightness, saturation, hue float64) error { + var err error + var multiplications []float64 + var additions []float64 + + colorspace := r.ColorSpace() + if colorspace == InterpretationRGB { + colorspace = InterpretationSRGB + } + + multiplications = []float64{brightness, saturation, 1} + additions = []float64{0, 0, hue} + + if r.HasAlpha() { + multiplications = append(multiplications, 1) + additions = append(additions, 0) + } + + err = r.ToColorSpace(InterpretationLCH) + if err != nil { + return err + } + + err = r.Linear(multiplications, additions) + if err != nil { + return err + } + + err = r.ToColorSpace(colorspace) + if err != nil { + return err + } + + return nil +} + +// ModulateHSV modulates the image HSV values based on the supplier parameters. +func (r *ImageRef) ModulateHSV(brightness, saturation float64, hue int) error { + var err error + var multiplications []float64 + var additions []float64 + + colorspace := r.ColorSpace() + if colorspace == InterpretationRGB { + colorspace = InterpretationSRGB + } + + if r.HasAlpha() { + multiplications = []float64{1, saturation, brightness, 1} + additions = []float64{float64(hue), 0, 0, 0} + } else { + multiplications = []float64{1, saturation, brightness} + additions = []float64{float64(hue), 0, 0} + } + + err = r.ToColorSpace(InterpretationHSV) + if err != nil { + return err + } + + err = r.Linear(multiplications, additions) + if err != nil { + return err + } + + err = r.ToColorSpace(colorspace) + if err != nil { + return err + } + + return nil +} + +// Invert inverts the image +func (r *ImageRef) Invert() error { + out, err := vipsInvert(r.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Average finds the average value in an image +func (r *ImageRef) Average() (float64, error) { + out, err := vipsAverage(r.image) + if err != nil { + return 0, err + } + return out, nil +} + +// FindTrim returns the bounding box of the non-border part of the image +// Returned values are left, top, width, height +func (r *ImageRef) FindTrim(threshold float64, backgroundColor *Color) (int, int, int, int, error) { + return vipsFindTrim(r.image, threshold, backgroundColor) +} + +// GetPoint reads a single pixel on an image. +// The pixel values are returned in a slice of length n. +func (r *ImageRef) GetPoint(x int, y int) ([]float64, error) { + n := 3 + if vipsHasAlpha(r.image) { + n = 4 + } + return vipsGetPoint(r.image, n, x, y) +} + +// Stats find many image statistics in a single pass through the data. Image is changed into a one-band +// `BandFormatDouble` image of at least 10 columns by n + 1 (where n is number of bands in image in) +// rows. Columns are statistics, and are, in order: minimum, maximum, sum, sum of squares, mean, +// standard deviation, x coordinate of minimum, y coordinate of minimum, x coordinate of maximum, +// y coordinate of maximum. +// +// Row 0 has statistics for all bands together, row 1 has stats for band 1, and so on. +// +// If there is more than one maxima or minima, one of them will be chosen at random. +func (r *ImageRef) Stats() error { + out, err := vipsStats(r.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// HistogramFind find the histogram the image. +// Find the histogram for all bands (producing a one-band histogram). +// char and uchar images are cast to uchar before histogramming, all other image types are cast to ushort. +func (r *ImageRef) HistogramFind() error { + out, err := vipsHistFind(r.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// HistogramCumulative form cumulative histogram. +func (r *ImageRef) HistogramCumulative() error { + out, err := vipsHistCum(r.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// HistogramNormalise +// The maximum of each band becomes equal to the maximum index, so for example the max for a uchar +// image becomes 255. Normalise each band separately. +func (r *ImageRef) HistogramNormalise() error { + out, err := vipsHistNorm(r.image) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// HistogramEntropy estimate image entropy from a histogram. Entropy is calculated as: +// `-sum(p * log2(p))` +// where p is histogram-value / sum-of-histogram-values. +func (r *ImageRef) HistogramEntropy() (float64, error) { + return vipsHistEntropy(r.image) +} + +// DrawRect draws an (optionally filled) rectangle with a single colour +func (r *ImageRef) DrawRect(ink ColorRGBA, left int, top int, width int, height int, fill bool) error { + err := vipsDrawRect(r.image, ink, left, top, width, height, fill) + if err != nil { + return err + } + return nil +} + +// Rank does rank filtering on an image. A window of size width by height is passed over the image. +// At each position, the pixels inside the window are sorted into ascending order and the pixel at position +// index is output. index numbers from 0. +func (r *ImageRef) Rank(width int, height int, index int) error { + out, err := vipsRank(r.image, width, height, index) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Resize resizes the image based on the scale, maintaining aspect ratio +func (r *ImageRef) Resize(scale float64, kernel Kernel) error { + return r.ResizeWithVScale(scale, -1, kernel) +} + +// ResizeWithVScale resizes the image with both horizontal and vertical scaling. +// The parameters are the scaling factors. +func (r *ImageRef) ResizeWithVScale(hScale, vScale float64, kernel Kernel) error { + if err := r.PremultiplyAlpha(); err != nil { + return err + } + + pages := r.Pages() + pageHeight := r.GetPageHeight() + + out, err := vipsResizeWithVScale(r.image, hScale, vScale, kernel) + if err != nil { + return err + } + r.setImage(out) + + if pages > 1 { + scale := hScale + if vScale != -1 { + scale = vScale + } + newPageHeight := int(float64(pageHeight)*scale + 0.5) + if err := r.SetPageHeight(newPageHeight); err != nil { + return err + } + } + + return r.UnpremultiplyAlpha() +} + +// Thumbnail resizes the image to the given width and height. +// crop decides algorithm vips uses to shrink and crop to fill target, +func (r *ImageRef) Thumbnail(width, height int, crop Interesting) error { + out, err := vipsThumbnail(r.image, width, height, crop, SizeBoth) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// ThumbnailWithSize resizes the image to the given width and height. +// crop decides algorithm vips uses to shrink and crop to fill target, +// size controls upsize, downsize, both or force +func (r *ImageRef) ThumbnailWithSize(width, height int, crop Interesting, size Size) error { + out, err := vipsThumbnail(r.image, width, height, crop, size) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Embed embeds the given picture in a new one, i.e. the opposite of ExtractArea +func (r *ImageRef) Embed(left, top, width, height int, extend ExtendStrategy) error { + if r.Height() > r.PageHeight() { + out, err := vipsEmbedMultiPage(r.image, left, top, width, height, extend) + if err != nil { + return err + } + r.setImage(out) + } else { + out, err := vipsEmbed(r.image, left, top, width, height, extend) + if err != nil { + return err + } + r.setImage(out) + } + return nil +} + +// EmbedBackground embeds the given picture with a background color +func (r *ImageRef) EmbedBackground(left, top, width, height int, backgroundColor *Color) error { + c := &ColorRGBA{ + R: backgroundColor.R, + G: backgroundColor.G, + B: backgroundColor.B, + A: 255, + } + if r.Height() > r.PageHeight() { + out, err := vipsEmbedMultiPageBackground(r.image, left, top, width, height, c) + if err != nil { + return err + } + r.setImage(out) + } else { + out, err := vipsEmbedBackground(r.image, left, top, width, height, c) + if err != nil { + return err + } + r.setImage(out) + } + return nil +} + +// EmbedBackgroundRGBA embeds the given picture with a background rgba color +func (r *ImageRef) EmbedBackgroundRGBA(left, top, width, height int, backgroundColor *ColorRGBA) error { + if r.Height() > r.PageHeight() { + out, err := vipsEmbedMultiPageBackground(r.image, left, top, width, height, backgroundColor) + if err != nil { + return err + } + r.setImage(out) + } else { + out, err := vipsEmbedBackground(r.image, left, top, width, height, backgroundColor) + if err != nil { + return err + } + r.setImage(out) + } + return nil +} + +// Zoom zooms the image by repeating pixels (fast nearest-neighbour) +func (r *ImageRef) Zoom(xFactor int, yFactor int) error { + out, err := vipsZoom(r.image, xFactor, yFactor) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Flip flips the image either horizontally or vertically based on the parameter +func (r *ImageRef) Flip(direction Direction) error { + out, err := vipsFlip(r.image, direction) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Recomb recombines the image bands using the matrix provided +func (r *ImageRef) Recomb(matrix [][]float64) error { + numBands := r.Bands() + // Ensure the provided matrix is 3x3 + if len(matrix) != 3 || len(matrix[0]) != 3 || len(matrix[1]) != 3 || len(matrix[2]) != 3 { + return errors.New("Invalid recombination matrix") + } + // If the image is RGBA, expand the matrix to 4x4 + if numBands == 4 { + matrix = append(matrix, []float64{0, 0, 0, 1}) + for i := 0; i < 3; i++ { + matrix[i] = append(matrix[i], 0) + } + } else if numBands != 3 { + return errors.New("Unsupported number of bands") + } + + // Flatten the matrix + var matrixValues []float64 + for _, row := range matrix { + for _, value := range row { + matrixValues = append(matrixValues, value) + } + } + + // Convert the Go slice to a C array and get its size + matrixPtr := unsafe.Pointer(&matrixValues[0]) + matrixSize := C.size_t(len(matrixValues) * 8) // 8 bytes for each float64 + + // Create a VipsImage from the matrix in memory + matrixImage := C.vips_image_new_from_memory(matrixPtr, matrixSize, C.int(numBands), C.int(numBands), 1, C.VIPS_FORMAT_DOUBLE) + + // Check for any Vips errors + errMsg := C.GoString(C.vips_error_buffer()) + if errMsg != "" { + C.vips_error_clear() + return errors.New("Vips error: " + errMsg) + } + + // Recombine the image using the matrix + out, err := vipsRecomb(r.image, matrixImage) + if err != nil { + return err + } + + r.setImage(out) + return nil +} + +// Rotate rotates the image by multiples of 90 degrees. To rotate by arbitrary angles use Similarity. +func (r *ImageRef) Rotate(angle Angle) error { + width := r.Width() + + if r.Pages() > 1 && (angle == Angle90 || angle == Angle270) { + if angle == Angle270 { + if err := r.Flip(DirectionHorizontal); err != nil { + return err + } + } + + if err := r.Grid(r.GetPageHeight(), r.Pages(), 1); err != nil { + return err + } + + if angle == Angle270 { + if err := r.Flip(DirectionHorizontal); err != nil { + return err + } + } + + } + + out, err := vipsRotate(r.image, angle) + if err != nil { + return err + } + r.setImage(out) + + if r.Pages() > 1 && (angle == Angle90 || angle == Angle270) { + if err := r.SetPageHeight(width); err != nil { + return err + } + } + return nil +} + +// Similarity lets you scale, offset and rotate images by arbitrary angles in a single operation while defining the +// color of new background pixels. If the input image has no alpha channel, the alpha on `backgroundColor` will be +// ignored. You can add an alpha channel to an image with `BandJoinConst` (e.g. `img.BandJoinConst([]float64{255})`) or +// AddAlpha. +func (r *ImageRef) Similarity(scale float64, angle float64, backgroundColor *ColorRGBA, + idx float64, idy float64, odx float64, ody float64) error { + out, err := vipsSimilarity(r.image, scale, angle, backgroundColor, idx, idy, odx, ody) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Grid tiles the image pages into a matrix across*down +func (r *ImageRef) Grid(tileHeight, across, down int) error { + out, err := vipsGrid(r.image, tileHeight, across, down) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// SmartCrop will crop the image based on interesting factor +func (r *ImageRef) SmartCrop(width int, height int, interesting Interesting) error { + out, err := vipsSmartCrop(r.image, width, height, interesting) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Crop will crop the image based on coordinate and box size +func (r *ImageRef) Crop(left int, top int, width int, height int) error { + out, err := vipsCrop(r.image, left, top, width, height) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Label overlays a label on top of the image +func (r *ImageRef) Label(labelParams *LabelParams) error { + out, err := labelImage(r.image, labelParams) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// Replicate repeats an image many times across and down +func (r *ImageRef) Replicate(across int, down int) error { + out, err := vipsReplicate(r.image, across, down) + if err != nil { + return err + } + r.setImage(out) + return nil +} + +// ToBytes writes the image to memory in VIPs format and returns the raw bytes, useful for storage. +func (r *ImageRef) ToBytes() ([]byte, error) { + var cSize C.size_t + cData := C.vips_image_write_to_memory(r.image, &cSize) + if cData == nil { + return nil, errors.New("failed to write image to memory") + } + defer C.free(cData) + + data := C.GoBytes(unsafe.Pointer(cData), C.int(cSize)) + return data, nil +} + +func (r *ImageRef) determineInputICCProfile() (inputProfile string) { + if r.Interpretation() == InterpretationCMYK { + inputProfile = "cmyk" + } + return +} + +// ToImage converts a VIPs image to a golang image.Image object, useful for interoperability with other golang libraries +func (r *ImageRef) ToImage(params *ExportParams) (image.Image, error) { + imageBytes, _, err := r.Export(params) + if err != nil { + return nil, err + } + + reader := bytes.NewReader(imageBytes) + img, _, err := image.Decode(reader) + if err != nil { + return nil, err + } + + return img, nil +} + +// setImage resets the image for this image and frees the previous one +func (r *ImageRef) setImage(image *C.VipsImage) { + r.lock.Lock() + defer r.lock.Unlock() + + if r.image == image { + return + } + + if r.image != nil { + clearImage(r.image) + } + + r.image = image +} + +func vipsHasAlpha(in *C.VipsImage) bool { + return int(C.has_alpha_channel(in)) > 0 +} + +func clearImage(ref *C.VipsImage) { + C.clear_image(&ref) +} + +// Coding represents VIPS_CODING type +type Coding int + +// Coding enum +// +//goland:noinspection GoUnusedConst +const ( + CodingError Coding = C.VIPS_CODING_ERROR + CodingNone Coding = C.VIPS_CODING_NONE + CodingLABQ Coding = C.VIPS_CODING_LABQ + CodingRAD Coding = C.VIPS_CODING_RAD +) + +func (r *ImageRef) newMetadata(format ImageType) *ImageMetadata { + return &ImageMetadata{ + Format: format, + Width: r.Width(), + Height: r.Height(), + Colorspace: r.ColorSpace(), + Orientation: r.Orientation(), + Pages: r.Pages(), + } +} + +// Pixelate applies a simple pixelate filter to the image +func Pixelate(imageRef *ImageRef, factor float64) (err error) { + if factor < 1 { + return errors.New("factor must be greater then 1") + } + + width := imageRef.Width() + height := imageRef.Height() + + if err = imageRef.Resize(1/factor, KernelAuto); err != nil { + return + } + + hScale := float64(width) / float64(imageRef.Width()) + vScale := float64(height) / float64(imageRef.Height()) + if err = imageRef.ResizeWithVScale(hScale, vScale, KernelNearest); err != nil { + return + } + + return +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/image.h b/vendor/github.com/davidbyttow/govips/v2/vips/image.h new file mode 100644 index 000000000..0a116f008 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/image.h @@ -0,0 +1,8 @@ +// https://libvips.github.io/libvips/API/current/VipsImage.html + +#include +#include + +int has_alpha_channel(VipsImage *image); + +void clear_image(VipsImage **image); diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/label.c b/vendor/github.com/davidbyttow/govips/v2/vips/label.c new file mode 100644 index 000000000..01de81343 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/label.c @@ -0,0 +1,37 @@ +#include "label.h" + +int text(VipsImage **out, const char *text, const char *font, int width, + int height, VipsAlign align, int dpi) { + return vips_text(out, text, "font", font, "width", width, "height", height, + "align", align, "dpi", dpi, NULL); +} + +int label(VipsImage *in, VipsImage **out, LabelOptions *o) { + double ones[3] = {1, 1, 1}; + VipsImage *base = vips_image_new(); + VipsImage **t = (VipsImage **)vips_object_local_array(VIPS_OBJECT(base), 9); + if (vips_text(&t[0], o->Text, "font", o->Font, "width", o->Width, "height", + o->Height, "align", o->Align, NULL) || + vips_linear1(t[0], &t[1], o->Opacity, 0.0, NULL) || + vips_cast(t[1], &t[2], VIPS_FORMAT_UCHAR, NULL) || + vips_embed(t[2], &t[3], o->OffsetX, o->OffsetY, t[2]->Xsize + o->OffsetX, + t[2]->Ysize + o->OffsetY, NULL)) { + g_object_unref(base); + return 1; + } + if (vips_black(&t[4], 1, 1, NULL) || + vips_linear(t[4], &t[5], ones, o->Color, 3, NULL) || + vips_cast(t[5], &t[6], VIPS_FORMAT_UCHAR, NULL) || + vips_copy(t[6], &t[7], "interpretation", in->Type, NULL) || + vips_embed(t[7], &t[8], 0, 0, in->Xsize, in->Ysize, "extend", + VIPS_EXTEND_COPY, NULL)) { + g_object_unref(base); + return 1; + } + if (vips_ifthenelse(t[3], t[8], in, out, "blend", TRUE, NULL)) { + g_object_unref(base); + return 1; + } + g_object_unref(base); + return 0; +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/label.go b/vendor/github.com/davidbyttow/govips/v2/vips/label.go new file mode 100644 index 000000000..84320b634 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/label.go @@ -0,0 +1,84 @@ +package vips + +// #include "label.h" +import "C" +import "unsafe" + +// Align represents VIPS_ALIGN +type Align int + +// Direction enum +const ( + AlignLow Align = C.VIPS_ALIGN_LOW + AlignCenter Align = C.VIPS_ALIGN_CENTRE + AlignHigh Align = C.VIPS_ALIGN_HIGH +) + +// DefaultFont is the default font to be used for label texts created by govips +const DefaultFont = "sans 10" + +// LabelParams represents a text-based label +type LabelParams struct { + Text string + Font string + Width Scalar + Height Scalar + OffsetX Scalar + OffsetY Scalar + Opacity float32 + Color Color + Alignment Align +} + +type vipsLabelOptions struct { + Text *C.char + Font *C.char + Width C.int + Height C.int + OffsetX C.int + OffsetY C.int + Alignment C.VipsAlign + DPI C.int + Margin C.int + Opacity C.float + Color [3]C.double +} + +func labelImage(in *C.VipsImage, params *LabelParams) (*C.VipsImage, error) { + incOpCounter("label") + var out *C.VipsImage + + text := C.CString(params.Text) + defer freeCString(text) + + font := C.CString(params.Font) + defer freeCString(font) + + // todo: release color? + color := [3]C.double{C.double(params.Color.R), C.double(params.Color.G), C.double(params.Color.B)} + + w := params.Width.GetRounded(int(in.Xsize)) + h := params.Height.GetRounded(int(in.Ysize)) + offsetX := params.OffsetX.GetRounded(int(in.Xsize)) + offsetY := params.OffsetY.GetRounded(int(in.Ysize)) + + opts := vipsLabelOptions{ + Text: text, + Font: font, + Width: C.int(w), + Height: C.int(h), + OffsetX: C.int(offsetX), + OffsetY: C.int(offsetY), + Alignment: C.VipsAlign(params.Alignment), + Opacity: C.float(params.Opacity), + Color: color, + } + + // todo: release inline pointer? + err := C.label(in, &out, (*C.LabelOptions)(unsafe.Pointer(&opts))) + if err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/label.h b/vendor/github.com/davidbyttow/govips/v2/vips/label.h new file mode 100644 index 000000000..332a5da30 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/label.h @@ -0,0 +1,21 @@ +#include +#include + +typedef struct { + const char *Text; + const char *Font; + int Width; + int Height; + int OffsetX; + int OffsetY; + VipsAlign Align; + int DPI; + int Margin; + float Opacity; + double Color[3]; +} LabelOptions; + +int label(VipsImage *in, VipsImage **out, LabelOptions *o); + +int text(VipsImage **out, const char *text, const char *font, int width, + int height, VipsAlign align, int dpi); diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/lang.go b/vendor/github.com/davidbyttow/govips/v2/vips/lang.go new file mode 100644 index 000000000..6889b798b --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/lang.go @@ -0,0 +1,49 @@ +package vips + +// #include +// #include +import "C" + +import ( + "reflect" + "unsafe" +) + +func freeCString(s *C.char) { + C.free(unsafe.Pointer(s)) +} + +func gFreePointer(ref unsafe.Pointer) { + C.g_free(C.gpointer(ref)) +} + +func boolToInt(b bool) int { + if b { + return 1 + } + return 0 +} + +func toGboolean(b bool) C.gboolean { + if b { + return C.gboolean(1) + } + return C.gboolean(0) +} + +func fromGboolean(b C.gboolean) bool { + return b != 0 +} + +func fromCArrayInt(out *C.int, n int) []int { + var result = make([]int, n) + var data []C.int + sh := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + sh.Data = uintptr(unsafe.Pointer(out)) + sh.Len = n + sh.Cap = n + for i := range data { + result[i] = int(data[i]) + } + return result +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/lang.h b/vendor/github.com/davidbyttow/govips/v2/vips/lang.h new file mode 100644 index 000000000..27c81384b --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/lang.h @@ -0,0 +1,4 @@ +#include +#include + +#define INT_TO_GBOOLEAN(bool) (bool > 0 ? TRUE : FALSE) diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/logging.go b/vendor/github.com/davidbyttow/govips/v2/vips/logging.go new file mode 100644 index 000000000..5053d0162 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/logging.go @@ -0,0 +1,98 @@ +package vips + +// #include +import "C" +import ( + "log" +) + +// LogLevel is the enum controlling logging message verbosity. +type LogLevel int + +// The logging verbosity levels classify and filter logging messages. +// From most to least verbose, they are debug, info, message, warning, critical and error. +const ( + LogLevelError LogLevel = C.G_LOG_LEVEL_ERROR + LogLevelCritical LogLevel = C.G_LOG_LEVEL_CRITICAL + LogLevelWarning LogLevel = C.G_LOG_LEVEL_WARNING + LogLevelMessage LogLevel = C.G_LOG_LEVEL_MESSAGE + LogLevelInfo LogLevel = C.G_LOG_LEVEL_INFO + LogLevelDebug LogLevel = C.G_LOG_LEVEL_DEBUG +) + +// Three global variables which keep state of the current logging handler +// function, desired verbosity for logging and whether defaults have been +// overridden. Set by LoggingSettings() +var ( + currentLoggingHandlerFunction LoggingHandlerFunction + currentLoggingVerbosity LogLevel + currentLoggingOverridden bool +) + +// govipsLoggingHandler is the private bridge function exported to the C library +// and called by glib and libvips for each logging message. It will call govipsLog +// which in turn will filter based on verbosity and direct the messages to the +// currently chosen LoggingHandlerFunction. +// +//export govipsLoggingHandler +func govipsLoggingHandler(messageDomain *C.char, messageLevel C.int, message *C.char) { + govipsLog(C.GoString(messageDomain), LogLevel(messageLevel), C.GoString(message)) +} + +// LoggingHandlerFunction is a function which will be called for each log message. +// By default, govips sends logging messages to os.Stderr. If you want to log elsewhere +// such as to a file or to a state variable which you inspect yourself, define a new +// logging handler function and set it via LoggingSettings(). +type LoggingHandlerFunction func(messageDomain string, messageLevel LogLevel, message string) + +// LoggingSettings sets the logging handler and logging verbosity for govips. +// The handler function is the function which will be called for each log message. +// You can define one yourself to log somewhere else besides the default (stderr). +// Use nil as handler to use standard logging handler. +// Verbosity is the minimum logLevel you want to log. Default is logLevelInfo +// due to backwards compatibility but it's quite verbose for a library. +// Suggest setting it to at least logLevelWarning. Use logLevelDebug for debugging. +func LoggingSettings(handler LoggingHandlerFunction, verbosity LogLevel) { + currentLoggingOverridden = true + govipsLoggingSettings(handler, verbosity) +} + +func govipsLoggingSettings(handler LoggingHandlerFunction, verbosity LogLevel) { + if handler == nil { + currentLoggingHandlerFunction = defaultLoggingHandlerFunction + } else { + currentLoggingHandlerFunction = handler + } + + currentLoggingVerbosity = verbosity + // TODO turn on debugging in libvips and redirect to handler when setting verbosity to debug + // This way debugging information would go to the same channel as all other logging +} + +func defaultLoggingHandlerFunction(messageDomain string, messageLevel LogLevel, message string) { + var messageLevelDescription string + switch messageLevel { + case LogLevelError: + messageLevelDescription = "error" + case LogLevelCritical: + messageLevelDescription = "critical" + case LogLevelWarning: + messageLevelDescription = "warning" + case LogLevelMessage: + messageLevelDescription = "message" + case LogLevelInfo: + messageLevelDescription = "info" + case LogLevelDebug: + messageLevelDescription = "debug" + } + + log.Printf("[%v.%v] %v", messageDomain, messageLevelDescription, message) +} + +// govipsLog is the default function used to log debug or error messages internally in govips. +// It's used by all govips functionality directly, as well as by glib and libvips via the C bridge. +func govipsLog(messageDomain string, messageLevel LogLevel, message string) { + if messageLevel <= currentLoggingVerbosity { + currentLoggingHandlerFunction(messageDomain, messageLevel, message) + } +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/math.go b/vendor/github.com/davidbyttow/govips/v2/vips/math.go new file mode 100644 index 000000000..c7176f1f4 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/math.go @@ -0,0 +1,57 @@ +package vips + +import "math" + +// Scalar is the basic scalar measurement of an image's height, width or offset coordinate. +type Scalar struct { + Value float64 + Relative bool +} + +// ValueOf takes a floating point value and returns a corresponding Scalar struct +func ValueOf(value float64) Scalar { + return Scalar{value, false} +} + +// IsZero checkes whether the associated Scalar's value is zero. +func (s *Scalar) IsZero() bool { + return s.Value == 0 && !s.Relative +} + +// SetInt sets an integer value for the associated Scalar. +func (s *Scalar) SetInt(value int) { + s.Set(float64(value)) +} + +// Set sets a float value for the associated Scalar. +func (s *Scalar) Set(value float64) { + s.Value = value + s.Relative = false +} + +// SetScale sets a float value for the associated Scalar and makes it relative. +func (s *Scalar) SetScale(f float64) { + s.Value = f + s.Relative = true +} + +// Get returns the value of the scalar. Either absolute, or if relative, multiplied by the base given as parameter. +func (s *Scalar) Get(base int) float64 { + if s.Relative { + return s.Value * float64(base) + } + return s.Value +} + +// GetRounded returns the value of the associated Scalar, rounded to the nearest integer, if absolute. +// If the Scalar is relative, it will be multiplied by the supplied base parameter. +func (s *Scalar) GetRounded(base int) int { + return roundFloat(s.Get(base)) +} + +func roundFloat(f float64) int { + if f < 0 { + return int(math.Ceil(f - 0.5)) + } + return int(math.Floor(f + 0.5)) +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/morphology.c b/vendor/github.com/davidbyttow/govips/v2/vips/morphology.c new file mode 100644 index 000000000..b57fdd919 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/morphology.c @@ -0,0 +1,6 @@ +#include "morphology.h" + +int rank(VipsImage *in, VipsImage **out, int width, int height, int index) { + return vips_rank(in, out, width, height, index, NULL); +} + diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/morphology.go b/vendor/github.com/davidbyttow/govips/v2/vips/morphology.go new file mode 100644 index 000000000..75e8668b9 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/morphology.go @@ -0,0 +1,17 @@ +package vips + +// #include "morphology.h" +import "C" + +// https://libvips.github.io/libvips/API/current/libvips-morphology.html#vips-rank +func vipsRank(in *C.VipsImage, width int, height int, index int) (*C.VipsImage, error) { + incOpCounter("rank") + var out *C.VipsImage + + err := C.rank(in, &out, C.int(width), C.int(height), C.int(index)) + if int(err) != 0 { + return nil, handleImageError(out) + } + + return out, nil +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/morphology.h b/vendor/github.com/davidbyttow/govips/v2/vips/morphology.h new file mode 100644 index 000000000..fe8e9e1a0 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/morphology.h @@ -0,0 +1,6 @@ +// https://libvips.github.io/libvips/API/current/libvips-morphology.html + +#include +#include + +int rank(VipsImage *in, VipsImage **out, int width, int height, int index); diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/resample.c b/vendor/github.com/davidbyttow/govips/v2/vips/resample.c new file mode 100644 index 000000000..aabc4102a --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/resample.c @@ -0,0 +1,61 @@ +#include "resample.h" + +int shrink_image(VipsImage *in, VipsImage **out, double xshrink, + double yshrink) { + return vips_shrink(in, out, xshrink, yshrink, NULL); +} + +int reduce_image(VipsImage *in, VipsImage **out, double xshrink, + double yshrink) { + return vips_reduce(in, out, xshrink, yshrink, NULL); +} + +int affine_image(VipsImage *in, VipsImage **out, double a, double b, double c, + double d, VipsInterpolate *interpolator) { + return vips_affine(in, out, a, b, c, d, "interpolate", interpolator, NULL); +} + +int resize_image(VipsImage *in, VipsImage **out, double scale, gdouble vscale, + int kernel) { + if (vscale > 0) { + return vips_resize(in, out, scale, "vscale", vscale, "kernel", kernel, + NULL); + } + + return vips_resize(in, out, scale, "kernel", kernel, NULL); +} + +int thumbnail(const char *filename, VipsImage **out, + int width, int height, int crop, int size) { + return vips_thumbnail(filename, out, width, "height", height, + "crop", crop, "size", size, NULL); +} + +int thumbnail_image(VipsImage *in, VipsImage **out, int width, int height, + int crop, int size) { + return vips_thumbnail_image(in, out, width, "height", height, "crop", crop, + "size", size, NULL); +} + +int thumbnail_buffer_with_option(void *buf, size_t len, VipsImage **out, + int width, int height, int crop, int size, + const char *option_string) { + return vips_thumbnail_buffer(buf, len, out, width, "height", height, + "crop", crop, "size", size, + "option_string", option_string, NULL); +} + +int thumbnail_buffer(void *buf, size_t len, VipsImage **out, + int width, int height, int crop, int size) { + return vips_thumbnail_buffer(buf, len, out, width, "height", height, + "crop", crop, "size", size, NULL); +} + +int mapim(VipsImage *in, VipsImage **out, VipsImage *index) { + return vips_mapim(in, out, index, NULL); +} + +int maplut(VipsImage *in, VipsImage **out, VipsImage *lut) { + return vips_maplut(in, out, lut, NULL); +} + diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/resample.go b/vendor/github.com/davidbyttow/govips/v2/vips/resample.go new file mode 100644 index 000000000..9998d5232 --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/resample.go @@ -0,0 +1,146 @@ +package vips + +// #include "resample.h" +import "C" +import ( + "os" + "runtime" + "unsafe" +) + +// Kernel represents VipsKernel type +type Kernel int + +// Kernel enum +const ( + KernelAuto Kernel = -1 + KernelNearest Kernel = C.VIPS_KERNEL_NEAREST + KernelLinear Kernel = C.VIPS_KERNEL_LINEAR + KernelCubic Kernel = C.VIPS_KERNEL_CUBIC + KernelLanczos2 Kernel = C.VIPS_KERNEL_LANCZOS2 + KernelLanczos3 Kernel = C.VIPS_KERNEL_LANCZOS3 + KernelMitchell Kernel = C.VIPS_KERNEL_MITCHELL +) + +// Size represents VipsSize type +type Size int + +const ( + SizeBoth Size = C.VIPS_SIZE_BOTH + SizeUp Size = C.VIPS_SIZE_UP + SizeDown Size = C.VIPS_SIZE_DOWN + SizeForce Size = C.VIPS_SIZE_FORCE + SizeLast Size = C.VIPS_SIZE_LAST +) + +// https://libvips.github.io/libvips/API/current/libvips-resample.html#vips-resize +func vipsResizeWithVScale(in *C.VipsImage, hscale, vscale float64, kernel Kernel) (*C.VipsImage, error) { + incOpCounter("resize") + var out *C.VipsImage + + // libvips recommends Lanczos3 as the default kernel + if kernel == KernelAuto { + kernel = KernelLanczos3 + } + + if err := C.resize_image(in, &out, C.double(hscale), C.double(vscale), C.int(kernel)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +func vipsThumbnail(in *C.VipsImage, width, height int, crop Interesting, size Size) (*C.VipsImage, error) { + incOpCounter("thumbnail") + var out *C.VipsImage + + if err := C.thumbnail_image(in, &out, C.int(width), C.int(height), C.int(crop), C.int(size)); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://www.libvips.org/API/current/libvips-resample.html#vips-thumbnail +func vipsThumbnailFromFile(filename string, width, height int, crop Interesting, size Size, params *ImportParams) (*C.VipsImage, ImageType, error) { + var out *C.VipsImage + + filenameOption := filename + if params != nil { + filenameOption += "[" + params.OptionString() + "]" + } + + cFileName := C.CString(filenameOption) + defer freeCString(cFileName) + + if err := C.thumbnail(cFileName, &out, C.int(width), C.int(height), C.int(crop), C.int(size)); err != 0 { + err := handleImageError(out) + if src, err2 := os.ReadFile(filename); err2 == nil { + if isBMP(src) { + if src2, err3 := bmpToPNG(src); err3 == nil { + return vipsThumbnailFromBuffer(src2, width, height, crop, size, params) + } + } + } + return nil, ImageTypeUnknown, err + } + + imageType := vipsDetermineImageTypeFromMetaLoader(out) + return out, imageType, nil +} + +// https://www.libvips.org/API/current/libvips-resample.html#vips-thumbnail-buffer +func vipsThumbnailFromBuffer(buf []byte, width, height int, crop Interesting, size Size, params *ImportParams) (*C.VipsImage, ImageType, error) { + src := buf + // Reference src here so it's not garbage collected during image initialization. + defer runtime.KeepAlive(src) + + var out *C.VipsImage + + var err C.int + + if params == nil { + err = C.thumbnail_buffer(unsafe.Pointer(&src[0]), C.size_t(len(src)), &out, C.int(width), C.int(height), C.int(crop), C.int(size)) + } else { + cOptionString := C.CString(params.OptionString()) + defer freeCString(cOptionString) + + err = C.thumbnail_buffer_with_option(unsafe.Pointer(&src[0]), C.size_t(len(src)), &out, C.int(width), C.int(height), C.int(crop), C.int(size), cOptionString) + } + if err != 0 { + err := handleImageError(out) + if isBMP(src) { + if src2, err2 := bmpToPNG(src); err2 == nil { + return vipsThumbnailFromBuffer(src2, width, height, crop, size, params) + } + } + return nil, ImageTypeUnknown, err + } + + imageType := vipsDetermineImageTypeFromMetaLoader(out) + return out, imageType, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-resample.html#vips-mapim +func vipsMapim(in *C.VipsImage, index *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("mapim") + var out *C.VipsImage + + if err := C.mapim(in, &out, index); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + +// https://libvips.github.io/libvips/API/current/libvips-histogram.html#vips-maplut +func vipsMaplut(in *C.VipsImage, lut *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("maplut") + var out *C.VipsImage + + if err := C.maplut(in, &out, lut); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/resample.h b/vendor/github.com/davidbyttow/govips/v2/vips/resample.h new file mode 100644 index 000000000..9df5e132c --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/resample.h @@ -0,0 +1,24 @@ +// https://libvips.github.io/libvips/API/current/libvips-resample.html + +#include +#include + +int shrink_image(VipsImage *in, VipsImage **out, double xshrink, + double yshrink); +int reduce_image(VipsImage *in, VipsImage **out, double xshrink, + double yshrink); +int affine_image(VipsImage *in, VipsImage **out, double a, double b, double c, + double d, VipsInterpolate *interpolator); +int resize_image(VipsImage *in, VipsImage **out, double scale, gdouble vscale, + int kernel); +int thumbnail(const char *filename, VipsImage **out, int width, int height, + int crop, int size); +int thumbnail_image(VipsImage *in, VipsImage **out, int width, int height, + int crop, int size); +int thumbnail_buffer(void *buf, size_t len, VipsImage **out, int width, int height, + int crop, int size); +int thumbnail_buffer_with_option(void *buf, size_t len, VipsImage **out, + int width, int height, int crop, int size, + const char *option_string); +int mapim(VipsImage *in, VipsImage **out, VipsImage *index); +int maplut(VipsImage *in, VipsImage **out, VipsImage *lut); diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/stats.go b/vendor/github.com/davidbyttow/govips/v2/vips/stats.go new file mode 100644 index 000000000..d3208c75c --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/stats.go @@ -0,0 +1,56 @@ +package vips + +import "sync" + +// RuntimeStats is a data structure to house a map of govips operation counts +type RuntimeStats struct { + OperationCounts map[string]int64 +} + +var ( + operationCounter chan string + runtimeStats *RuntimeStats + statLock sync.RWMutex +) + +func incOpCounter(op string) { + if operationCounter != nil { + operationCounter <- op + } +} + +func collectStats() chan struct{} { + operationCounter = make(chan string, 100) + done := make(chan struct{}) + exit := false + go func() { + for !exit { + select { + case op := <-operationCounter: + statLock.Lock() + runtimeStats.OperationCounts[op] = runtimeStats.OperationCounts[op] + 1 + statLock.Unlock() + case <-done: + exit = true + break + } + } + }() + return done +} + +// ReadRuntimeStats returns operation counts for govips +func ReadRuntimeStats(stats *RuntimeStats) { + statLock.RLock() + defer statLock.RUnlock() + stats.OperationCounts = make(map[string]int64) + for k, v := range runtimeStats.OperationCounts { + stats.OperationCounts[k] = v + } +} + +func init() { + runtimeStats = &RuntimeStats{ + OperationCounts: make(map[string]int64), + } +} diff --git a/vendor/github.com/davidbyttow/govips/v2/vips/test_resources.go b/vendor/github.com/davidbyttow/govips/v2/vips/test_resources.go new file mode 100644 index 000000000..b2e91e4cd --- /dev/null +++ b/vendor/github.com/davidbyttow/govips/v2/vips/test_resources.go @@ -0,0 +1,6 @@ +package vips + +// relative to "/vips/.." +const ( + resources = "../resources/" +) diff --git a/vendor/modules.txt b/vendor/modules.txt index 13eb87b66..76b654b68 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -745,6 +745,9 @@ github.com/cyphar/filepath-securejoin # github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc ## explicit github.com/davecgh/go-spew/spew +# github.com/davidbyttow/govips/v2 v2.15.0 +## explicit; go 1.15 +github.com/davidbyttow/govips/v2/vips # github.com/deckarep/golang-set v1.8.0 ## explicit; go 1.17 github.com/deckarep/golang-set