Skip to content

Commit

Permalink
jxl support (#398)
Browse files Browse the repository at this point in the history
Signed-off-by: songjiayang <[email protected]>
  • Loading branch information
songjiayang authored Jan 21, 2024
1 parent d87b96a commit 9339542
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 2 deletions.
Binary file added resources/jxl-8bit-grey-icc-dot-gain.jxl
Binary file not shown.
25 changes: 24 additions & 1 deletion vips/foreign.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ int set_jp2kload_options(VipsOperation *operation, LoadParams *params) {
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");
Expand Down Expand Up @@ -399,6 +404,19 @@ int set_jp2ksave_options(VipsOperation *operation, SaveParams *params) {
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:
Expand Down Expand Up @@ -431,9 +449,12 @@ int load_from_buffer(LoadParams *params, void *buf, size_t len) {
case AVIF:
return load_buffer("heifload_buffer", buf, len, params,
set_heifload_options);
case JP2K:
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);
}
Expand Down Expand Up @@ -462,6 +483,8 @@ int save_to_buffer(SaveParams *params) {
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);
}
Expand Down
26 changes: 26 additions & 0 deletions vips/foreign.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const (
ImageTypeBMP ImageType = C.BMP
ImageTypeAVIF ImageType = C.AVIF
ImageTypeJP2K ImageType = C.JP2K
ImageTypeJXL ImageType = C.JXL
)

var imageTypeExtensionMap = map[ImageType]string{
Expand All @@ -59,6 +60,7 @@ var imageTypeExtensionMap = map[ImageType]string{
ImageTypeBMP: ".bmp",
ImageTypeAVIF: ".avif",
ImageTypeJP2K: ".jp2",
ImageTypeJXL: ".jxl",
}

// ImageTypes defines the various image types supported by govips
Expand All @@ -75,6 +77,7 @@ var ImageTypes = map[ImageType]string{
ImageTypeBMP: "bmp",
ImageTypeAVIF: "heif",
ImageTypeJP2K: "jp2k",
ImageTypeJXL: "jxl",
}

// TiffCompression represents method for compressing a tiff at export
Expand Down Expand Up @@ -157,6 +160,8 @@ func DetermineImageType(buf []byte) ImageType {
return ImageTypeBMP
} else if isJP2K(buf) {
return ImageTypeJP2K
} else if isJXL(buf) {
return ImageTypeJXL
} else {
return ImageTypeUnknown
}
Expand Down Expand Up @@ -252,6 +257,12 @@ func isJP2K(buf []byte) bool {
return bytes.HasPrefix(buf, jp2kHeader)
}

var jxlHeader = []byte("\xff\x0a")

func isJXL(buf []byte) bool {
return bytes.HasPrefix(buf, jxlHeader)
}

func vipsLoadFromBuffer(buf []byte, params *ImportParams) (*C.VipsImage, ImageType, ImageType, error) {
src := buf
// Reference src here so it's not garbage collected during image initialization.
Expand Down Expand Up @@ -461,6 +472,21 @@ func vipsSaveGIFToBuffer(in *C.VipsImage, params GifExportParams) ([]byte, error
return vipsSaveToBuffer(p)
}

func vipsSaveJxlToBuffer(in *C.VipsImage, params JxlExportParams) ([]byte, error) {
incOpCounter("save_jxl_buffer")

p := C.create_save_params(C.JXL)
p.inputImage = in
p.outputFormat = C.JXL
p.quality = C.int(params.Quality)
p.jxlLossless = C.int(boolToInt(params.Lossless))
p.jxlTier = C.int(params.Tier)
p.jxlDistance = C.double(params.Distance)
p.jxlEffort = C.int(params.Effort)

return vipsSaveToBuffer(p)
}

func vipsSaveToBuffer(params C.struct_SaveParams) ([]byte, error) {
if err := C.save_to_buffer(&params); err != 0 {
return nil, handleSaveBufferError(params.outputBuffer)
Expand Down
9 changes: 8 additions & 1 deletion vips/foreign.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ typedef enum types {
HEIF,
BMP,
AVIF,
JP2K
JP2K,
JXL
} ImageType;

typedef enum ParamType {
Expand Down Expand Up @@ -124,6 +125,12 @@ typedef struct SaveParams {
BOOL jp2kLossless;
int jp2kTileWidth;
int jp2kTileHeight;

// JXL
int jxlTier;
double jxlDistance;
int jxlEffort;
BOOL jxlLossless;
} SaveParams;

SaveParams create_save_params(ImageType outputFormat);
Expand Down
11 changes: 11 additions & 0 deletions vips/foreign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,14 @@ func Test_DetermineImageType__JP2K(t *testing.T) {
imageType := DetermineImageType(buf)
assert.Equal(t, ImageTypeJP2K, imageType)
}

func Test_DetermineImageType__JXL(t *testing.T) {
Startup(&Config{})

buf, err := ioutil.ReadFile(resources + "jxl-8bit-grey-icc-dot-gain.jxl")
assert.NoError(t, err)
assert.NotNil(t, buf)

imageType := DetermineImageType(buf)
assert.Equal(t, ImageTypeJXL, imageType)
}
3 changes: 3 additions & 0 deletions vips/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ func vipsDetermineImageTypeFromMetaLoader(in *C.VipsImage) ImageType {
if strings.HasPrefix(vipsLoader, "jp2k") {
return ImageTypeJP2K
}
if strings.HasPrefix(vipsLoader, "jxl") {
return ImageTypeJXL
}
if strings.HasPrefix(vipsLoader, "magick") {
return ImageTypeMagick
}
Expand Down
40 changes: 40 additions & 0 deletions vips/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,24 @@ func NewJp2kExportParams() *Jp2kExportParams {
}
}

// 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,
}
}

// NewImageFromReader loads an ImageRef from the given reader
func NewImageFromReader(r io.Reader) (*ImageRef, error) {
buf, err := ioutil.ReadAll(r)
Expand Down Expand Up @@ -844,6 +862,12 @@ func (r *ImageRef) Export(params *ExportParams) ([]byte, *ImageMetadata, error)
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{
Expand Down Expand Up @@ -879,6 +903,8 @@ func (r *ImageRef) ExportNative() ([]byte, *ImageMetadata, error) {
return r.ExportJp2k(NewJp2kExportParams())
case ImageTypeGIF:
return r.ExportGIF(NewGifExportParams())
case ImageTypeJXL:
return r.ExportJxl(NewJxlExportParams())
default:
return r.ExportJpeg(NewJpegExportParams())
}
Expand Down Expand Up @@ -999,6 +1025,20 @@ func (r *ImageRef) ExportJp2k(params *Jp2kExportParams) ([]byte, *ImageMetadata,
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))
Expand Down

0 comments on commit 9339542

Please sign in to comment.