mirror of
https://github.com/talgo-cloud/bimg.git
synced 2026-03-16 19:05:54 -07:00
commit
931a857edf
5 changed files with 150 additions and 50 deletions
53
type.go
53
type.go
|
|
@ -51,23 +51,28 @@ var ImageTypes = map[ImageType]string{
|
||||||
// for SupportedImageTypes map.
|
// for SupportedImageTypes map.
|
||||||
var imageMutex = &sync.RWMutex{}
|
var imageMutex = &sync.RWMutex{}
|
||||||
|
|
||||||
|
// SupportedImageType represents whether a type can be loaded and/or saved by
|
||||||
|
// the current libvips compilation.
|
||||||
|
type SupportedImageType struct {
|
||||||
|
Load bool
|
||||||
|
Save bool
|
||||||
|
}
|
||||||
|
|
||||||
// SupportedImageTypes stores the optional image type supported
|
// SupportedImageTypes stores the optional image type supported
|
||||||
// by the current libvips compilation.
|
// by the current libvips compilation.
|
||||||
// Note: lazy evaluation as demand is required due
|
// Note: lazy evaluation as demand is required due
|
||||||
// to bootstrap runtime limitation with C/libvips world.
|
// to bootstrap runtime limitation with C/libvips world.
|
||||||
var SupportedImageTypes = map[ImageType]bool{}
|
var SupportedImageTypes = map[ImageType]SupportedImageType{}
|
||||||
|
|
||||||
// discoverSupportedImageTypes is used to fill SupportedImageTypes map.
|
// discoverSupportedImageTypes is used to fill SupportedImageTypes map.
|
||||||
func discoverSupportedImageTypes() {
|
func discoverSupportedImageTypes() {
|
||||||
imageMutex.Lock()
|
imageMutex.Lock()
|
||||||
SupportedImageTypes[JPEG] = VipsIsTypeSupported(JPEG)
|
for imageType := range ImageTypes {
|
||||||
SupportedImageTypes[PNG] = VipsIsTypeSupported(PNG)
|
SupportedImageTypes[imageType] = SupportedImageType{
|
||||||
SupportedImageTypes[GIF] = VipsIsTypeSupported(GIF)
|
Load: VipsIsTypeSupported(imageType),
|
||||||
SupportedImageTypes[WEBP] = VipsIsTypeSupported(WEBP)
|
Save: VipsIsTypeSupportedSave(imageType),
|
||||||
SupportedImageTypes[SVG] = VipsIsTypeSupported(SVG)
|
}
|
||||||
SupportedImageTypes[TIFF] = VipsIsTypeSupported(TIFF)
|
}
|
||||||
SupportedImageTypes[PDF] = VipsIsTypeSupported(PDF)
|
|
||||||
SupportedImageTypes[MAGICK] = VipsIsTypeSupported(MAGICK)
|
|
||||||
imageMutex.Unlock()
|
imageMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,7 +107,7 @@ func DetermineImageTypeName(buf []byte) string {
|
||||||
|
|
||||||
// IsImageTypeSupportedByVips returns true if the given image type
|
// IsImageTypeSupportedByVips returns true if the given image type
|
||||||
// is supported by current libvips compilation.
|
// is supported by current libvips compilation.
|
||||||
func IsImageTypeSupportedByVips(t ImageType) bool {
|
func IsImageTypeSupportedByVips(t ImageType) SupportedImageType {
|
||||||
imageMutex.RLock()
|
imageMutex.RLock()
|
||||||
|
|
||||||
// Discover supported image types and cache the result
|
// Discover supported image types and cache the result
|
||||||
|
|
@ -113,25 +118,45 @@ func IsImageTypeSupportedByVips(t ImageType) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if image type is actually supported
|
// Check if image type is actually supported
|
||||||
isSupported, ok := SupportedImageTypes[t]
|
supported, ok := SupportedImageTypes[t]
|
||||||
if !itShouldDiscover {
|
if !itShouldDiscover {
|
||||||
imageMutex.RUnlock()
|
imageMutex.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
return ok && isSupported
|
if ok {
|
||||||
|
return supported
|
||||||
|
}
|
||||||
|
return SupportedImageType{Load: false, Save: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsTypeSupported checks if a given image type is supported
|
// IsTypeSupported checks if a given image type is supported
|
||||||
func IsTypeSupported(t ImageType) bool {
|
func IsTypeSupported(t ImageType) bool {
|
||||||
_, ok := ImageTypes[t]
|
_, ok := ImageTypes[t]
|
||||||
return ok && IsImageTypeSupportedByVips(t)
|
return ok && IsImageTypeSupportedByVips(t).Load
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsTypeNameSupported checks if a given image type name is supported
|
// IsTypeNameSupported checks if a given image type name is supported
|
||||||
func IsTypeNameSupported(t string) bool {
|
func IsTypeNameSupported(t string) bool {
|
||||||
for imageType, name := range ImageTypes {
|
for imageType, name := range ImageTypes {
|
||||||
if name == t {
|
if name == t {
|
||||||
return IsImageTypeSupportedByVips(imageType)
|
return IsImageTypeSupportedByVips(imageType).Load
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTypeSupportedSave checks if a given image type is support for saving
|
||||||
|
func IsTypeSupportedSave(t ImageType) bool {
|
||||||
|
_, ok := ImageTypes[t]
|
||||||
|
return ok && IsImageTypeSupportedByVips(t).Save
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTypeNameSupportedSave checks if a given image type name is supported for
|
||||||
|
// saving
|
||||||
|
func IsTypeNameSupportedSave(t string) bool {
|
||||||
|
for imageType, name := range ImageTypes {
|
||||||
|
if name == t {
|
||||||
|
return IsImageTypeSupportedByVips(imageType).Save
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
41
type_test.go
41
type_test.go
|
|
@ -66,7 +66,7 @@ func TestIsTypeSupported(t *testing.T) {
|
||||||
|
|
||||||
for _, n := range types {
|
for _, n := range types {
|
||||||
if IsTypeSupported(n.name) == false {
|
if IsTypeSupported(n.name) == false {
|
||||||
t.Fatal("Image type is not valid")
|
t.Fatalf("Image type %#v is not valid", ImageTypes[n.name])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +85,44 @@ func TestIsTypeNameSupported(t *testing.T) {
|
||||||
|
|
||||||
for _, n := range types {
|
for _, n := range types {
|
||||||
if IsTypeNameSupported(n.name) != n.expected {
|
if IsTypeNameSupported(n.name) != n.expected {
|
||||||
t.Fatal("Image type is not valid")
|
t.Fatalf("Image type %#v is not valid", n.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsTypeSupportedSave(t *testing.T) {
|
||||||
|
types := []struct {
|
||||||
|
name ImageType
|
||||||
|
}{
|
||||||
|
{JPEG}, {PNG}, {WEBP},
|
||||||
|
}
|
||||||
|
if VipsVersion >= "8.5.0" {
|
||||||
|
types = append(types, struct{ name ImageType }{TIFF})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range types {
|
||||||
|
if IsTypeSupportedSave(n.name) == false {
|
||||||
|
t.Fatalf("Image type %#v is not valid", ImageTypes[n.name])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsTypeNameSupportedSave(t *testing.T) {
|
||||||
|
types := []struct {
|
||||||
|
name string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"jpeg", true},
|
||||||
|
{"png", true},
|
||||||
|
{"webp", true},
|
||||||
|
{"gif", false},
|
||||||
|
{"pdf", false},
|
||||||
|
{"tiff", VipsVersion >= "8.5.0"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range types {
|
||||||
|
if IsTypeNameSupportedSave(n.name) != n.expected {
|
||||||
|
t.Fatalf("Image type %#v is not valid", n.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
vips.go
48
vips.go
|
|
@ -8,6 +8,7 @@ import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
@ -167,6 +168,25 @@ func VipsIsTypeSupported(t ImageType) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VipsIsTypeSupportedSave returns true if the given image type
|
||||||
|
// is supported by the current libvips compilation for the
|
||||||
|
// save operation.
|
||||||
|
func VipsIsTypeSupportedSave(t ImageType) bool {
|
||||||
|
if t == JPEG {
|
||||||
|
return int(C.vips_type_find_save_bridge(C.JPEG)) != 0
|
||||||
|
}
|
||||||
|
if t == WEBP {
|
||||||
|
return int(C.vips_type_find_save_bridge(C.WEBP)) != 0
|
||||||
|
}
|
||||||
|
if t == PNG {
|
||||||
|
return int(C.vips_type_find_save_bridge(C.PNG)) != 0
|
||||||
|
}
|
||||||
|
if t == TIFF {
|
||||||
|
return int(C.vips_type_find_save_bridge(C.TIFF)) != 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func vipsExifOrientation(image *C.VipsImage) int {
|
func vipsExifOrientation(image *C.VipsImage) int {
|
||||||
return int(C.vips_exif_orientation(image))
|
return int(C.vips_exif_orientation(image))
|
||||||
}
|
}
|
||||||
|
|
@ -366,25 +386,19 @@ func vipsSave(image *C.VipsImage, o vipsSaveOptions) ([]byte, error) {
|
||||||
interlace := C.int(boolToInt(o.Interlace))
|
interlace := C.int(boolToInt(o.Interlace))
|
||||||
quality := C.int(o.Quality)
|
quality := C.int(o.Quality)
|
||||||
|
|
||||||
|
if o.Type != 0 && !IsTypeSupportedSave(o.Type) {
|
||||||
|
return nil, fmt.Errorf("VIPS cannot save to %#v", ImageTypes[o.Type])
|
||||||
|
}
|
||||||
var ptr unsafe.Pointer
|
var ptr unsafe.Pointer
|
||||||
switch o.Type {
|
switch o.Type {
|
||||||
case WEBP:
|
case WEBP:
|
||||||
saveErr = C.vips_webpsave_bridge(tmpImage, &ptr, &length, 1, quality)
|
saveErr = C.vips_webpsave_bridge(tmpImage, &ptr, &length, 1, quality)
|
||||||
break
|
|
||||||
case PNG:
|
case PNG:
|
||||||
saveErr = C.vips_pngsave_bridge(tmpImage, &ptr, &length, 1, C.int(o.Compression), quality, interlace)
|
saveErr = C.vips_pngsave_bridge(tmpImage, &ptr, &length, 1, C.int(o.Compression), quality, interlace)
|
||||||
break
|
case TIFF:
|
||||||
case GIF:
|
saveErr = C.vips_tiffsave_bridge(tmpImage, &ptr, &length)
|
||||||
return nil, errors.New("VIPS cannot save to GIF")
|
|
||||||
case PDF:
|
|
||||||
return nil, errors.New("VIPS cannot save to PDF")
|
|
||||||
case SVG:
|
|
||||||
return nil, errors.New("VIPS cannot save to SVG")
|
|
||||||
case MAGICK:
|
|
||||||
return nil, errors.New("VIPS cannot save to MAGICK")
|
|
||||||
default:
|
default:
|
||||||
saveErr = C.vips_jpegsave_bridge(tmpImage, &ptr, &length, 1, quality, interlace)
|
saveErr = C.vips_jpegsave_bridge(tmpImage, &ptr, &length, 1, quality, interlace)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(saveErr) != 0 {
|
if int(saveErr) != 0 {
|
||||||
|
|
@ -506,24 +520,24 @@ func vipsImageType(buf []byte) ImageType {
|
||||||
if buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF {
|
if buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF {
|
||||||
return JPEG
|
return JPEG
|
||||||
}
|
}
|
||||||
if IsImageTypeSupportedByVips(WEBP) && buf[8] == 0x57 && buf[9] == 0x45 && buf[10] == 0x42 && buf[11] == 0x50 {
|
if IsTypeSupported(WEBP) && buf[8] == 0x57 && buf[9] == 0x45 && buf[10] == 0x42 && buf[11] == 0x50 {
|
||||||
return WEBP
|
return WEBP
|
||||||
}
|
}
|
||||||
if IsImageTypeSupportedByVips(TIFF) &&
|
if IsTypeSupported(TIFF) &&
|
||||||
((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
|
((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
|
||||||
(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) {
|
(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) {
|
||||||
return TIFF
|
return TIFF
|
||||||
}
|
}
|
||||||
if IsImageTypeSupportedByVips(GIF) && buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46 {
|
if IsTypeSupported(GIF) && buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46 {
|
||||||
return GIF
|
return GIF
|
||||||
}
|
}
|
||||||
if IsImageTypeSupportedByVips(PDF) && buf[0] == 0x25 && buf[1] == 0x50 && buf[2] == 0x44 && buf[3] == 0x46 {
|
if IsTypeSupported(PDF) && buf[0] == 0x25 && buf[1] == 0x50 && buf[2] == 0x44 && buf[3] == 0x46 {
|
||||||
return PDF
|
return PDF
|
||||||
}
|
}
|
||||||
if IsImageTypeSupportedByVips(SVG) && IsSVGImage(buf) {
|
if IsTypeSupported(SVG) && IsSVGImage(buf) {
|
||||||
return SVG
|
return SVG
|
||||||
}
|
}
|
||||||
if IsImageTypeSupportedByVips(MAGICK) && strings.HasSuffix(readImageType(buf), "MagickBuffer") {
|
if IsTypeSupported(MAGICK) && strings.HasSuffix(readImageType(buf), "MagickBuffer") {
|
||||||
return MAGICK
|
return MAGICK
|
||||||
}
|
}
|
||||||
return UNKNOWN
|
return UNKNOWN
|
||||||
|
|
|
||||||
28
vips.h
28
vips.h
|
|
@ -144,6 +144,23 @@ vips_type_find_bridge(int t) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
vips_type_find_save_bridge(int t) {
|
||||||
|
if (t == TIFF) {
|
||||||
|
return vips_type_find("VipsOperation", "tiffsave_buffer");
|
||||||
|
}
|
||||||
|
if (t == WEBP) {
|
||||||
|
return vips_type_find("VipsOperation", "webpsave_buffer");
|
||||||
|
}
|
||||||
|
if (t == PNG) {
|
||||||
|
return vips_type_find("VipsOperation", "pngsave_buffer");
|
||||||
|
}
|
||||||
|
if (t == JPEG) {
|
||||||
|
return vips_type_find("VipsOperation", "jpegsave_buffer");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vips_rotate(VipsImage *in, VipsImage **out, int angle) {
|
vips_rotate(VipsImage *in, VipsImage **out, int angle) {
|
||||||
int rotate = VIPS_ANGLE_D0;
|
int rotate = VIPS_ANGLE_D0;
|
||||||
|
|
@ -276,6 +293,17 @@ vips_webpsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int qual
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
vips_tiffsave_bridge(VipsImage *in, void **buf, size_t *len) {
|
||||||
|
#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 5)
|
||||||
|
return vips_tiffsave_buffer(in, buf, len,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vips_is_16bit (VipsInterpretation interpretation) {
|
vips_is_16bit (VipsInterpretation interpretation) {
|
||||||
return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;
|
return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;
|
||||||
|
|
|
||||||
30
vips_test.go
30
vips_test.go
|
|
@ -29,7 +29,7 @@ func TestVipsRead(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVipsSave(t *testing.T) {
|
func TestVipsSave(t *testing.T) {
|
||||||
types := [...]ImageType{JPEG, PNG, TIFF, WEBP}
|
types := [...]ImageType{JPEG, PNG, WEBP}
|
||||||
|
|
||||||
for _, typ := range types {
|
for _, typ := range types {
|
||||||
image, _, _ := vipsRead(readImage("test.jpg"))
|
image, _, _ := vipsRead(readImage("test.jpg"))
|
||||||
|
|
@ -45,20 +45,16 @@ func TestVipsSave(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVipsCannotSave(t *testing.T) {
|
func TestVipsSaveTiff(t *testing.T) {
|
||||||
types := [...]ImageType{GIF, PDF, SVG, MAGICK}
|
if !IsTypeSupportedSave(TIFF) {
|
||||||
|
t.Skipf("Format %#v is not supported", ImageTypes[TIFF])
|
||||||
|
}
|
||||||
|
image, _, _ := vipsRead(readImage("test.jpg"))
|
||||||
|
options := vipsSaveOptions{Quality: 95, Type: TIFF}
|
||||||
|
buf, _ := vipsSave(image, options)
|
||||||
|
|
||||||
for _, typ := range types {
|
if len(buf) == 0 {
|
||||||
image, _, _ := vipsRead(readImage("test.jpg"))
|
t.Fatalf("Empty saved '%v' image", ImageTypes[TIFF])
|
||||||
options := vipsSaveOptions{Quality: 95, Type: typ}
|
|
||||||
|
|
||||||
buf, err := vipsSave(image, options)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Format '%v' shouldn't be supported", ImageTypes[typ])
|
|
||||||
}
|
|
||||||
if len(buf) != 0 {
|
|
||||||
t.Fatalf("'%v' image is not empty", ImageTypes[typ])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,9 +114,9 @@ func TestVipsWatermark(t *testing.T) {
|
||||||
t.Errorf("Cannot add watermark: %s", err)
|
t.Errorf("Cannot add watermark: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, _ := vipsSave(newImg, vipsSaveOptions{Quality: 95})
|
buf, err := vipsSave(newImg, vipsSaveOptions{Quality: 95})
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 || err != nil {
|
||||||
t.Fatal("Empty image")
|
t.Fatalf("Empty image. %#v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue