Files
Курнат Андрей 2315f25754 Initial QSfera import
2026-06-07 10:20:04 +03:00

1061 lines
29 KiB
Go

package vips
// #cgo CFLAGS: -std=c99
// #include "operations.h"
import "C"
import (
"errors"
"os"
"runtime"
"strings"
"unsafe"
)
// Arithmetic
// 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
if err := C.getpoint(in, &out, C.int(n), C.int(x), C.int(y)); err != 0 {
return nil, handleVipsError()
}
// Copy from C memory into a Go slice, then free the C allocation.
result := make([]float64, n)
copy(result, (*[4]float64)(unsafe.Pointer(out))[:n:n])
gFreePointer(unsafe.Pointer(out))
return result, nil
}
// https://www.libvips.org/API/current/libvips-arithmetic.html#vips-min
func vipsMin(in *C.VipsImage) (float64, int, int, error) {
incOpCounter("min")
var out C.double
var x, y C.int
if err := C.minOp(in, &out, &x, &y, C.int(1)); err != 0 {
return 0, 0, 0, handleVipsError()
}
return float64(out), int(x), int(y), nil
}
// Color
// 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
}
// Composite
// 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
}
// Conversion
// 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
)
// Gravity represents VIPS_GRAVITY type
type Gravity int
// Gravity enum
const (
GravityCentre Gravity = C.VIPS_COMPASS_DIRECTION_CENTRE
GravityNorth Gravity = C.VIPS_COMPASS_DIRECTION_NORTH
GravityEast Gravity = C.VIPS_COMPASS_DIRECTION_EAST
GravitySouth Gravity = C.VIPS_COMPASS_DIRECTION_SOUTH
GravityWest Gravity = C.VIPS_COMPASS_DIRECTION_WEST
GravityNorthEast Gravity = C.VIPS_COMPASS_DIRECTION_NORTH_EAST
GravityNorthWest Gravity = C.VIPS_COMPASS_DIRECTION_NORTH_WEST
GravitySouthEast Gravity = C.VIPS_COMPASS_DIRECTION_SOUTH_EAST
GravitySouthWest Gravity = C.VIPS_COMPASS_DIRECTION_SOUTH_WEST
)
// 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
)
// 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) {
return vipsGenFlip(in, direction)
}
// 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) {
return vipsGenExtractArea(in, left, top, width, height)
}
func vipsExtractAreaMultiPage(in *C.VipsImage, left, top, width, height int) (*C.VipsImage, error) {
incOpCounter("extractAreaMultiPage")
pageHeight := vipsGetPageHeight(in)
nPages := int(in.Ysize) / pageHeight
pages := make([]*C.VipsImage, nPages)
for i := 0; i < nPages; i++ {
page, err := vipsGenExtractArea(in, left, pageHeight*i+top, width, height)
if err != nil {
for j := 0; j < i; j++ {
clearImage(pages[j])
}
return nil, err
}
pages[i] = page
}
across := 1
joined, err := vipsGenArrayjoin(pages, &ArrayjoinOptions{Across: &across})
for _, p := range pages {
clearImage(p)
}
if err != nil {
return nil, err
}
out, err := vipsGenCopy(joined, nil)
clearImage(joined)
if err != nil {
return nil, err
}
vipsSetPageHeight(out, height)
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) {
_, out, _, err := vipsGenSmartcrop(in, width, height, &SmartcropOptions{Interesting: &interesting})
return out, err
}
// 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-composite
func vipsComposite(ins []*C.VipsImage, modes []C.int, xs, ys []C.int) (*C.VipsImage, error) {
if len(ins) == 0 || len(modes) == 0 || len(xs) == 0 || len(ys) == 0 {
return nil, errors.New("vipsComposite: empty input slice")
}
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-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
}
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
}
// Create
type TextWrap int
type TextParams struct {
Text string
Font string
Width int
Height int
Alignment Align
DPI int
RGBA bool
Justify bool
Spacing int
Wrap TextWrap
}
type vipsTextOptions struct {
Text *C.char
Font *C.char
Width C.int
Height C.int
DPI C.int
RGBA C.gboolean
Justify C.gboolean
Spacing C.int
Alignment C.VipsAlign
Wrap C.VipsTextWrap
}
// TextWrap enum
const (
TextWrapWord TextWrap = C.VIPS_TEXT_WRAP_WORD
TextWrapChar TextWrap = C.VIPS_TEXT_WRAP_CHAR
TextWrapWordChar TextWrap = C.VIPS_TEXT_WRAP_WORD_CHAR
TextWrapNone TextWrap = C.VIPS_TEXT_WRAP_NONE
)
// https://libvips.github.io/libvips/API/current/libvips-create.html#vips-text
func vipsText(params *TextParams) (*C.VipsImage, error) {
var out *C.VipsImage
text := C.CString(params.Text)
defer freeCString(text)
font := C.CString(params.Font)
defer freeCString(font)
opts := vipsTextOptions{
Text: text,
Font: font,
Width: C.int(params.Width),
Height: C.int(params.Height),
DPI: C.int(params.DPI),
Alignment: C.VipsAlign(params.Alignment),
Spacing: C.int(params.Spacing),
Wrap: C.VipsTextWrap(params.Wrap),
}
if params.RGBA {
opts.RGBA = C.TRUE
}
if params.Justify {
opts.Justify = C.TRUE
}
err := C.text(&out, (*C.TextOptions)(unsafe.Pointer(&opts)))
if err != 0 {
return nil, handleImageError(out)
}
return out, nil
}
// Draw
// 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
}
// Header
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,
"delay",
"loop",
}
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
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
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
}
func vipsImageGetLoop(in *C.VipsImage) int {
return int(C.get_image_loop(in))
}
func vipsImageSetLoop(in *C.VipsImage, loop int) {
C.set_image_loop(in, C.int(loop))
}
func vipsImageGetBackground(in *C.VipsImage) ([]float64, error) {
incOpCounter("imageGetBackground")
var out *C.double
var n C.int
if err := C.get_background(in, &out, &n); err != 0 {
return nil, handleVipsError()
}
return fromCArrayDouble(out, int(n)), 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
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 func() { freeCString(cFieldValue) }()
if int(C.image_get_as_string(in, cField, &cFieldValue)) == 0 {
return C.GoString(cFieldValue)
}
return ""
}
// Label
// 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
}
// Resample
// 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 {
return vipsThumbnailFromBuffer(src, 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)
return nil, ImageTypeUnknown, err
}
imageType := vipsDetermineImageTypeFromMetaLoader(out)
return out, imageType, nil
}