Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: ECW 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 : // ncsjpcbuffer.h needs the min and max macros.
14 : #undef NOMINMAX
15 :
16 : #include "ecwdrivercore.h"
17 :
18 : #include "ecwsdk_headers.h"
19 :
20 : constexpr unsigned char jpc_header[] = {0xff, 0x4f, 0xff,
21 : 0x51}; // SOC + RSIZ markers
22 : constexpr unsigned char jp2_header[] = {0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50,
23 : 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a};
24 :
25 : /* Needed for v4.3 and v5.0 */
26 : #if !defined(NCS_ECWSDK_VERSION_STRING) && defined(NCS_ECWJP2_VERSION_STRING)
27 : #define NCS_ECWSDK_VERSION_STRING NCS_ECWJP2_VERSION_STRING
28 : #endif
29 :
30 : /************************************************************************/
31 : /* IdentifyECW() */
32 : /* */
33 : /* Identify method that only supports ECW files. */
34 : /************************************************************************/
35 :
36 53860 : int ECWDatasetIdentifyECW(GDALOpenInfo *poOpenInfo)
37 :
38 : {
39 : /* -------------------------------------------------------------------- */
40 : /* This has to either be a file on disk ending in .ecw or a */
41 : /* ecwp: protocol url. */
42 : /* -------------------------------------------------------------------- */
43 53860 : if ((!EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "ecw") ||
44 125 : poOpenInfo->nHeaderBytes == 0) &&
45 107769 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "ecwp:") &&
46 53790 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "ecwps:"))
47 53786 : return FALSE;
48 :
49 68 : return TRUE;
50 : }
51 :
52 : /************************************************************************/
53 : /* ECWDriverSetCommonMetadata() */
54 : /************************************************************************/
55 :
56 1306 : void ECWDriverSetCommonMetadata(GDALDriver *poDriver)
57 : {
58 1306 : poDriver->SetDescription(ECW_DRIVER_NAME);
59 1306 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
60 :
61 2612 : CPLString osLongName = "ERDAS Compressed Wavelets (SDK ";
62 :
63 : #ifdef NCS_ECWSDK_VERSION_STRING
64 : osLongName += NCS_ECWSDK_VERSION_STRING;
65 : #else
66 1306 : osLongName += "3.x";
67 : #endif
68 1306 : osLongName += ")";
69 :
70 1306 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, osLongName);
71 1306 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/ecw.html");
72 1306 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "ecw");
73 :
74 1306 : poDriver->pfnIdentify = ECWDatasetIdentifyECW;
75 1306 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
76 : #ifdef HAVE_COMPRESS
77 : // The create method does not work with SDK 3.3 ( crash in
78 : // CNCSJP2FileView::WriteLineBIL() due to m_pFile being nullptr ).
79 : #if ECWSDK_VERSION >= 50
80 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
81 : #endif
82 1306 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
83 : #if ECWSDK_VERSION >= 50
84 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte UInt16");
85 : #else
86 1306 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
87 : #endif
88 1306 : poDriver->SetMetadataItem(
89 : GDAL_DMD_CREATIONOPTIONLIST,
90 : "<CreationOptionList>"
91 : " <Option name='TARGET' type='float' description='Compression "
92 : "Percentage' />"
93 : " <Option name='PROJ' type='string' description='ECW Projection "
94 : "Name'/>"
95 : " <Option name='DATUM' type='string' description='ECW Datum Name' />"
96 :
97 : #if ECWSDK_VERSION < 40
98 : " <Option name='LARGE_OK' type='boolean' description='Enable "
99 : "compressing 500+MB files'/>"
100 : #else
101 : " <Option name='ECW_ENCODE_KEY' type='string' description='OEM "
102 : "Compress Key from ERDAS.'/>"
103 : " <Option name='ECW_ENCODE_COMPANY' type='string' description='OEM "
104 : "Company Name.'/>"
105 : #endif
106 :
107 : #if ECWSDK_VERSION >= 50
108 : " <Option name='ECW_FORMAT_VERSION' type='integer' description='ECW "
109 : "format version (2 or 3).' default='2'/>"
110 : #endif
111 :
112 1306 : "</CreationOptionList>");
113 : #else
114 : // In read-only mode, we support VirtualIO. This is not the case
115 : // for ECWCreateCopyECW().
116 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
117 : #endif
118 1306 : }
119 :
120 : /************************************************************************/
121 : /* IdentifyJPEG2000() */
122 : /* */
123 : /* Identify method that only supports JPEG2000 files. */
124 : /************************************************************************/
125 :
126 59891 : int ECWDatasetIdentifyJPEG2000(GDALOpenInfo *poOpenInfo)
127 :
128 : {
129 59891 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "J2K_SUBFILE:"))
130 0 : return TRUE;
131 :
132 59891 : else if (poOpenInfo->nHeaderBytes >= 16 &&
133 10204 : (memcmp(poOpenInfo->pabyHeader, jpc_header, sizeof(jpc_header)) ==
134 10149 : 0 ||
135 10149 : memcmp(poOpenInfo->pabyHeader, jp2_header, sizeof(jp2_header)) ==
136 : 0))
137 149 : return TRUE;
138 :
139 : else
140 59742 : return FALSE;
141 : }
142 :
143 : /************************************************************************/
144 : /* JP2ECWDriverSetCommonMetadata() */
145 : /************************************************************************/
146 :
147 1306 : void JP2ECWDriverSetCommonMetadata(GDALDriver *poDriver)
148 : {
149 1306 : poDriver->SetDescription(JP2ECW_DRIVER_NAME);
150 1306 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
151 1306 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
152 :
153 2612 : CPLString osLongName = "ERDAS JPEG2000 (SDK ";
154 :
155 : #ifdef NCS_ECWSDK_VERSION_STRING
156 : osLongName += NCS_ECWSDK_VERSION_STRING;
157 : #else
158 1306 : osLongName += "3.x";
159 : #endif
160 1306 : osLongName += ")";
161 :
162 1306 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, osLongName);
163 1306 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/jp2ecw.html");
164 1306 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jp2");
165 1306 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
166 :
167 1306 : poDriver->pfnIdentify = ECWDatasetIdentifyJPEG2000;
168 1306 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
169 :
170 1306 : poDriver->SetMetadataItem(
171 : GDAL_DMD_OPENOPTIONLIST,
172 : "<OpenOptionList>"
173 : " <Option name='1BIT_ALPHA_PROMOTION' type='boolean' "
174 : "description='Whether a 1-bit alpha channel should be promoted to "
175 : "8-bit' default='YES'/>"
176 : " <Option name='OPEN_REMOTE_GML' type='boolean' description='Whether "
177 : "to load remote vector layers referenced by a link in a GMLJP2 v2 box' "
178 : "default='NO'/>"
179 : " <Option name='GEOREF_SOURCES' type='string' description='Comma "
180 : "separated list made with values "
181 : "INTERNAL/GMLJP2/GEOJP2/WORLDFILE/PAM/NONE that describe the priority "
182 : "order for georeferencing' default='PAM,GEOJP2,GMLJP2,WORLDFILE'/>"
183 1306 : "</OpenOptionList>");
184 :
185 : #ifdef HAVE_COMPRESS
186 1306 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
187 1306 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
188 1306 : poDriver->SetMetadataItem(
189 : GDAL_DMD_CREATIONDATATYPES,
190 : "Byte UInt16 Int16 UInt32 Int32 "
191 : "Float32 "
192 : #if ECWSDK_VERSION >= 40
193 : // Crashes for sure with 3.3. Didn't try other versions
194 : "Float64"
195 : #endif
196 1306 : );
197 1306 : poDriver->SetMetadataItem(
198 : GDAL_DMD_CREATIONOPTIONLIST,
199 : "<CreationOptionList>"
200 : " <Option name='TARGET' type='float' description='Compression "
201 : "Percentage' />"
202 : " <Option name='PROJ' type='string' description='ECW Projection "
203 : "Name'/>"
204 : " <Option name='DATUM' type='string' description='ECW Datum Name' />"
205 : " <Option name='UNITS' type='string-select' description='ECW "
206 : "Projection Units'>"
207 : " <Value>METERS</Value>"
208 : " <Value>FEET</Value>"
209 : " </Option>"
210 :
211 : #if ECWSDK_VERSION < 40
212 : " <Option name='LARGE_OK' type='boolean' description='Enable "
213 : "compressing 500+MB files'/>"
214 : #else
215 : " <Option name='ECW_ENCODE_KEY' type='string' description='OEM "
216 : "Compress Key from ERDAS.'/>"
217 : " <Option name='ECW_ENCODE_COMPANY' type='string' description='OEM "
218 : "Company Name.'/>"
219 : #endif
220 :
221 : " <Option name='GeoJP2' type='boolean' description='defaults to ON'/>"
222 : " <Option name='GMLJP2' type='boolean' description='defaults to ON'/>"
223 : " <Option name='GMLJP2V2_DEF' type='string' description='Definition "
224 : "file to describe how a GMLJP2 v2 box should be generated. If set to "
225 : "YES, a minimal instance will be created'/>"
226 : " <Option name='PROFILE' type='string-select'>"
227 : " <Value>BASELINE_0</Value>"
228 : " <Value>BASELINE_1</Value>"
229 : " <Value>BASELINE_2</Value>"
230 : " <Value>NPJE</Value>"
231 : " <Value>EPJE</Value>"
232 : " </Option>"
233 : " <Option name='PROGRESSION' type='string-select'>"
234 : " <Value>LRCP</Value>"
235 : " <Value>RLCP</Value>"
236 : " <Value>RPCL</Value>"
237 : " </Option>"
238 : " <Option name='CODESTREAM_ONLY' type='boolean' description='No JP2 "
239 : "wrapper'/>"
240 : " <Option name='NBITS' type='int' description='Bits (precision) for "
241 : "sub-byte files (1-7), sub-uint16 (9-15)'/>"
242 : " <Option name='LEVELS' type='int'/>"
243 : " <Option name='LAYERS' type='int'/>"
244 : " <Option name='PRECINCT_WIDTH' type='int'/>"
245 : " <Option name='PRECINCT_HEIGHT' type='int'/>"
246 : " <Option name='TILE_WIDTH' type='int'/>"
247 : " <Option name='TILE_HEIGHT' type='int'/>"
248 : " <Option name='INCLUDE_SOP' type='boolean'/>"
249 : " <Option name='INCLUDE_EPH' type='boolean'/>"
250 : " <Option name='DECOMPRESS_LAYERS' type='int'/>"
251 : " <Option name='DECOMPRESS_RECONSTRUCTION_PARAMETER' type='float'/>"
252 : " <Option name='WRITE_METADATA' type='boolean' description='Whether "
253 : "metadata should be written, in a dedicated JP2 XML box' default='NO'/>"
254 : " <Option name='MAIN_MD_DOMAIN_ONLY' type='boolean' "
255 : "description='(Only if WRITE_METADATA=YES) Whether only metadata from "
256 : "the main domain should be written' default='NO'/>"
257 1306 : "</CreationOptionList>");
258 : #endif
259 1306 : }
260 :
261 : /************************************************************************/
262 : /* DeclareDeferredECWPlugin() */
263 : /************************************************************************/
264 :
265 : #ifdef PLUGIN_FILENAME
266 1595 : void DeclareDeferredECWPlugin()
267 : {
268 1595 : if (GDALGetDriverByName(ECW_DRIVER_NAME) != nullptr)
269 : {
270 302 : return;
271 : }
272 : {
273 1293 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
274 : #ifdef PLUGIN_INSTALLATION_MESSAGE
275 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
276 : PLUGIN_INSTALLATION_MESSAGE);
277 : #endif
278 1293 : ECWDriverSetCommonMetadata(poDriver);
279 1293 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
280 : }
281 : {
282 1293 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
283 : #ifdef PLUGIN_INSTALLATION_MESSAGE
284 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
285 : PLUGIN_INSTALLATION_MESSAGE);
286 : #endif
287 1293 : JP2ECWDriverSetCommonMetadata(poDriver);
288 1293 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
289 : }
290 : }
291 : #endif
|