diff --git a/Attributes.go b/Attributes.go new file mode 100644 index 0000000..23ae418 --- /dev/null +++ b/Attributes.go @@ -0,0 +1,82 @@ +package imagequant + +import ( + "errors" +) + +/* +#include "libimagequant.h" +*/ +import "C" + +type Attributes struct { + p *C.struct_liq_attr +} + +// Callers MUST call Release() on the returned object to free memory. +func NewAttributes() (*Attributes, error) { + pAttr := C.liq_attr_create() + if pAttr == nil { // nullptr + return nil, errors.New("Unsupported platform") + } + + return &Attributes{p: pAttr}, nil +} + +func (this *Attributes) SetMaxColors(colors int) error { + return translateError(C.liq_set_max_colors(this.p, C.int(colors))) +} + +func (this *Attributes) GetMaxColors() int { + return int(C.liq_get_max_colors(this.p)) +} + +func (this *Attributes) SetQuality(minimum, maximum int) error { + return translateError(C.liq_set_quality(this.p, C.int(minimum), C.int(maximum))) +} + +func (this *Attributes) GetMinQuality() int { + return int(C.liq_get_min_quality(this.p)) +} + +func (this *Attributes) GetMaxQuality() int { + return int(C.liq_get_max_quality(this.p)) +} + +func (this *Attributes) SetSpeed(speed int) error { + return translateError(C.liq_set_speed(this.p, C.int(speed))) +} + +func (this *Attributes) GetSpeed() int { + return int(C.liq_get_speed(this.p)) +} + +func (this *Attributes) SetMinOpacity(min int) error { + return translateError(C.liq_set_min_opacity(this.p, C.int(min))) +} + +func (this *Attributes) GetMinOpacity() int { + return int(C.liq_get_min_opacity(this.p)) +} + +func (this *Attributes) SetMinPosterization(bits int) error { + return translateError(C.liq_set_min_posterization(this.p, C.int(bits))) +} + +func (this *Attributes) GetMinPosterization() int { + return int(C.liq_get_min_posterization(this.p)) +} + +func (this *Attributes) SetLastIndexTransparent(is_last int) { + C.liq_set_last_index_transparent(this.p, C.int(is_last)) +} + +func (this *Attributes) CreateHistogram() *Histogram { + ptr := C.liq_histogram_create(this.p) + return &Histogram{p: ptr} +} + +// Free memory. Callers must not use this object after Release has been called. +func (this *Attributes) Release() { + C.liq_attr_destroy(this.p) +} diff --git a/Color.go b/Color.go new file mode 100644 index 0000000..3922de3 --- /dev/null +++ b/Color.go @@ -0,0 +1,5 @@ +package imagequant + +type Color struct { + r, g, b, a uint8 +} diff --git a/Histogram.go b/Histogram.go new file mode 100644 index 0000000..739f5af --- /dev/null +++ b/Histogram.go @@ -0,0 +1,29 @@ +package imagequant + +/* +#include "libimagequant.h" +*/ +import "C" + +type Histogram struct { + p *C.struct_liq_histogram +} + +func (this *Histogram) AddImage(attr *Attributes, img *Image) error { + return translateError(C.liq_histogram_add_image(this.p, attr.p, img.p)) +} + +func (this *Histogram) Quantize(attr *Attributes) (*Result, error) { + res := Result{} + liqerr := C.liq_histogram_quantize(this.p, attr.p, &res.p) + if liqerr != C.LIQ_OK { + return nil, translateError(liqerr) + } + + return &res, nil +} + +// Free memory. Callers must not use this object after Release has been called. +func (this *Histogram) Release() { + C.liq_histogram_destroy(this.p) +} diff --git a/Image.go b/Image.go new file mode 100644 index 0000000..37e48a1 --- /dev/null +++ b/Image.go @@ -0,0 +1,48 @@ +package imagequant + +import ( + "errors" + "unsafe" +) + +/* +#include "libimagequant.h" +*/ +import "C" + +type Image struct { + p *C.struct_liq_image + w, h int + released bool +} + +// Callers MUST call Release() on the returned object to free memory. +func NewImage(attr *Attributes, rgba32data string, width, height int, gamma float64) (*Image, error) { + pImg := C.liq_image_create_rgba(attr.p, unsafe.Pointer(C.CString(rgba32data)), C.int(width), C.int(height), C.double(gamma)) + if pImg == nil { + return nil, errors.New("Failed to create image (invalid argument)") + } + + return &Image{ + p: pImg, + w: width, + h: height, + released: false, + }, nil +} + +// Free memory. Callers must not use this object after Release has been called. +func (this *Image) Release() { + C.liq_image_destroy(this.p) + this.released = true +} + +func (this *Image) Quantize(attr *Attributes) (*Result, error) { + res := Result{} + liqerr := C.liq_image_quantize(this.p, attr.p, &res.p) + if liqerr != C.LIQ_OK { + return nil, translateError(liqerr) + } + + return &res, nil +} diff --git a/Palette.go b/Palette.go new file mode 100644 index 0000000..7b57320 --- /dev/null +++ b/Palette.go @@ -0,0 +1,29 @@ +package imagequant + +/* +#include "libimagequant.h" +*/ +import "C" + +// This struct has standard Go lifetime and does not need manual release. +type Palette struct { + p C.struct_liq_palette +} + +func (this *Palette) Count() uint { + return uint(this.p.count) +} + +func (this *Palette) At(idx int) (Color, error) { + if idx < 0 || idx >= int(this.Count()) { + return Color{}, ErrValueOutOfRange + } + + return Color{ + r: uint8(this.p.entries[idx].r), + g: uint8(this.p.entries[idx].g), + b: uint8(this.p.entries[idx].b), + a: uint8(this.p.entries[idx].a), + }, nil + +} diff --git a/Result.go b/Result.go new file mode 100644 index 0000000..eb156d0 --- /dev/null +++ b/Result.go @@ -0,0 +1,79 @@ +package imagequant + +/* +#include "libimagequant.h" +*/ +import "C" + +// Callers must not use this object once Release has been called on the parent +// Image struct. +type Result struct { + p *C.struct_liq_result + im *Image +} + +func (this *Result) SetDitheringLevel(dither_level float32) error { + return translateError(C.liq_set_dithering_level(this.p, C.float(dither_level))) +} + +func (this *Result) GetQuantizationError() float64 { + return float64(C.liq_get_quantization_error(this.p)) +} + +func (this *Result) GetRemappingError() float64 { + return float64(C.liq_get_remapping_error(this.p)) +} + +func (this *Result) GetQuantizationQuality() float64 { + return float64(C.liq_get_quantization_quality(this.p)) +} + +func (this *Result) GetRemappingQuality() float64 { + return float64(C.liq_get_remapping_quality(this.p)) +} + +func (this *Result) SetOutputGamma(gamma float64) error { + return translateError(C.liq_set_output_gamma(this.p, C.double(gamma))) +} + +func (this *Result) GetImageWidth() int { + // C.liq_image_get_width + return this.im.w +} + +func (this *Result) GetImageHeight() int { + // C.liq_image_get_height + return this.im.h +} + +func (this *Result) GetOutputGamma() float64 { + return float64(C.liq_get_output_gamma(this.p)) +} + +func (this *Result) WriteRemappedImage() ([]byte, error) { + if this.im.released { + return nil, ErrUseAfterFree + } + + buff_size := this.im.w * this.im.h + buff := make([]byte, buff_size) + + // n.b. C.CBytes() added in go1.7.3 + + iqe := C.liq_write_remapped_image(this.p, this.im.p, C.CBytes(buff), C.size_t(buff_size)) + if iqe != C.LIQ_OK { + return nil, translateError(iqe) + } + + return buff, nil +} + +func (this *Result) GetPalette() *Palette { + ptr := *C.liq_get_palette(this.p) // copy struct content + return &Palette{p: ptr} +} + +// Free memory. Callers must not use this object after Release has been called. +func (this *Result) Release() { + C.liq_result_destroy(this.p) +} diff --git a/imagequant.go b/imagequant.go index 5e76aa0..9c42c23 100644 --- a/imagequant.go +++ b/imagequant.go @@ -2,7 +2,6 @@ package imagequant import ( "errors" - "unsafe" ) /* @@ -60,218 +59,8 @@ func GetLibraryVersionString() string { return C.GoString(C.liqVersionString()) } -type Attributes struct { - p *C.struct_liq_attr -} - -// Callers MUST call Release() on the returned object to free memory. -func NewAttributes() (*Attributes, error) { - pAttr := C.liq_attr_create() - if pAttr == nil { // nullptr - return nil, errors.New("Unsupported platform") - } - - return &Attributes{p: pAttr}, nil -} - -func (this *Attributes) SetMaxColors(colors int) error { - return translateError(C.liq_set_max_colors(this.p, C.int(colors))) -} - -func (this *Attributes) GetMaxColors() int { - return int(C.liq_get_max_colors(this.p)) -} - -func (this *Attributes) SetQuality(minimum, maximum int) error { - return translateError(C.liq_set_quality(this.p, C.int(minimum), C.int(maximum))) -} - -func (this *Attributes) GetMinQuality() int { - return int(C.liq_get_min_quality(this.p)) -} - -func (this *Attributes) GetMaxQuality() int { - return int(C.liq_get_max_quality(this.p)) -} - const ( SPEED_SLOWEST = 1 SPEED_DEFAULT = 3 SPEED_FASTEST = 10 ) - -func (this *Attributes) SetSpeed(speed int) error { - return translateError(C.liq_set_speed(this.p, C.int(speed))) -} - -func (this *Attributes) GetSpeed() int { - return int(C.liq_get_speed(this.p)) -} - -func (this *Attributes) SetMinOpacity(min int) error { - return translateError(C.liq_set_min_opacity(this.p, C.int(min))) -} - -func (this *Attributes) GetMinOpacity() int { - return int(C.liq_get_min_opacity(this.p)) -} - -func (this *Attributes) SetMinPosterization(bits int) error { - return translateError(C.liq_set_min_posterization(this.p, C.int(bits))) -} - -func (this *Attributes) GetMinPosterization() int { - return int(C.liq_get_min_posterization(this.p)) -} - -func (this *Attributes) SetLastIndexTransparent(is_last int) { - C.liq_set_last_index_transparent(this.p, C.int(is_last)) -} - -// Free memory. Callers must not use this object after Release has been called. -func (this *Attributes) Release() { - C.liq_attr_destroy(this.p) -} - -type Histogram struct { - p *C.struct_liq_histogram -} - -func (this *Attributes) CreateHistogram() *Histogram { - ptr := C.liq_histogram_create(this.p) - return &Histogram{p: ptr} -} - -// Free memory. Callers must not use this object after Release has been called. -func (this *Histogram) Release() { - C.liq_histogram_destroy(this.p) -} - -func (this *Histogram) AddImage(attr *Attributes, img *Image) error { - return translateError(C.liq_histogram_add_image(this.p, attr.p, img.p)) -} - -func (this *Histogram) Quantize(attr *Attributes) (*Result, error) { - res := Result{} - liqerr := C.liq_histogram_quantize(this.p, attr.p, &res.p) - if liqerr != C.LIQ_OK { - return nil, translateError(liqerr) - } - - return &res, nil -} - -type Image struct { - p *C.struct_liq_image - w, h int - released bool -} - -// Callers MUST call Release() on the returned object to free memory. -func NewImage(attr *Attributes, rgba32data string, width, height int, gamma float64) (*Image, error) { - pImg := C.liq_image_create_rgba(attr.p, unsafe.Pointer(C.CString(rgba32data)), C.int(width), C.int(height), C.double(gamma)) - if pImg == nil { - return nil, errors.New("Failed to create image (invalid argument)") - } - - return &Image{ - p: pImg, - w: width, - h: height, - released: false, - }, nil -} - -// Free memory. Callers must not use this object after Release has been called. -func (this *Image) Release() { - C.liq_image_destroy(this.p) - this.released = true -} - -// Callers must not use this object once Release has been called on the parent -// Image struct. -type Result struct { - p *C.struct_liq_result - im *Image -} - -func (this *Image) Quantize(attr *Attributes) (*Result, error) { - res := Result{} - liqerr := C.liq_image_quantize(this.p, attr.p, &res.p) - if liqerr != C.LIQ_OK { - return nil, translateError(liqerr) - } - - return &res, nil -} - -func (this *Result) SetDitheringLevel(dither_level float32) error { - return translateError(C.liq_set_dithering_level(this.p, C.float(dither_level))) -} - -func (this *Result) GetQuantizationError() float64 { - return float64(C.liq_get_quantization_error(this.p)) -} - -func (this *Result) GetRemappingError() float64 { - return float64(C.liq_get_remapping_error(this.p)) -} - -func (this *Result) GetQuantizationQuality() float64 { - return float64(C.liq_get_quantization_quality(this.p)) -} - -func (this *Result) GetRemappingQuality() float64 { - return float64(C.liq_get_remapping_quality(this.p)) -} - -func (this *Result) SetOutputGamma(gamma float64) error { - return translateError(C.liq_set_output_gamma(this.p, C.double(gamma))) -} - -func (this *Result) GetImageWidth() int { - // C.liq_image_get_width - return this.im.w -} - -func (this *Result) GetImageHeight() int { - // C.liq_image_get_height - return this.im.h -} - -func (this *Result) GetOutputGamma() float64 { - return float64(C.liq_get_output_gamma(this.p)) -} - -// Free memory. Callers must not use this object after Release has been called. -func (this *Result) Release() { - C.liq_result_destroy(this.p) -} - -func (this *Result) WriteRemappedImage() ([]byte, error) { - if this.im.released { - return nil, ErrUseAfterFree - } - - buff_size := this.im.w * this.im.h - buff := make([]byte, buff_size) - - // n.b. C.CBytes() added in go1.7.3 - - iqe := C.liq_write_remapped_image(this.p, this.im.p, C.CBytes(buff), C.size_t(buff_size)) - if iqe != C.LIQ_OK { - return nil, translateError(iqe) - } - - return buff, nil -} - -// This struct has standard Go lifetime and does not need manual release. -type Palette struct { - p C.struct_liq_palette -} - -func (this *Result) GetPalette() *Palette { - ptr := *C.liq_get_palette(this.p) // copy struct content - return &Palette{p: ptr} -}