From 39dcff4a999fa880a2cd68e35fa55259f545b1ea Mon Sep 17 00:00:00 2001 From: Jesus Solis Date: Tue, 21 Jul 2026 03:31:25 -0500 Subject: [PATCH] tdf#156931 add import support for JPEG XL image format Change-Id: I5b8c70d5739fdc91b0ca30fbaa780a5c7004741d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208352 Tested-by: Jenkins Reviewed-by: Xisco Fauli --- filter/Configuration_filter.mk | 9 +- filter/source/config/cache/typedetection.cxx | 1 + .../fragments/filters/JXL___JPEG_XL.xcu | 30 +++ .../internalgraphicfilters/jxl_Import.xcu | 27 +++ .../config/fragments/types/jxl_JPEG_XL.xcu | 29 +++ include/sal/log-areas.dox | 1 + include/svl/inettype.hxx | 1 + include/svtools/imagemgr.hxx | 1 + include/svx/strings.hrc | 1 + include/vcl/gfxlink.hxx | 5 +- include/vcl/graphic/GraphicMetadata.hxx | 1 + include/vcl/graphicfilter.hxx | 5 + include/vcl/salctype.hxx | 1 + scp2/source/draw/registryitem_draw.scp | 1 + svx/source/core/graphichelper.cxx | 2 + svx/source/gallery2/galtheme.cxx | 1 + svx/source/xoutdev/_xoutbmp.cxx | 3 + vcl/Library_vcl.mk | 2 + vcl/inc/filter/JxlReader.hxx | 26 +++ vcl/inc/graphic/GraphicFormatDetector.hxx | 4 + vcl/inc/graphic/UnoGraphicDescriptor.hxx | 1 + vcl/source/filter/FilterConfigCache.cxx | 10 +- vcl/source/filter/GraphicFormatDetector.cxx | 25 +++ vcl/source/filter/graphicfilter.cxx | 22 ++ vcl/source/filter/graphicfilter2.cxx | 10 + vcl/source/filter/jxl/reader.cxx | 203 ++++++++++++++++++ vcl/source/gdi/gfxlink.cxx | 1 + vcl/source/gdi/mtfxmldump.cxx | 1 + vcl/source/graphic/UnoGraphicDescriptor.cxx | 2 + vcl/workben/fftester.cxx | 7 + 30 files changed, 425 insertions(+), 8 deletions(-) create mode 100644 filter/source/config/fragments/filters/JXL___JPEG_XL.xcu create mode 100644 filter/source/config/fragments/internalgraphicfilters/jxl_Import.xcu create mode 100644 filter/source/config/fragments/types/jxl_JPEG_XL.xcu create mode 100644 vcl/inc/filter/JxlReader.hxx create mode 100644 vcl/source/filter/jxl/reader.cxx diff --git a/filter/Configuration_filter.mk b/filter/Configuration_filter.mk index ceafbe860dcf..d881314da19e 100644 --- a/filter/Configuration_filter.mk +++ b/filter/Configuration_filter.mk @@ -715,8 +715,9 @@ $(eval $(call filter_Configuration_add_types,fcfg_langpack,fcfg_drawgraphics_typ gif_Graphics_Interchange \ graphic_HTML \ jpg_JPEG \ + jxl_JPEG_XL \ met_OS2_Metafile \ - mov_MOV \ + mov_MOV \ pbm_Portable_Bitmap \ pcd_Photo_CD_Base \ pcd_Photo_CD_Base16 \ @@ -750,6 +751,7 @@ $(eval $(call filter_Configuration_add_filters,fcfg_langpack,fcfg_drawgraphics_f EPS___Encapsulated_PostScript \ GIF___Graphics_Interchange \ JPG___JPEG \ + JXL___JPEG_XL \ MET___OS_2_Metafile \ mov__MOV \ PBM___Portable_Bitmap \ @@ -801,6 +803,7 @@ $(eval $(call filter_Configuration_add_types,fcfg_langpack,fcfg_impressgraphics_ graphic_HTML \ impress_CGM_Computer_Graphics_Metafile \ jpg_JPEG \ + jxl_JPEG_XL \ met_OS2_Metafile \ pbm_Portable_Bitmap \ pct_Mac_Pict \ @@ -871,8 +874,9 @@ $(eval $(call filter_Configuration_add_types,fcfg_langpack,fcfg_internalgraphics eps_Encapsulated_PostScript \ gif_Graphics_Interchange \ jpg_JPEG \ + jxl_JPEG_XL \ met_OS2_Metafile \ - mov_MOV \ + mov_MOV \ pbm_Portable_Bitmap \ pcd_Photo_CD_Base \ pcd_Photo_CD_Base16 \ @@ -911,6 +915,7 @@ $(eval $(call filter_Configuration_add_internal_filters,fcfg_langpack,fcfg_inter gif_Import \ jpg_Export \ jpg_Import \ + jxl_Import \ met_Import \ pbm_Import \ pcd_Import_Base \ diff --git a/filter/source/config/cache/typedetection.cxx b/filter/source/config/cache/typedetection.cxx index 007f8a368e90..24f68ff89011 100644 --- a/filter/source/config/cache/typedetection.cxx +++ b/filter/source/config/cache/typedetection.cxx @@ -234,6 +234,7 @@ int getFlatTypeRank(std::u16string_view rType) "psd_Adobe_Photoshop", "png_Portable_Network_Graphic", "jpg_JPEG", + "jxl_JPEG_XL", "mov_MOV", "gif_Graphics_Interchange", "bmp_MS_Windows", diff --git a/filter/source/config/fragments/filters/JXL___JPEG_XL.xcu b/filter/source/config/fragments/filters/JXL___JPEG_XL.xcu new file mode 100644 index 000000000000..cc49099002c6 --- /dev/null +++ b/filter/source/config/fragments/filters/JXL___JPEG_XL.xcu @@ -0,0 +1,30 @@ + + + IMPORT ALIEN + + + + + JXL - JPEG XL Image + + 0 + jxl_JPEG_XL + + com.sun.star.drawing.DrawingDocument + diff --git a/filter/source/config/fragments/internalgraphicfilters/jxl_Import.xcu b/filter/source/config/fragments/internalgraphicfilters/jxl_Import.xcu new file mode 100644 index 000000000000..862f2b599f68 --- /dev/null +++ b/filter/source/config/fragments/internalgraphicfilters/jxl_Import.xcu @@ -0,0 +1,27 @@ + + + jxl_JPEG_XL + SVIJXL + JXL - JPEG XL + + + JXL - JPEG XL Image + + IMPORT + diff --git a/filter/source/config/fragments/types/jxl_JPEG_XL.xcu b/filter/source/config/fragments/types/jxl_JPEG_XL.xcu new file mode 100644 index 000000000000..ef884eb9f428 --- /dev/null +++ b/filter/source/config/fragments/types/jxl_JPEG_XL.xcu @@ -0,0 +1,29 @@ + + + com.sun.star.comp.draw.FormatDetector + + jxl + image/jxl + false + JXL - JPEG XL + + JXL - JPEG XL Image + + + diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox index c3cb5e70c137..4c6826bb91c1 100644 --- a/include/sal/log-areas.dox +++ b/include/sal/log-areas.dox @@ -501,6 +501,7 @@ certain functionality. @li @c vcl.emf - EMF/EMF+ processing @li @c vcl.filter @li @c vcl.filter.webp +@li @c vcl.filter.jxl @li @c vcl.fonts - font-specific code @li @c vcl.fonts.detail @li @c vcl.gdi - the GDI part of VCL, devices, bitmaps, etc. diff --git a/include/svl/inettype.hxx b/include/svl/inettype.hxx index f57ca1b927ad..24ac527ddd37 100644 --- a/include/svl/inettype.hxx +++ b/include/svl/inettype.hxx @@ -166,6 +166,7 @@ enum INetContentType CONTENT_TYPE_AUDIO_WEBM, CONTENT_TYPE_IMAGE_GIF, CONTENT_TYPE_IMAGE_JPEG, + CONTENT_TYPE_IMAGE_JXL, CONTENT_TYPE_IMAGE_PCX, CONTENT_TYPE_IMAGE_PNG, CONTENT_TYPE_IMAGE_TIFF, diff --git a/include/svtools/imagemgr.hxx b/include/svtools/imagemgr.hxx index 0b2425c24b8d..3e4e2225a923 100644 --- a/include/svtools/imagemgr.hxx +++ b/include/svtools/imagemgr.hxx @@ -40,6 +40,7 @@ enum class SvImageId { HTML = START + 63, JPG = START + 64, WEBP = START + 65, + JXL = START + 66, Math = START + 68, MathTemplate = START + 69, File = START + 74, diff --git a/include/svx/strings.hrc b/include/svx/strings.hrc index dd067b893c9d..e6a38395963c 100644 --- a/include/svx/strings.hrc +++ b/include/svx/strings.hrc @@ -1426,6 +1426,7 @@ #define STR_IMAGE_CAPACITY_WITH_REDUCTION NC_("STR_IMAGE_CAPACITY_WITH_REDUCTION", "$(CAPACITY) KiB ($(REDUCTION)% reduction)") #define STR_IMAGE_GIF NC_("STR_IMAGE_GIF", "GIF image") #define STR_IMAGE_JPEG NC_("STR_IMAGE_JPEG", "JPEG image") +#define STR_IMAGE_JXL NC_("STR_IMAGE_JXL", "JPEG XL image") #define STR_IMAGE_PNG NC_("STR_IMAGE_PNG", "PNG image") #define STR_IMAGE_TIFF NC_("STR_IMAGE_TIFF", "TIFF image") #define STR_IMAGE_WMF NC_("STR_IMAGE_WMF", "WMF image") diff --git a/include/vcl/gfxlink.hxx b/include/vcl/gfxlink.hxx index 55e294aba70e..a58f3f72f824 100644 --- a/include/vcl/gfxlink.hxx +++ b/include/vcl/gfxlink.hxx @@ -48,12 +48,13 @@ enum class GfxLinkType NativeMov = 10, NativeBmp = 11, NativePdf = 12, - NativeWebp = 13, // If a new type is added, make sure to change NativeLast too + NativeWebp = 13, + NativeJxl = 14, // If a new type is added, make sure to change NativeLast too // Alias for when the first native type starts and last native // type ends. NativeFirst = NativeGif, - NativeLast = NativeWebp, + NativeLast = NativeJxl, }; class Graphic; diff --git a/include/vcl/graphic/GraphicMetadata.hxx b/include/vcl/graphic/GraphicMetadata.hxx index b01e38c8cfab..63a94c485823 100644 --- a/include/vcl/graphic/GraphicMetadata.hxx +++ b/include/vcl/graphic/GraphicMetadata.hxx @@ -38,6 +38,7 @@ enum class GraphicFileFormat PSD = 0x000f, EPS = 0x0010, WEBP = 0x0011, + JXL = 0x0012, MOV = 0x00e0, PDF = 0x00e1, DXF = 0x00f1, diff --git a/include/vcl/graphicfilter.hxx b/include/vcl/graphicfilter.hxx index 26b9ab60479c..6e39e8fe9b8e 100644 --- a/include/vcl/graphicfilter.hxx +++ b/include/vcl/graphicfilter.hxx @@ -81,6 +81,7 @@ namespace o3tl #define IMP_GIF "SVIGIF" #define IMP_PNG "SVIPNG" #define IMP_JPEG "SVIJPEG" +#define IMP_JXL "SVIJXL" #define IMP_XBM "SVIXBM" #define IMP_XPM "SVIXPM" #define IMP_SVG "SVISVG" @@ -106,6 +107,7 @@ namespace o3tl #define EXP_EMF "SVEMF" #define EXP_EMZ "SVEMZ" #define EXP_JPEG "SVEJPEG" +#define EXP_JXL "SVEJXL" #define EXP_SVG "SVESVG" #define EXP_SVGZ "SVESVGZ" #define EXP_PDF "SVEPDF" @@ -120,6 +122,7 @@ namespace o3tl inline constexpr OUString BMP_SHORTNAME = u"BMP"_ustr; inline constexpr OUString GIF_SHORTNAME = u"GIF"_ustr; inline constexpr OUString JPG_SHORTNAME = u"JPG"_ustr; +inline constexpr OUString JXL_SHORTNAME = u"JXL"_ustr; inline constexpr OUString MET_SHORTNAME = u"MET"_ustr; inline constexpr OUString PCT_SHORTNAME = u"PCT"_ustr; inline constexpr OUString PNG_SHORTNAME = u"PNG"_ustr; @@ -141,6 +144,7 @@ class VCL_DLLPUBLIC GraphicDescriptor final SAL_DLLPRIVATE bool ImpDetectBMP( SvStream& rStm, bool bExtendedInfo ); SAL_DLLPRIVATE bool ImpDetectGIF( SvStream& rStm, bool bExtendedInfo ); SAL_DLLPRIVATE bool ImpDetectJPG( SvStream& rStm, bool bExtendedInfo ); + SAL_DLLPRIVATE bool ImpDetectJXL( SvStream& rStm, bool bExtendedInfo ); SAL_DLLPRIVATE bool ImpDetectPCD( SvStream& rStm, bool bExtendedInfo ); SAL_DLLPRIVATE bool ImpDetectPCX( SvStream& rStm ); SAL_DLLPRIVATE bool ImpDetectPNG( SvStream& rStm, bool bExtendedInfo ); @@ -319,6 +323,7 @@ public: BinaryDataContainer & rpGraphicContent); SAL_DLLPRIVATE static ErrCode readJPEG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, GraphicFilterImportFlags nImportFlags); + SAL_DLLPRIVATE static ErrCode readJXL(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType); SAL_DLLPRIVATE static ErrCode readSVG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, BinaryDataContainer & rpGraphicContent); SAL_DLLPRIVATE static ErrCode readXBM(SvStream & rStream, Graphic & rGraphic); diff --git a/include/vcl/salctype.hxx b/include/vcl/salctype.hxx index c52128aef51d..37a2aefe61f6 100644 --- a/include/vcl/salctype.hxx +++ b/include/vcl/salctype.hxx @@ -31,6 +31,7 @@ enum class ConvertDataFormat BMP, GIF, JPG, + JXL, MET, PCT, PNG, diff --git a/scp2/source/draw/registryitem_draw.scp b/scp2/source/draw/registryitem_draw.scp index e1781c58cf24..d381e8c6960c 100644 --- a/scp2/source/draw/registryitem_draw.scp +++ b/scp2/source/draw/registryitem_draw.scp @@ -1079,6 +1079,7 @@ CONDITIONAL_REGISTER_DOC_EXTENSION( Gif, gid_Module_Prg_Draw_Other_Reg, gif, G CONDITIONAL_REGISTER_DOC_EXTENSION( Jpg, gid_Module_Prg_Draw_Other_Reg, jpg, JPG, JOINT_PHOTOGRAPHIC_EXPERTS_GROUP, 5, sdraw.exe, open, Draw ) CONDITIONAL_REGISTER_DOC_EXTENSION( Jpeg, gid_Module_Prg_Draw_Other_Reg, jpeg, JPEG, JOINT_PHOTOGRAPHIC_EXPERTS_GROUP, 5, sdraw.exe, open, Draw ) CONDITIONAL_REGISTER_DOC_EXTENSION( Jpe, gid_Module_Prg_Draw_Other_Reg, jpe, JPE, JOINT_PHOTOGRAPHIC_EXPERTS_GROUP, 5, sdraw.exe, open, Draw ) +CONDITIONAL_REGISTER_DOC_EXTENSION( Jxl, gid_Module_Prg_Draw_Other_Reg, jxl, JXL, JPEG_XL, 5, sdraw.exe, open, Draw ) CONDITIONAL_REGISTER_DOC_EXTENSION( Pbm, gid_Module_Prg_Draw_Other_Reg, pbm, PBM, PORTABLE_BITMAP_FORMAT, 5, sdraw.exe, open, Draw ) CONDITIONAL_REGISTER_DOC_EXTENSION( Pcx, gid_Module_Prg_Draw_Other_Reg, pcx, PCX, PERSONAL_COMPUTER_EXCHANGE, 5, sdraw.exe, open, Draw ) CONDITIONAL_REGISTER_DOC_EXTENSION( Pgm, gid_Module_Prg_Draw_Other_Reg, pgm, PGM, PORTABLE_GRAYMAP_FORMAT, 5, sdraw.exe, open, Draw ) diff --git a/svx/source/core/graphichelper.cxx b/svx/source/core/graphichelper.cxx index f92241709984..198f493ca668 100644 --- a/svx/source/core/graphichelper.cxx +++ b/svx/source/core/graphichelper.cxx @@ -74,6 +74,7 @@ const auto constGfxTypeToExtension = frozen::make_unordered_map + +VCL_DLLPUBLIC bool ImportJxlGraphic(SvStream& rStream, Graphic& rGraphic); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/graphic/GraphicFormatDetector.hxx b/vcl/inc/graphic/GraphicFormatDetector.hxx index 19c4e2f7e9b1..4569e647f9d3 100644 --- a/vcl/inc/graphic/GraphicFormatDetector.hxx +++ b/vcl/inc/graphic/GraphicFormatDetector.hxx @@ -40,6 +40,9 @@ static inline OUString getImportFormatShortName(GraphicFileFormat nFormat) case GraphicFileFormat::JPG: aKeyName = u"JPG"_ustr; break; + case GraphicFileFormat::JXL: + aKeyName = u"JXL"_ustr; + break; case GraphicFileFormat::PCD: aKeyName = u"PCD"_ustr; break; @@ -165,6 +168,7 @@ public: bool checkPNG(); bool checkAPNG(); bool checkJPG(); + bool checkJXL(); SAL_DLLPRIVATE bool checkSVM(); SAL_DLLPRIVATE bool checkPCD(); bool checkPSD(); diff --git a/vcl/inc/graphic/UnoGraphicDescriptor.hxx b/vcl/inc/graphic/UnoGraphicDescriptor.hxx index 4ac5d17da9ab..8f67b673e016 100644 --- a/vcl/inc/graphic/UnoGraphicDescriptor.hxx +++ b/vcl/inc/graphic/UnoGraphicDescriptor.hxx @@ -30,6 +30,7 @@ class SvStream; inline constexpr OUString MIMETYPE_BMP = u"image/x-MS-bmp"_ustr; inline constexpr OUString MIMETYPE_GIF = u"image/gif"_ustr; inline constexpr OUString MIMETYPE_JPG = u"image/jpeg"_ustr; +inline constexpr OUString MIMETYPE_JXL = u"image/jxl"_ustr; inline constexpr OUString MIMETYPE_PCD = u"image/x-photo-cd"_ustr; inline constexpr OUString MIMETYPE_PCX = u"image/x-pcx"_ustr; inline constexpr OUString MIMETYPE_PNG = u"image/png"_ustr; diff --git a/vcl/source/filter/FilterConfigCache.cxx b/vcl/source/filter/FilterConfigCache.cxx index 25411c78c392..caada2535786 100644 --- a/vcl/source/filter/FilterConfigCache.cxx +++ b/vcl/source/filter/FilterConfigCache.cxx @@ -42,10 +42,11 @@ using namespace ::com::sun::star::configuration ; const char* const FilterConfigCache::FilterConfigCacheEntry::InternalPixelFilterNameList[] = { - IMP_BMP, IMP_GIF, IMP_PNG, IMP_JPEG, IMP_TIFF, IMP_WEBP, - IMP_XBM, IMP_XPM, IMP_TGA, IMP_PICT, IMP_MET, IMP_RAS, - IMP_PCX, IMP_MOV, IMP_PSD, IMP_PCD, IMP_PBM, IMP_DXF, - EXP_BMP, EXP_GIF, EXP_PNG, EXP_JPEG, EXP_TIFF, EXP_WEBP, + IMP_BMP, IMP_GIF, IMP_PNG, IMP_JPEG, IMP_JXL, IMP_TIFF, + IMP_WEBP, IMP_XBM, IMP_XPM, IMP_TGA, IMP_PICT, IMP_MET, + IMP_RAS, IMP_PCX, IMP_MOV, IMP_PSD, IMP_PCD, IMP_PBM, + IMP_DXF, + EXP_BMP, EXP_GIF, EXP_PNG, EXP_JPEG, EXP_TIFF, EXP_WEBP, nullptr }; @@ -211,6 +212,7 @@ constexpr FilterEntry InternalFilterListForSvxLight[] { u"gif"_ustr, 2, u"SVEGIF"_ustr }, { u"jpg"_ustr, 1, u"SVIJPEG"_ustr }, { u"jpg"_ustr, 2, u"SVEJPEG"_ustr }, + { u"jxl"_ustr, 1, u"SVIJXL"_ustr }, { u"mov"_ustr, 1, u"SVMOV"_ustr }, { u"mov"_ustr, 2, u"SVMOV"_ustr }, { u"met"_ustr, 1, u"SVMET"_ustr }, diff --git a/vcl/source/filter/GraphicFormatDetector.cxx b/vcl/source/filter/GraphicFormatDetector.cxx index 1015e3bbb807..86cc1fc0dbdf 100644 --- a/vcl/source/filter/GraphicFormatDetector.cxx +++ b/vcl/source/filter/GraphicFormatDetector.cxx @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "igif/gifread.hxx" #include @@ -166,6 +167,16 @@ bool peekGraphicFormat(SvStream& rStream, OUString& rFormatExtension, bool bTest } } + if (!bTest || rFormatExtension.startsWith("JXL")) + { + bSomethingTested = true; + if (aDetector.checkJXL()) + { + rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat); + return true; + } + } + if (!bTest || rFormatExtension.startsWith("SVM")) { bSomethingTested = true; @@ -980,6 +991,20 @@ bool GraphicFormatDetector::checkJPG() return false; } +bool GraphicFormatDetector::checkJXL() +{ + if ((maFirstBytes[0] == 0xFF && maFirstBytes[1] == 0x0A) + || (maFirstBytes[0] == 0x00 && maFirstBytes[1] == 0x00 && maFirstBytes[2] == 0x00 + && maFirstBytes[3] == 0x0C && maFirstBytes[4] == 0x4A && maFirstBytes[5] == 0x58 + && maFirstBytes[6] == 0x4C && maFirstBytes[7] == 0x20 && maFirstBytes[8] == 0x0D + && maFirstBytes[9] == 0x0A && maFirstBytes[10] == 0x87 && maFirstBytes[11] == 0x0A)) + { + maMetadata.mnFormat = GraphicFileFormat::JXL; + return true; + } + return false; +} + bool GraphicFormatDetector::checkSVM() { sal_uInt32 n32 = 0; diff --git a/vcl/source/filter/graphicfilter.cxx b/vcl/source/filter/graphicfilter.cxx index 8a9c6763170c..6ab119893282 100644 --- a/vcl/source/filter/graphicfilter.cxx +++ b/vcl/source/filter/graphicfilter.cxx @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -724,6 +725,11 @@ ErrCode prepareImageTypeAndData(SvStream& rStream, sal_uInt32 nStreamLength, Bin rLinkType = GfxLinkType::NativeJpg; nStatus = ERRCODE_NONE; } + else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_JXL)) + { + rLinkType = GfxLinkType::NativeJxl; + nStatus = ERRCODE_NONE; + } else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_SVG)) { rStream.Seek(nStreamBegin); @@ -943,6 +949,17 @@ ErrCode GraphicFilter::readJPEG(SvStream & rStream, Graphic & rGraphic, GfxLinkT return ERRCODE_NONE; } +ErrCode GraphicFilter::readJXL(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType) +{ + if (ImportJxlGraphic(rStream, rGraphic)) + { + rLinkType = GfxLinkType::NativeJxl; + return ERRCODE_NONE; + } + else + return ERRCODE_GRFILTER_FILTERERROR; +} + ErrCode GraphicFilter::readSVG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, BinaryDataContainer& rpGraphicContent) { ErrCode aReturnCode = ERRCODE_NONE; @@ -1333,6 +1350,10 @@ ErrCode GraphicFilter::ImportGraphic(Graphic& rGraphic, std::u16string_view rPat { nStatus = readJPEG(rIStream, rGraphic, eLinkType, nImportFlags); } + else if (aFilterName.equalsIgnoreAsciiCase(IMP_JXL)) + { + nStatus = readJXL(rIStream, rGraphic, eLinkType); + } else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVG) || aFilterName.equalsIgnoreAsciiCase(IMP_SVGZ)) { nStatus = readSVG(rIStream, rGraphic, eLinkType, aGraphicContent); @@ -1839,6 +1860,7 @@ IMPL_LINK( GraphicFilter, FilterCallback, ConvertData&, rData, bool ) case ConvertDataFormat::BMP: aShortName = BMP_SHORTNAME; break; case ConvertDataFormat::GIF: aShortName = GIF_SHORTNAME; break; case ConvertDataFormat::JPG: aShortName = JPG_SHORTNAME; break; + case ConvertDataFormat::JXL: aShortName = JXL_SHORTNAME; break; case ConvertDataFormat::MET: aShortName = MET_SHORTNAME; break; case ConvertDataFormat::PCT: aShortName = PCT_SHORTNAME; break; case ConvertDataFormat::PNG: aShortName = PNG_SHORTNAME; break; diff --git a/vcl/source/filter/graphicfilter2.cxx b/vcl/source/filter/graphicfilter2.cxx index 6558deda4597..b0b8f55605f3 100644 --- a/vcl/source/filter/graphicfilter2.cxx +++ b/vcl/source/filter/graphicfilter2.cxx @@ -62,6 +62,7 @@ bool GraphicDescriptor::Detect( bool bExtendedInfo ) if ( ImpDetectGIF( rStm, bExtendedInfo ) ) bRet = true; else if ( ImpDetectJPG( rStm, bExtendedInfo ) ) bRet = true; + else if ( ImpDetectJXL( rStm, bExtendedInfo ) ) bRet = true; else if ( ImpDetectBMP( rStm, bExtendedInfo ) ) bRet = true; else if ( ImpDetectPNG( rStm, bExtendedInfo ) ) bRet = true; else if ( ImpDetectTIF( rStm, bExtendedInfo ) ) bRet = true; @@ -301,6 +302,15 @@ bool GraphicDescriptor::ImpDetectJPG( SvStream& rStm, bool bExtendedInfo ) return bRet; } +bool GraphicDescriptor::ImpDetectJXL( SvStream& rStm, bool bExtendedInfo ) +{ + vcl::GraphicFormatDetector aDetector( rStm, aPathExt, bExtendedInfo ); + bool bRet = aDetector.detect() && aDetector.checkJXL(); + if ( bRet ) + aMetadata = aDetector.getMetadata(); + return bRet; +} + bool GraphicDescriptor::ImpDetectPCD( SvStream& rStm, bool ) { vcl::GraphicFormatDetector aDetector( rStm, aPathExt, false /*bExtendedInfo*/ ); diff --git a/vcl/source/filter/jxl/reader.cxx b/vcl/source/filter/jxl/reader.cxx new file mode 100644 index 000000000000..4393b02bc8d9 --- /dev/null +++ b/vcl/source/filter/jxl/reader.cxx @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static bool readJxl(SvStream& stream, Graphic& graphic) +{ + std::vector data; + std::vector pixels; + + size_t xsize = 0; + size_t ysize = 0; + + // Multi-threaded parallel runner. + JxlResizableParallelRunnerPtr runner = JxlResizableParallelRunnerMake(nullptr); + + JxlDecoderPtr dec = JxlDecoderMake(nullptr); + if (JXL_DEC_SUCCESS + != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING + | JXL_DEC_FULL_IMAGE)) + { + SAL_WARN("vcl.filter.jxl", "JxlDecoderSubscribeEvents failed"); + return false; + } + + if (JXL_DEC_SUCCESS + != JxlDecoderSetParallelRunner(dec.get(), JxlResizableParallelRunner, runner.get())) + { + SAL_WARN("vcl.filter.jxl", "JxlDecoderSetParallelRunner failed"); + return false; + } + + JxlBasicInfo info; + JxlPixelFormat format = { 4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0 }; + + size_t chunksize = 4096; + + // Read first chunksize bytes. + data.resize(chunksize); + sal_Size nBytesRead = stream.ReadBytes(data.data(), chunksize); + if (nBytesRead <= 0) + return false; // no bytes to read + data.resize(nBytesRead); + + if (JXL_DEC_SUCCESS != JxlDecoderSetInput(dec.get(), data.data(), nBytesRead)) + { + SAL_WARN("vcl.filter.jxl", "JxlDecoderSetInput failed"); + return false; + } + + // Try using partially read image if there is an error. + bool success = true; + for (;;) + { + JxlDecoderStatus status = JxlDecoderProcessInput(dec.get()); + + if (status == JXL_DEC_ERROR) + { + SAL_WARN("vcl.filter.jxl", "Decoder error"); + success = false; + break; + } + else if (status == JXL_DEC_NEED_MORE_INPUT || status == JXL_DEC_SUCCESS + || status == JXL_DEC_FULL_IMAGE) + { + JxlDecoderReleaseInput(dec.get()); + JxlDecoderFlushImage(dec.get()); + + // Read 4096 (more) bytes. + data.resize(chunksize); + nBytesRead = stream.ReadBytes(data.data(), chunksize); + if (nBytesRead <= 0) + { + if (status == JXL_DEC_NEED_MORE_INPUT) + { + SAL_WARN("vcl.filter.jxl", "Incomplete/truncated file"); + success = false; + break; + } + else + { + // Finished decoding complete file. + break; + } + } + data.resize(nBytesRead); + + if (JXL_DEC_SUCCESS != JxlDecoderSetInput(dec.get(), data.data(), nBytesRead)) + { + SAL_WARN("vcl.filter.jxl", "JxlDecoderSetInput failed"); + success = false; + break; + } + } + else if (status == JXL_DEC_BASIC_INFO) + { + if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) + { + SAL_WARN("vcl.filter.jxl", "JxlDecoderGetBasicInfo failed"); + success = false; + break; + } + xsize = info.xsize; + ysize = info.ysize; + JxlResizableParallelRunnerSetThreads( + runner.get(), JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize)); + } + else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) + { + size_t buffer_size; + if (JXL_DEC_SUCCESS != JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) + { + SAL_WARN("vcl.filter.jxl", "JxlDecoderImageOutBufferSize failed"); + success = false; + break; + } + if (buffer_size != xsize * ysize * 4) + { + SAL_WARN("vcl.filter.jxl", "Invalid out buffer size, expected " + << xsize * ysize * 4 << " but got " << buffer_size); + success = false; + break; + } + pixels.resize(xsize * ysize * 4); + if (JXL_DEC_SUCCESS + != JxlDecoderSetImageOutBuffer(dec.get(), &format, pixels.data(), pixels.size())) + { + SAL_WARN("vcl.filter.jxl", "JxlDecoderSetImageOutBuffer failed"); + success = false; + break; + } + } + else if (status != JXL_DEC_COLOR_ENCODING) // not using color profile + { + SAL_WARN("vcl.filter.jxl", "Unknown JxlDecoderStatus"); + success = false; + break; + } + } + + // convert pixels into a bitmap + + Bitmap bitmap(Size(xsize, ysize), vcl::PixelFormat::N32_BPP); + BitmapScopedWriteAccess access(bitmap); + if (!access) + return false; + + for (tools::Long y = 0, nHeight = access->Height(); y < nHeight; ++y) + { + const unsigned char* src = pixels.data() + xsize * 4 * y; + for (tools::Long x = 0, nWidth = access->Width(); x < nWidth; ++x) + { + sal_uInt8 r = src[0]; + sal_uInt8 g = src[1]; + sal_uInt8 b = src[2]; + sal_uInt8 a = src[3]; + access->SetPixel(y, x, Color(ColorAlpha, a, r, g, b)); + src += 4; + } + } + + access.reset(); // Flush BitmapScopedWriteAccess + graphic = bitmap; + + return success; +} + +bool ImportJxlGraphic(SvStream& rStream, Graphic& rGraphic) +{ + bool bRetValue = readJxl(rStream, rGraphic); + if (!bRetValue) + rStream.SetError(SVSTREAM_FILEFORMAT_ERROR); + return bRetValue; +} diff --git a/vcl/source/gdi/gfxlink.cxx b/vcl/source/gdi/gfxlink.cxx index aff0e9778980..8a139e582597 100644 --- a/vcl/source/gdi/gfxlink.cxx +++ b/vcl/source/gdi/gfxlink.cxx @@ -117,6 +117,7 @@ bool GfxLink::LoadNative(Graphic& rGraphic, sal_Int32 nPageNum) const { case GfxLinkType::NativeGif: aShortName = GIF_SHORTNAME; break; case GfxLinkType::NativeJpg: aShortName = JPG_SHORTNAME; break; + case GfxLinkType::NativeJxl: aShortName = JXL_SHORTNAME; break; case GfxLinkType::NativePng: aShortName = PNG_SHORTNAME; break; case GfxLinkType::NativeTif: aShortName = TIF_SHORTNAME; break; case GfxLinkType::NativeWmf: aShortName = WMF_SHORTNAME; break; diff --git a/vcl/source/gdi/mtfxmldump.cxx b/vcl/source/gdi/mtfxmldump.cxx index 2807b36a70cb..c973824ddd60 100644 --- a/vcl/source/gdi/mtfxmldump.cxx +++ b/vcl/source/gdi/mtfxmldump.cxx @@ -490,6 +490,7 @@ OUString convertGfxLinkTypeToString(GfxLinkType eGfxLinkType) case GfxLinkType::NativeBmp: return u"NativeBmp"_ustr; case GfxLinkType::NativeGif: return u"NativeGif"_ustr; case GfxLinkType::NativeJpg: return u"NativeJpg"_ustr; + case GfxLinkType::NativeJxl: return u"NativeJxl"_ustr; case GfxLinkType::NativeMet: return u"NativeMet"_ustr; case GfxLinkType::NativeMov: return u"NativeMov"_ustr; case GfxLinkType::NativePct: return u"NativePct"_ustr; diff --git a/vcl/source/graphic/UnoGraphicDescriptor.cxx b/vcl/source/graphic/UnoGraphicDescriptor.cxx index 00330f53946d..9dc31979b311 100644 --- a/vcl/source/graphic/UnoGraphicDescriptor.cxx +++ b/vcl/source/graphic/UnoGraphicDescriptor.cxx @@ -116,6 +116,7 @@ void GraphicDescriptor::implCreate( SvStream& rIStm, const OUString& rURL ) case GraphicFileFormat::BMP: aMimeType = MIMETYPE_BMP; cType = graphic::GraphicType::PIXEL; break; case GraphicFileFormat::GIF: aMimeType = MIMETYPE_GIF; cType = graphic::GraphicType::PIXEL; break; case GraphicFileFormat::JPG: aMimeType = MIMETYPE_JPG; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::JXL: aMimeType = MIMETYPE_JXL; cType = graphic::GraphicType::PIXEL; break; case GraphicFileFormat::PCD: aMimeType = MIMETYPE_PCD; cType = graphic::GraphicType::PIXEL; break; case GraphicFileFormat::PCX: aMimeType = MIMETYPE_PCX; cType = graphic::GraphicType::PIXEL; break; case GraphicFileFormat::PNG: aMimeType = MIMETYPE_PNG; cType = graphic::GraphicType::PIXEL; break; @@ -290,6 +291,7 @@ void GraphicDescriptor::_getPropertyValues( const comphelper::PropertyMapEntry** case GfxLinkType::NativeBmp: aMimeType = MIMETYPE_BMP; break; case GfxLinkType::NativeJpg: aMimeType = MIMETYPE_JPG; break; + case GfxLinkType::NativeJxl: aMimeType = MIMETYPE_JXL; break; case GfxLinkType::NativePng: aMimeType = MIMETYPE_PNG; break; case GfxLinkType::NativeTif: aMimeType = MIMETYPE_TIF; break; case GfxLinkType::NativeWmf: aMimeType = aLink.IsEMF() ? MIMETYPE_EMF : MIMETYPE_WMF; break; diff --git a/vcl/workben/fftester.cxx b/vcl/workben/fftester.cxx index 0c10b6bea810..3877a0df7cd5 100644 --- a/vcl/workben/fftester.cxx +++ b/vcl/workben/fftester.cxx @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -147,6 +148,12 @@ SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) SvFileStream aFileStream(out, StreamMode::READ); ret = static_cast(ImportJPEG(aFileStream, aImportOutput, GraphicFilterImportFlags::NONE, nullptr)); } + else if (strcmp(argv[2], "jxl") == 0) + { + Graphic aGraphic; + SvFileStream aFileStream(out, StreamMode::READ); + ret = static_cast(ImportJxlGraphic(aFileStream, aGraphic)); + } else if (strcmp(argv[2], "gif") == 0) { ImportOutput aImportOutput;