From 4468b5b52a5a55d8b5673bc5391fa36f3d3cd7f2 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Fri, 16 Feb 2024 14:56:42 -0500 Subject: [PATCH] Add Parse*Geometry methods (#304) * Implement Parse*Geometry methods This required changing the implementations of GeometryInfo and RectangleInfo to match their C struct counter parts. * Expose QUANTUM_RANGE * Add ParseAbsoluteGeometry(string, *RectangleInfo) * Add ParseRegionGeometry/ParsePageGeometry * More accurately wrap Parse*Geometry APIs * Add func documentation --- imagick/geometry.go | 192 +++++++++++++++++++++++++++++++++++ imagick/geometry_info.go | 19 +++- imagick/magick_wand_image.go | 2 +- imagick/quantum.go | 2 +- imagick/rectangle_info.go | 18 +++- 5 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 imagick/geometry.go diff --git a/imagick/geometry.go b/imagick/geometry.go new file mode 100644 index 0000000..e48d274 --- /dev/null +++ b/imagick/geometry.go @@ -0,0 +1,192 @@ +package imagick + +/* +#include +#include +*/ +import "C" + +// ParseGeometry parses a geometry specification and returns the sigma, +// rho, xi, and psi values. It also returns flags that indicates which +// of the four values (sigma, rho, xi, psi) were located in the string, and +// whether the xi or pi values are negative. +// +// In addition, it reports if there are any of meta characters (%, !, <, >, @, +// and ^) flags present. It does not report the location of the percentage +// relative to the values. +// +// Values may also be separated by commas, colons, or slashes, and offsets. +// Chroma subsampling definitions have to be in the form of a:b:c. Offsets may +// be prefixed by multiple signs to make offset string substitutions easier to +// handle from shell scripts. For example: "-10-10", "-+10-+10", or "+-10+-10" +// will generate negative offsets, while "+10+10", "++10++10", or "--10--10" +// will generate positive offsets. +// +// A description of each parameter follows: +// +// o geometry: The geometry string (e.g. "100x100+10+10"). +// o geometry_info: returns the parsed width/height/x/y in this structure. +func ParseGeometry(geometry string, info *GeometryInfo) uint { + var gi C.GeometryInfo + flags := C.ParseGeometry(C.CString(geometry), &gi) + *info = *newGeometryInfo(&gi) + return uint(flags) +} + +// SetGeometry sets the geometry to its default values. +// +// A description of each parameter follows: +// +// o image: the image. +// o geometry: the geometry. +func SetGeometry(image *Image, info *RectangleInfo) { + var ri C.RectangleInfo + + C.SetGeometry(image.img, &ri) + *info = *newRectangleInfo(&ri) +} + +// ParseAbsoluteGeometry returns a region as defined by the geometry string, +// without any modification by percentages or gravity. +// +// It currently just a wrapper around GetGeometry(), but may be expanded in +// the future to handle other positioning information. +// +// A description of each parameter follows: +// +// o geometry: The geometry string (e.g. "100x100+10+10"). +// o region_info: the region as defined by the geometry string. +func ParseAbsoluteGeometry(geometry string, info *RectangleInfo) uint { + var ri C.RectangleInfo + + ri.x = C.ssize_t(info.X) + ri.y = C.ssize_t(info.Y) + ri.width = C.size_t(info.Width) + ri.height = C.size_t(info.Height) + + flags := C.ParseAbsoluteGeometry(C.CString(geometry), &ri) + *info = *newRectangleInfo(&ri) + + return uint(flags) +} + +// ParseMetaGeometry is similar to GetGeometry() except the returned +// geometry is modified as determined by the meta characters: %, !, <, >, @, +// :, and ^ in relation to image resizing. +// +// Final image dimensions are adjusted so as to preserve the aspect ratio as +// much as possible, while generating a integer (pixel) size, and fitting the +// image within the specified geometry width and height. +// +// Flags are interpreted... +// +// % geometry size is given percentage of original width and height given +// ! do not try to preserve aspect ratio +// < only enlarge images smaller that geometry +// > only shrink images larger than geometry +// @ fit image to contain at most this many pixels +// : width and height denotes an aspect ratio +// ^ contain the given geometry given, (minimal dimensions given) +// +// A description of each parameter follows: +// +// o geometry: The geometry string (e.g. "100x100+10+10"). +// o x,y: The x and y offset, set according to the geometry specification. +// o width,height: The width and height of original image, modified by +// the given geometry specification. +func ParseMetaGeometry(geometry string, x *int, y *int, width *uint, height *uint) uint { + var ri C.RectangleInfo + + ri.x = C.ssize_t(*x) + ri.y = C.ssize_t(*y) + ri.width = C.size_t(*width) + ri.height = C.size_t(*height) + + flags := C.ParseMetaGeometry(C.CString(geometry), &ri.x, &ri.y, &ri.width, &ri.height) + + *x = int(ri.x) + *y = int(ri.y) + *width = uint(ri.width) + *height = uint(ri.height) + + return uint(flags) +} + +// ParseGravityGeometry returns a region as defined by the geometry string +// with respect to the given image page (canvas) dimensions and the images +// gravity setting. +// +// This is typically used for specifying a area within a given image for +// cropping images to a smaller size, chopping out rows and or columns, or +// resizing and positioning overlay images. +// +// Percentages are relative to image size and not page size, and are set to +// nearest integer (pixel) size. +// +// The format of the ParseGravityGeometry method is: +// +// MagickStatusType ParseGravityGeometry(Image *image,const char *geometry, +// RectangleInfo *region_info,ExceptionInfo *exception) +// +// A description of each parameter follows: +// +// o geometry: The geometry string (e.g. "100x100+10+10"). +// o region_info: the region as defined by the geometry string with respect +// to the image dimensions and its gravity. +// o exception: return any errors or warnings in this structure. +func ParseGravityGeometry(image *Image, geometry string, rect *RectangleInfo, exception *ExceptionInfo) uint { + var ri C.RectangleInfo + var ex C.ExceptionInfo + + flags := C.ParseGravityGeometry(image.img, C.CString(geometry), &ri, &ex) + *rect = *newRectangleInfo(&ri) + *exception = *newExceptionInfo(&ex) + + return uint(flags) +} + +// ParsePageGeometry returns a region as defined by the geometry string with +// respect to the image page (canvas) dimensions. +// +// WARNING: Percentage dimensions remain relative to the actual image +// dimensions, and not canvas dimensions. +// +// A description of each parameter follows: +// +// o geometry: The geometry string (e.g. "100x100+10+10"). +// o region_info: the region as defined by the geometry string with +// respect to the image and its gravity. +// o exception: return any errors or warnings in this structure. +func ParsePageGeometry(image *Image, geometry string, rect *RectangleInfo, exception *ExceptionInfo) uint { + var ri C.RectangleInfo + var ex C.ExceptionInfo + + flags := C.ParsePageGeometry(image.img, C.CString(geometry), &ri, &ex) + *rect = *newRectangleInfo(&ri) + *exception = *newExceptionInfo(&ex) + + return uint(flags) +} + +// ParseRegionGeometry returns a region as defined by the geometry string +// with respect to the image dimensions and aspect ratio. +// +// This is basically a wrapper around ParseMetaGeometry. This is typically +// used to parse a geometry string to work out the final integer dimensions +// for image resizing. +// +// A description of each parameter follows: +// +// o geometry: The geometry string (e.g. "100x100+10+10"). +// o region_info: the region as defined by the geometry string. +// o exception: return any errors or warnings in this structure. +func ParseRegionGeometry(image *Image, geometry string, rect *RectangleInfo, exception *ExceptionInfo) uint { + var ri C.RectangleInfo + var ex C.ExceptionInfo + + flags := C.ParseRegionGeometry(image.img, C.CString(geometry), &ri, &ex) + *rect = *newRectangleInfo(&ri) + *exception = *newExceptionInfo(&ex) + + return uint(flags) +} diff --git a/imagick/geometry_info.go b/imagick/geometry_info.go index 3919082..c263880 100644 --- a/imagick/geometry_info.go +++ b/imagick/geometry_info.go @@ -6,5 +6,22 @@ package imagick import "C" type GeometryInfo struct { - gi C.GeometryInfo + Rho, + Sigma, + Xi, + Psi, + Chi float64 +} + +func newGeometryInfo(gi *C.GeometryInfo) *GeometryInfo { + if gi == nil { + return nil + } + return &GeometryInfo{ + Rho: float64(gi.rho), + Sigma: float64(gi.sigma), + Xi: float64(gi.xi), + Psi: float64(gi.psi), + Chi: float64(gi.chi), + } } diff --git a/imagick/magick_wand_image.go b/imagick/magick_wand_image.go index d80e6df..1ca6ff8 100644 --- a/imagick/magick_wand_image.go +++ b/imagick/magick_wand_image.go @@ -2450,7 +2450,7 @@ func (mw *MagickWand) SimilarityImage(reference *MagickWand, metric MetricType, &rectInfo, (*C.double)(&similarity)) runtime.KeepAlive(reference) - return &RectangleInfo{&rectInfo}, similarity, newMagickWand(mwarea) + return newRectangleInfo(&rectInfo), similarity, newMagickWand(mwarea) } // SketchImage Simulates a pencil sketch. We convolve the image with a Gaussian operator diff --git a/imagick/quantum.go b/imagick/quantum.go index 7f9e1ce..0a9a18f 100644 --- a/imagick/quantum.go +++ b/imagick/quantum.go @@ -11,4 +11,4 @@ import "C" type Quantum C.Quantum -//const QUANTUM_RANGE = C.QuantumRange +const QUANTUM_RANGE = C.QuantumRange diff --git a/imagick/rectangle_info.go b/imagick/rectangle_info.go index 3edfc97..4bc6ea4 100644 --- a/imagick/rectangle_info.go +++ b/imagick/rectangle_info.go @@ -10,5 +10,21 @@ package imagick import "C" type RectangleInfo struct { - info *C.RectangleInfo + X int + Y int + Width uint + Height uint +} + +// Create a new RectangleInfo wrapper around a C RectangleInfo ptr +func newRectangleInfo(rectInfo *C.RectangleInfo) *RectangleInfo { + if rectInfo == nil { + return nil + } + return &RectangleInfo{ + X: int(rectInfo.x), + Y: int(rectInfo.y), + Width: uint(rectInfo.width), + Height: uint(rectInfo.height), + } }