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