From 252b02dd8b34a1b7613e25a0ea33c28394b997de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Kov=C3=A1=C5=99?= Date: Fri, 1 Mar 2024 22:32:36 +0100 Subject: [PATCH 1/2] Add JPEG XL import/export with OpenImageIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Kovář --- .../blender/blenkernel/intern/image_format.cc | 26 ++++++++++ source/blender/imbuf/CMakeLists.txt | 1 + source/blender/imbuf/IMB_imbuf_enums.h | 1 + source/blender/imbuf/intern/IMB_filetype.hh | 13 +++++ source/blender/imbuf/intern/filetype.cc | 12 +++++ source/blender/imbuf/intern/format_jxl.cc | 50 +++++++++++++++++++ source/blender/imbuf/intern/util.cc | 2 +- source/blender/makesdna/DNA_scene_types.h | 2 +- source/blender/makesrna/intern/rna_scene.cc | 3 ++ 9 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 source/blender/imbuf/intern/format_jxl.cc diff --git a/source/blender/blenkernel/intern/image_format.cc b/source/blender/blenkernel/intern/image_format.cc index e65250e8fb2..23dd37e611a 100644 --- a/source/blender/blenkernel/intern/image_format.cc +++ b/source/blender/blenkernel/intern/image_format.cc @@ -120,6 +120,9 @@ int BKE_imtype_to_ftype(const char imtype, ImbFormatOptions *r_options) return IMB_FTYPE_WEBP; } #endif + if (imtype == R_IMF_IMTYPE_JXL) { + return IMB_FTYPE_JXL; + } r_options->quality = 90; return IMB_FTYPE_JPG; @@ -176,6 +179,9 @@ char BKE_ftype_to_imtype(const int ftype, const ImbFormatOptions *options) return R_IMF_IMTYPE_WEBP; } #endif + if (ftype == IMB_FTYPE_JXL) { + return R_IMF_IMTYPE_JXL; + } return R_IMF_IMTYPE_JPEG90; } @@ -211,6 +217,7 @@ bool BKE_imtype_supports_quality(const char imtype) case R_IMF_IMTYPE_JP2: case R_IMF_IMTYPE_AVIJPEG: case R_IMF_IMTYPE_WEBP: + case R_IMF_IMTYPE_JXL: return true; } return false; @@ -224,6 +231,7 @@ bool BKE_imtype_requires_linear_float(const char imtype) case R_IMF_IMTYPE_RADHDR: case R_IMF_IMTYPE_OPENEXR: case R_IMF_IMTYPE_MULTILAYER: + case R_IMF_IMTYPE_JXL: return true; } return false; @@ -251,6 +259,7 @@ char BKE_imtype_valid_channels(const char imtype, bool write_file) case R_IMF_IMTYPE_JP2: case R_IMF_IMTYPE_DPX: case R_IMF_IMTYPE_WEBP: + case R_IMF_IMTYPE_JXL: chan_flag |= IMA_CHAN_FLAG_RGBA; break; } @@ -264,6 +273,7 @@ char BKE_imtype_valid_channels(const char imtype, bool write_file) case R_IMF_IMTYPE_RAWTGA: case R_IMF_IMTYPE_TIFF: case R_IMF_IMTYPE_IRIS: + case R_IMF_IMTYPE_JXL: chan_flag |= IMA_CHAN_FLAG_BW; break; } @@ -291,6 +301,8 @@ char BKE_imtype_valid_depths(const char imtype) return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16; case R_IMF_IMTYPE_PNG: return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_16; + case R_IMF_IMTYPE_JXL: + return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_10 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_32; /* Most formats are 8bit only. */ default: return R_IMF_CHAN_DEPTH_8; @@ -308,6 +320,9 @@ char BKE_imtype_from_arg(const char *imtype_arg) if (STREQ(imtype_arg, "JPEG")) { return R_IMF_IMTYPE_JPEG90; } + if (STREQ(imtype_arg, "JXL")) { + return R_IMF_IMTYPE_JXL; + } if (STREQ(imtype_arg, "RAWTGA")) { return R_IMF_IMTYPE_RAWTGA; } @@ -450,6 +465,9 @@ static int image_path_ext_from_imformat_impl(const char imtype, r_ext[ext_num++] = ".webp"; } #endif + else if (imtype == R_IMF_IMTYPE_JXL) { + r_ext[ext_num++] = ".jxl"; + } else { /* Handles: #R_IMF_IMTYPE_AVIRAW, #R_IMF_IMTYPE_AVIJPEG, #R_IMF_IMTYPE_JPEG90 etc. */ r_ext[ext_num++] = ".jpg"; @@ -711,6 +729,10 @@ void BKE_image_format_to_imbuf(ImBuf *ibuf, const ImageFormatData *imf) ibuf->foptions.quality = quality; } #endif + else if (imtype == R_IMF_IMTYPE_JXL) { + ibuf->ftype = IMB_FTYPE_JXL; + ibuf->foptions.quality = quality; + } else { /* #R_IMF_IMTYPE_JPEG90, etc. default to JPEG. */ if (quality < 10) { @@ -839,6 +861,10 @@ void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf) im_format->quality = quality; } #endif + else if (ftype == IMB_FTYPE_JXL) { + im_format->imtype = R_IMF_IMTYPE_JXL; + im_format->quality = quality; + } else { im_format->imtype = R_IMF_IMTYPE_JPEG90; diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index c2ca3f2f3a7..71086e8994b 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -29,6 +29,7 @@ set(SRC intern/format_bmp.cc intern/format_dds.cc intern/format_hdr.cc + intern/format_jxl.cc intern/format_png.cc intern/format_psd.cc intern/format_svg.cc diff --git a/source/blender/imbuf/IMB_imbuf_enums.h b/source/blender/imbuf/IMB_imbuf_enums.h index 72cf36008a6..aa4d5e13ae6 100644 --- a/source/blender/imbuf/IMB_imbuf_enums.h +++ b/source/blender/imbuf/IMB_imbuf_enums.h @@ -43,6 +43,7 @@ enum eImbFileType { #ifdef WITH_WEBP IMB_FTYPE_WEBP = 14, #endif + IMB_FTYPE_JXL = 15, }; typedef enum IMB_Timecode_Type { diff --git a/source/blender/imbuf/intern/IMB_filetype.hh b/source/blender/imbuf/intern/IMB_filetype.hh index d485ad9d9eb..0695701e9de 100644 --- a/source/blender/imbuf/intern/IMB_filetype.hh +++ b/source/blender/imbuf/intern/IMB_filetype.hh @@ -149,6 +149,19 @@ ImBuf *imb_thumbnail_jpeg(const char *filepath, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Format: JPEG XL (#IMB_FTYPE_JXL) + * \{ */ + +bool imb_is_a_jxl(const unsigned char *buf, size_t size); +ImBuf *imb_load_jxl(const unsigned char *mem, + size_t size, + int flags, + char colorspace[IM_MAX_SPACE]); +bool imb_save_jxl(ImBuf *ibuf, const char *filepath, int flags); + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Format: BMP (#IMB_FTYPE_BMP) * \{ */ diff --git a/source/blender/imbuf/intern/filetype.cc b/source/blender/imbuf/intern/filetype.cc index 59129a67b9c..5802f39faee 100644 --- a/source/blender/imbuf/intern/filetype.cc +++ b/source/blender/imbuf/intern/filetype.cc @@ -34,6 +34,18 @@ const ImFileType IMB_FILE_TYPES[] = { /*filetype*/ IMB_FTYPE_JPG, /*default_save_role*/ COLOR_ROLE_DEFAULT_BYTE, }, + { + /*init*/ nullptr, + /*exit*/ nullptr, + /*is_a*/ imb_is_a_jxl, + /*load*/ imb_load_jxl, + /*load_filepath*/ nullptr, + /*load_filepath_thumbnail*/ nullptr, + /*save*/ imb_save_jxl, + /*flag*/ IM_FTYPE_FLOAT, + /*filetype*/ IMB_FTYPE_JXL, + /*default_save_role*/ COLOR_ROLE_DEFAULT_FLOAT, + }, { /*init*/ nullptr, /*exit*/ nullptr, diff --git a/source/blender/imbuf/intern/format_jxl.cc b/source/blender/imbuf/intern/format_jxl.cc new file mode 100644 index 00000000000..3db5158cdc1 --- /dev/null +++ b/source/blender/imbuf/intern/format_jxl.cc @@ -0,0 +1,50 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup imbuf + */ + +#include "oiio/openimageio_support.hh" + +#include "IMB_filetype.hh" +#include "IMB_imbuf_types.hh" + +OIIO_NAMESPACE_USING +using namespace blender::imbuf; + +extern "C" { + +bool imb_is_a_jxl(const uchar *mem, size_t size) +{ + return imb_oiio_check(mem, size, "jxl"); +} + +ImBuf *imb_load_jxl(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) +{ + ImageSpec config, spec; + + ReadContext ctx{mem, size, "jxl", IMB_FTYPE_JXL, flags}; + + ImBuf *ibuf = imb_oiio_read(ctx, config, colorspace, spec); + if (ibuf) { + if (flags & IB_rect) { + IMB_rect_from_float(ibuf); + } + } + + return ibuf; +} + +bool imb_save_jxl(ImBuf *ibuf, const char *filepath, int flags) +{ + const int file_channels = 3; + const TypeDesc data_format = TypeDesc::FLOAT; + + WriteContext ctx = imb_create_write_context("jxl", ibuf, flags); + ImageSpec file_spec = imb_create_write_spec(ctx, file_channels, data_format); + + return imb_oiio_write(ctx, filepath, file_spec); +} +} diff --git a/source/blender/imbuf/intern/util.cc b/source/blender/imbuf/intern/util.cc index 90c49737130..26d6ba15c84 100644 --- a/source/blender/imbuf/intern/util.cc +++ b/source/blender/imbuf/intern/util.cc @@ -45,7 +45,7 @@ extern "C" { #define UTIL_DEBUG 0 const char *imb_ext_image[] = { - ".png", ".tga", ".bmp", ".jpg", ".jpeg", ".sgi", ".rgb", ".rgba", ".tif", ".tiff", ".tx", + ".png", ".tga", ".bmp", ".jpg", ".jpeg", ".jxl", ".sgi", ".rgb", ".rgba", ".tif", ".tiff", ".tx", #ifdef WITH_OPENJPEG ".jp2", ".j2c", #endif diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 464d359d752..65869236c6d 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -505,7 +505,7 @@ enum { R_IMF_IMTYPE_PSD = 34, R_IMF_IMTYPE_WEBP = 35, R_IMF_IMTYPE_AV1 = 36, - + R_IMF_IMTYPE_JXL = 37, R_IMF_IMTYPE_INVALID = 255, }; diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 36f182341df..5ea6cb7678c 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -287,6 +287,8 @@ const EnumPropertyItem rna_enum_curve_fit_method_items[] = { {R_IMF_IMTYPE_PNG, "PNG", ICON_FILE_IMAGE, "PNG", "Output image in PNG format"}, #define R_IMF_ENUM_JPEG \ {R_IMF_IMTYPE_JPEG90, "JPEG", ICON_FILE_IMAGE, "JPEG", "Output image in JPEG format"}, +#define R_IMF_ENUM_JXL \ + {R_IMF_IMTYPE_JXL, "JXL", ICON_FILE_IMAGE, "JPEG XL", "Output image in JPEG XL format"}, #define R_IMF_ENUM_TAGA \ {R_IMF_IMTYPE_TARGA, "TARGA", ICON_FILE_IMAGE, "Targa", "Output image in Targa format"}, #define R_IMF_ENUM_TAGA_RAW \ @@ -364,6 +366,7 @@ const EnumPropertyItem rna_enum_curve_fit_method_items[] = { R_IMF_ENUM_PNG \ R_IMF_ENUM_JPEG \ R_IMF_ENUM_JPEG2K \ + R_IMF_ENUM_JXL \ R_IMF_ENUM_TAGA \ R_IMF_ENUM_TAGA_RAW \ RNA_ENUM_ITEM_SEPR_COLUMN, R_IMF_ENUM_CINEON R_IMF_ENUM_DPX R_IMF_ENUM_EXR_MULTILAYER \ -- 2.30.2 From 1ed68114ff481cf9e80bd81a6a6359cf44bb1858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Kov=C3=A1=C5=99?= Date: Sat, 9 Mar 2024 16:42:23 +0100 Subject: [PATCH 2/2] =?UTF-8?q?Add=20=CE=B1=20channel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Kovář --- source/blender/imbuf/intern/format_jxl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/imbuf/intern/format_jxl.cc b/source/blender/imbuf/intern/format_jxl.cc index 3db5158cdc1..25ae53dec3c 100644 --- a/source/blender/imbuf/intern/format_jxl.cc +++ b/source/blender/imbuf/intern/format_jxl.cc @@ -39,7 +39,7 @@ ImBuf *imb_load_jxl(const uchar *mem, size_t size, int flags, char colorspace[IM bool imb_save_jxl(ImBuf *ibuf, const char *filepath, int flags) { - const int file_channels = 3; + const int file_channels = ibuf->planes >> 3; const TypeDesc data_format = TypeDesc::FLOAT; WriteContext ctx = imb_create_write_context("jxl", ibuf, flags); -- 2.30.2