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 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "jpegxldrivercore.h"
30 :
31 : /************************************************************************/
32 : /* IsJPEGXLContainer() */
33 : /************************************************************************/
34 :
35 2606 : bool IsJPEGXLContainer(GDALOpenInfo *poOpenInfo)
36 : {
37 2606 : constexpr const GByte abyJXLContainerSignature[] = {
38 : 0x00, 0x00, 0x00, 0x0C, 'J', 'X', 'L', ' ', 0x0D, 0x0A, 0x87, 0x0A};
39 2606 : return (poOpenInfo->nHeaderBytes >=
40 5028 : static_cast<int>(sizeof(abyJXLContainerSignature)) &&
41 2422 : memcmp(poOpenInfo->pabyHeader, abyJXLContainerSignature,
42 2606 : sizeof(abyJXLContainerSignature)) == 0);
43 : }
44 :
45 : /************************************************************************/
46 : /* JPEGXLDatasetIdentifyPartial() */
47 : /************************************************************************/
48 :
49 18257 : static int JPEGXLDatasetIdentifyPartial(GDALOpenInfo *poOpenInfo)
50 : {
51 18257 : if (poOpenInfo->fpL == nullptr)
52 17296 : return false;
53 :
54 961 : if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "jxl"))
55 1 : return true;
56 :
57 : // See
58 : // https://github.com/libjxl/libjxl/blob/c98f133f3f5e456caaa2ba00bc920e923b713abc/lib/jxl/decode.cc#L107-L138
59 :
60 : // JPEG XL codestream
61 960 : if (poOpenInfo->nHeaderBytes >= 2 && poOpenInfo->pabyHeader[0] == 0xff &&
62 9 : poOpenInfo->pabyHeader[1] == 0x0a)
63 : {
64 : // Two bytes is not enough to reliably identify
65 : // JPEGXLDataset::Identify() does a bit more work then
66 1 : return GDAL_IDENTIFY_UNKNOWN;
67 : }
68 :
69 959 : return IsJPEGXLContainer(poOpenInfo);
70 : }
71 :
72 : /************************************************************************/
73 : /* JPEGXLDriverSetCommonMetadata() */
74 : /************************************************************************/
75 :
76 1230 : void JPEGXLDriverSetCommonMetadata(GDALDriver *poDriver)
77 : {
78 : // Set the driver details.
79 1230 : poDriver->SetDescription(DRIVER_NAME);
80 :
81 1230 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
82 1230 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "JPEG-XL");
83 1230 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/jpegxl.html");
84 1230 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jxl");
85 1230 : poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/jxl");
86 :
87 1230 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
88 1230 : "Byte UInt16 Float32");
89 :
90 : #ifdef HAVE_JXL_BOX_API
91 1230 : const char *pszOpenOptions =
92 : "<OpenOptionList>\n"
93 : " <Option name='APPLY_ORIENTATION' type='boolean' "
94 : "description='whether to take into account EXIF Orientation to "
95 : "rotate/flip the image' default='NO'/>\n"
96 : "</OpenOptionList>\n";
97 1230 : poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST, pszOpenOptions);
98 : #endif
99 :
100 1230 : poDriver->SetMetadataItem(
101 : GDAL_DMD_CREATIONOPTIONLIST,
102 : "<CreationOptionList>\n"
103 : " <Option name='LOSSLESS' type='boolean' description='Whether JPEGXL "
104 : "compression should be lossless' default='YES'/>"
105 : " <Option name='LOSSLESS_COPY' type='string-select' "
106 : "description='Whether conversion should be lossless' default='AUTO'>"
107 : " <Value>AUTO</Value>"
108 : " <Value>YES</Value>"
109 : " <Value>NO</Value>"
110 : " </Option>"
111 : " <Option name='EFFORT' type='int' description='Level of effort "
112 : "1(fast)-9(slow)' default='5'/>"
113 : " <Option name='DISTANCE' type='float' description='Distance level "
114 : "for lossy compression (0=mathematically lossless, 1.0=visually "
115 : "lossless, usual range [0.5,3])' default='1.0' min='0.1' max='15.0'/>"
116 : #ifdef HAVE_JxlEncoderSetExtraChannelDistance
117 : " <Option name='ALPHA_DISTANCE' type='float' "
118 : "description='Distance level for alpha channel "
119 : "(-1=same as non-alpha channels, "
120 : "0=mathematically lossless, 1.0=visually lossless, "
121 : "usual range [0.5,3])' default='-1' min='-1' max='15.0'/>"
122 : #endif
123 : " <Option name='QUALITY' type='float' description='Alternative "
124 : "setting to DISTANCE to specify lossy compression, roughly matching "
125 : "libjpeg quality setting in the [0,100] range' default='90' max='100'/>"
126 : " <Option name='NBITS' type='int' description='BITS for sub-byte "
127 : "files (1-7), sub-uint16_t (9-15)'/>"
128 : " <Option name='SOURCE_ICC_PROFILE' description='ICC profile encoded "
129 : "in Base64' type='string'/>\n"
130 : #ifdef HAVE_JXL_THREADS
131 : " <Option name='NUM_THREADS' type='string' description='Number of "
132 : "worker threads for compression. Can be set to ALL_CPUS' "
133 : "default='ALL_CPUS'/>"
134 : #endif
135 : #ifdef HAVE_JXL_BOX_API
136 : " <Option name='WRITE_EXIF_METADATA' type='boolean' "
137 : "description='Whether to write EXIF_ metadata in a Exif box' "
138 : "default='YES'/>"
139 : " <Option name='WRITE_XMP' type='boolean' description='Whether to "
140 : "write xml:XMP metadata in a xml box' default='YES'/>"
141 : " <Option name='WRITE_GEOJP2' type='boolean' description='Whether to "
142 : "write georeferencing in a jumb.uuid box' default='YES'/>"
143 : " <Option name='COMPRESS_BOXES' type='boolean' description='Whether "
144 : "to decompress Exif/XMP/GeoJP2 boxes' default='NO'/>"
145 : #endif
146 1230 : "</CreationOptionList>\n");
147 :
148 1230 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
149 :
150 : #ifdef HAVE_JxlEncoderInitExtraChannelInfo
151 1230 : poDriver->SetMetadataItem("JXL_ENCODER_SUPPORT_EXTRA_CHANNELS", "YES");
152 : #endif
153 :
154 1230 : poDriver->pfnIdentify = JPEGXLDatasetIdentifyPartial;
155 1230 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
156 1230 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
157 1230 : }
158 :
159 : /************************************************************************/
160 : /* DeclareDeferredJPEGXLPlugin() */
161 : /************************************************************************/
162 :
163 : #ifdef PLUGIN_FILENAME
164 1522 : void DeclareDeferredJPEGXLPlugin()
165 : {
166 1522 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
167 : {
168 301 : return;
169 : }
170 1221 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
171 : #ifdef PLUGIN_INSTALLATION_MESSAGE
172 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
173 : PLUGIN_INSTALLATION_MESSAGE);
174 : #endif
175 1221 : JPEGXLDriverSetCommonMetadata(poDriver);
176 1221 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
177 : }
178 : #endif
|