Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: GDAL 4 : * Purpose: JPEGXL driver 5 : * Author: Even Rouault, <even.rouault at spatialys.com> 6 : * 7 : ****************************************************************************** 8 : * Copyright (c) 2023, Even Rouault, <even.rouault at spatialys.com> 9 : * 10 : * SPDX-License-Identifier: MIT 11 : ****************************************************************************/ 12 : 13 : #include "gdal_frmts.h" 14 : #include "gdalplugindriverproxy.h" 15 : 16 : #include "jpegxldrivercore.h" 17 : 18 : /************************************************************************/ 19 : /* IsJPEGXLContainer() */ 20 : /************************************************************************/ 21 : 22 3128 : bool IsJPEGXLContainer(GDALOpenInfo *poOpenInfo) 23 : { 24 3128 : constexpr const GByte abyJXLContainerSignature[] = { 25 : 0x00, 0x00, 0x00, 0x0C, 'J', 'X', 'L', ' ', 0x0D, 0x0A, 0x87, 0x0A}; 26 3128 : return (poOpenInfo->nHeaderBytes >= 27 6039 : static_cast<int>(sizeof(abyJXLContainerSignature)) && 28 2911 : memcmp(poOpenInfo->pabyHeader, abyJXLContainerSignature, 29 3128 : sizeof(abyJXLContainerSignature)) == 0); 30 : } 31 : 32 : /************************************************************************/ 33 : /* JPEGXLDatasetIdentifyPartial() */ 34 : /************************************************************************/ 35 : 36 13174 : static int JPEGXLDatasetIdentifyPartial(GDALOpenInfo *poOpenInfo) 37 : { 38 13174 : if (poOpenInfo->fpL == nullptr) 39 12093 : return false; 40 : 41 1081 : if (poOpenInfo->IsExtensionEqualToCI("jxl")) 42 1 : return true; 43 : 44 : // See 45 : // https://github.com/libjxl/libjxl/blob/c98f133f3f5e456caaa2ba00bc920e923b713abc/lib/jxl/decode.cc#L107-L138 46 : 47 : // JPEG XL codestream 48 1080 : if (poOpenInfo->nHeaderBytes >= 2 && poOpenInfo->pabyHeader[0] == 0xff && 49 10 : poOpenInfo->pabyHeader[1] == 0x0a) 50 : { 51 : // Two bytes is not enough to reliably identify 52 : // JPEGXLDataset::Identify() does a bit more work then 53 1 : return GDAL_IDENTIFY_UNKNOWN; 54 : } 55 : 56 1079 : return IsJPEGXLContainer(poOpenInfo); 57 : } 58 : 59 : /************************************************************************/ 60 : /* JPEGXLDriverSetCommonMetadata() */ 61 : /************************************************************************/ 62 : 63 1762 : void JPEGXLDriverSetCommonMetadata(GDALDriver *poDriver) 64 : { 65 : // Set the driver details. 66 1762 : poDriver->SetDescription(DRIVER_NAME); 67 : 68 1762 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); 69 1762 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "JPEG-XL"); 70 1762 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/jpegxl.html"); 71 1762 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jxl"); 72 1762 : poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/jxl"); 73 : 74 1762 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, 75 1762 : "Byte UInt16 Float32"); 76 : 77 : #ifdef HAVE_JXL_BOX_API 78 1762 : const char *pszOpenOptions = 79 : "<OpenOptionList>\n" 80 : " <Option name='APPLY_ORIENTATION' type='boolean' " 81 : "description='whether to take into account EXIF Orientation to " 82 : "rotate/flip the image' default='NO'/>\n" 83 : "</OpenOptionList>\n"; 84 1762 : poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST, pszOpenOptions); 85 : #endif 86 : 87 1762 : poDriver->SetMetadataItem( 88 : GDAL_DMD_CREATIONOPTIONLIST, 89 : "<CreationOptionList>\n" 90 : " <Option name='LOSSLESS' type='boolean' description='Whether JPEGXL " 91 : "compression should be lossless' default='YES'/>" 92 : " <Option name='LOSSLESS_COPY' type='string-select' " 93 : "description='Whether conversion should be lossless' default='AUTO'>" 94 : " <Value>AUTO</Value>" 95 : " <Value>YES</Value>" 96 : " <Value>NO</Value>" 97 : " </Option>" 98 : " <Option name='EFFORT' type='int' description='Level of effort " 99 : "1(fast)-9(slow)' default='5'/>" 100 : " <Option name='DISTANCE' type='float' description='Distance level " 101 : "for lossy compression (0=mathematically lossless, 1.0=visually " 102 : "lossless, usual range [0.5,3])' default='1.0' min='0.01' max='25.0'/>" 103 : #ifdef HAVE_JxlEncoderSetExtraChannelDistance 104 : " <Option name='ALPHA_DISTANCE' type='float' " 105 : "description='Distance level for alpha channel " 106 : "(-1=same as non-alpha channels, " 107 : "0=mathematically lossless, 1.0=visually lossless, " 108 : "usual range [0.5,3])' default='-1' min='-1' max='25.0'/>" 109 : #endif 110 : " <Option name='QUALITY' type='float' description='Alternative " 111 : "setting to DISTANCE to specify lossy compression, roughly matching " 112 : "libjpeg quality setting in the [0,100] range' default='90' max='100'/>" 113 : " <Option name='NBITS' type='int' description='BITS for sub-byte " 114 : "files (1-7), sub-uint16_t (9-15)'/>" 115 : " <Option name='SOURCE_ICC_PROFILE' description='ICC profile encoded " 116 : "in Base64' type='string'/>\n" 117 : #ifdef HAVE_JXL_THREADS 118 : " <Option name='NUM_THREADS' type='string' description='Number of " 119 : "worker threads for compression. Can be set to ALL_CPUS' " 120 : "default='ALL_CPUS'/>" 121 : #endif 122 : #ifdef HAVE_JXL_BOX_API 123 : " <Option name='WRITE_EXIF_METADATA' type='boolean' " 124 : "description='Whether to write EXIF_ metadata in a Exif box' " 125 : "default='YES'/>" 126 : " <Option name='WRITE_XMP' type='boolean' description='Whether to " 127 : "write xml:XMP metadata in a xml box' default='YES'/>" 128 : " <Option name='WRITE_GEOJP2' type='boolean' description='Whether to " 129 : "write georeferencing in a jumb.uuid box' default='YES'/>" 130 : " <Option name='COMPRESS_BOXES' type='boolean' description='Whether " 131 : "to decompress Exif/XMP/GeoJP2 boxes' default='NO'/>" 132 : #endif 133 1762 : "</CreationOptionList>\n"); 134 : 135 1762 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); 136 : 137 : #ifdef HAVE_JxlEncoderInitExtraChannelInfo 138 1762 : poDriver->SetMetadataItem("JXL_ENCODER_SUPPORT_EXTRA_CHANNELS", "YES"); 139 : #endif 140 : 141 1762 : poDriver->pfnIdentify = JPEGXLDatasetIdentifyPartial; 142 1762 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES"); 143 1762 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES"); 144 1762 : } 145 : 146 : /************************************************************************/ 147 : /* DeclareDeferredJPEGXLPlugin() */ 148 : /************************************************************************/ 149 : 150 : #ifdef PLUGIN_FILENAME 151 2033 : void DeclareDeferredJPEGXLPlugin() 152 : { 153 2033 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr) 154 : { 155 283 : return; 156 : } 157 1750 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME); 158 : #ifdef PLUGIN_INSTALLATION_MESSAGE 159 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE, 160 : PLUGIN_INSTALLATION_MESSAGE); 161 : #endif 162 1750 : JPEGXLDriverSetCommonMetadata(poDriver); 163 1750 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver); 164 : } 165 : #endif