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 54836 : 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 54836 : if ((!poOpenInfo->IsExtensionEqualToCI("ecw") ||
44 131 : poOpenInfo->nHeaderBytes == 0) &&
45 109740 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "ecwp:") &&
46 54772 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "ecwps:"))
47 54769 : return FALSE;
48 :
49 68 : return TRUE;
50 : }
51 :
52 : /************************************************************************/
53 : /* ECWDriverSetCommonMetadata() */
54 : /************************************************************************/
55 :
56 1397 : void ECWDriverSetCommonMetadata(GDALDriver *poDriver)
57 : {
58 1397 : poDriver->SetDescription(ECW_DRIVER_NAME);
59 1397 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
60 :
61 2794 : CPLString osLongName = "ERDAS Compressed Wavelets (SDK ";
62 :
63 : #ifdef NCS_ECWSDK_VERSION_STRING
64 : osLongName += NCS_ECWSDK_VERSION_STRING;
65 : #else
66 1397 : osLongName += "3.x";
67 : #endif
68 1397 : osLongName += ")";
69 :
70 1397 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, osLongName);
71 1397 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/ecw.html");
72 1397 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "ecw");
73 :
74 1397 : poDriver->pfnIdentify = ECWDatasetIdentifyECW;
75 1397 : 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 1397 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
83 : #if ECWSDK_VERSION >= 50
84 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte UInt16");
85 : #else
86 1397 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
87 : #endif
88 1397 : 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 1397 : "</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 :
119 1397 : poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
120 1397 : poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS,
121 : "GeoTransform SRS "
122 1397 : "DatasetMetadata BandMetadata");
123 1397 : }
124 :
125 : /************************************************************************/
126 : /* IdentifyJPEG2000() */
127 : /* */
128 : /* Identify method that only supports JPEG2000 files. */
129 : /************************************************************************/
130 :
131 61096 : int ECWDatasetIdentifyJPEG2000(GDALOpenInfo *poOpenInfo)
132 :
133 : {
134 61096 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "J2K_SUBFILE:"))
135 0 : return TRUE;
136 :
137 61096 : else if (poOpenInfo->nHeaderBytes >= 16 &&
138 10484 : (memcmp(poOpenInfo->pabyHeader, jpc_header, sizeof(jpc_header)) ==
139 10429 : 0 ||
140 10429 : memcmp(poOpenInfo->pabyHeader, jp2_header, sizeof(jp2_header)) ==
141 : 0))
142 149 : return TRUE;
143 :
144 : else
145 60947 : return FALSE;
146 : }
147 :
148 : /************************************************************************/
149 : /* JP2ECWDriverSetCommonMetadata() */
150 : /************************************************************************/
151 :
152 1397 : void JP2ECWDriverSetCommonMetadata(GDALDriver *poDriver)
153 : {
154 1397 : poDriver->SetDescription(JP2ECW_DRIVER_NAME);
155 1397 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
156 1397 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
157 :
158 2794 : CPLString osLongName = "ERDAS JPEG2000 (SDK ";
159 :
160 : #ifdef NCS_ECWSDK_VERSION_STRING
161 : osLongName += NCS_ECWSDK_VERSION_STRING;
162 : #else
163 1397 : osLongName += "3.x";
164 : #endif
165 1397 : osLongName += ")";
166 :
167 1397 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, osLongName);
168 1397 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/jp2ecw.html");
169 1397 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jp2");
170 1397 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
171 :
172 1397 : poDriver->pfnIdentify = ECWDatasetIdentifyJPEG2000;
173 1397 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
174 :
175 1397 : poDriver->SetMetadataItem(
176 : GDAL_DMD_OPENOPTIONLIST,
177 : "<OpenOptionList>"
178 : " <Option name='1BIT_ALPHA_PROMOTION' type='boolean' "
179 : "description='Whether a 1-bit alpha channel should be promoted to "
180 : "8-bit' default='YES'/>"
181 : " <Option name='OPEN_REMOTE_GML' type='boolean' description='Whether "
182 : "to load remote vector layers referenced by a link in a GMLJP2 v2 box' "
183 : "default='NO'/>"
184 : " <Option name='GEOREF_SOURCES' type='string' description='Comma "
185 : "separated list made with values "
186 : "INTERNAL/GMLJP2/GEOJP2/WORLDFILE/PAM/NONE that describe the priority "
187 : "order for georeferencing' default='PAM,GEOJP2,GMLJP2,WORLDFILE'/>"
188 1397 : "</OpenOptionList>");
189 :
190 : #ifdef HAVE_COMPRESS
191 1397 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
192 1397 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
193 1397 : poDriver->SetMetadataItem(
194 : GDAL_DMD_CREATIONDATATYPES,
195 : "Byte UInt16 Int16 UInt32 Int32 "
196 : "Float32 "
197 : #if ECWSDK_VERSION >= 40
198 : // Crashes for sure with 3.3. Didn't try other versions
199 : "Float64"
200 : #endif
201 1397 : );
202 1397 : poDriver->SetMetadataItem(
203 : GDAL_DMD_CREATIONOPTIONLIST,
204 : "<CreationOptionList>"
205 : " <Option name='TARGET' type='float' description='Compression "
206 : "Percentage' />"
207 : " <Option name='PROJ' type='string' description='ECW Projection "
208 : "Name'/>"
209 : " <Option name='DATUM' type='string' description='ECW Datum Name' />"
210 : " <Option name='UNITS' type='string-select' description='ECW "
211 : "Projection Units'>"
212 : " <Value>METERS</Value>"
213 : " <Value>FEET</Value>"
214 : " </Option>"
215 :
216 : #if ECWSDK_VERSION < 40
217 : " <Option name='LARGE_OK' type='boolean' description='Enable "
218 : "compressing 500+MB files'/>"
219 : #else
220 : " <Option name='ECW_ENCODE_KEY' type='string' description='OEM "
221 : "Compress Key from ERDAS.'/>"
222 : " <Option name='ECW_ENCODE_COMPANY' type='string' description='OEM "
223 : "Company Name.'/>"
224 : #endif
225 :
226 : " <Option name='GeoJP2' type='boolean' description='defaults to ON'/>"
227 : " <Option name='GMLJP2' type='boolean' description='defaults to ON'/>"
228 : " <Option name='GMLJP2V2_DEF' type='string' description='Definition "
229 : "file to describe how a GMLJP2 v2 box should be generated. If set to "
230 : "YES, a minimal instance will be created'/>"
231 : " <Option name='PROFILE' type='string-select'>"
232 : " <Value>BASELINE_0</Value>"
233 : " <Value>BASELINE_1</Value>"
234 : " <Value>BASELINE_2</Value>"
235 : " <Value>NPJE</Value>"
236 : " <Value>EPJE</Value>"
237 : " </Option>"
238 : " <Option name='PROGRESSION' type='string-select'>"
239 : " <Value>LRCP</Value>"
240 : " <Value>RLCP</Value>"
241 : " <Value>RPCL</Value>"
242 : " </Option>"
243 : " <Option name='CODESTREAM_ONLY' type='boolean' description='No JP2 "
244 : "wrapper'/>"
245 : " <Option name='NBITS' type='int' description='Bits (precision) for "
246 : "sub-byte files (1-7), sub-uint16 (9-15)'/>"
247 : " <Option name='LEVELS' type='int'/>"
248 : " <Option name='LAYERS' type='int'/>"
249 : " <Option name='PRECINCT_WIDTH' type='int'/>"
250 : " <Option name='PRECINCT_HEIGHT' type='int'/>"
251 : " <Option name='TILE_WIDTH' type='int'/>"
252 : " <Option name='TILE_HEIGHT' type='int'/>"
253 : " <Option name='INCLUDE_SOP' type='boolean'/>"
254 : " <Option name='INCLUDE_EPH' type='boolean'/>"
255 : " <Option name='DECOMPRESS_LAYERS' type='int'/>"
256 : " <Option name='DECOMPRESS_RECONSTRUCTION_PARAMETER' type='float'/>"
257 : " <Option name='WRITE_METADATA' type='boolean' description='Whether "
258 : "metadata should be written, in a dedicated JP2 XML box' default='NO'/>"
259 : " <Option name='MAIN_MD_DOMAIN_ONLY' type='boolean' "
260 : "description='(Only if WRITE_METADATA=YES) Whether only metadata from "
261 : "the main domain should be written' default='NO'/>"
262 1397 : "</CreationOptionList>");
263 : #endif
264 1397 : }
265 :
266 : /************************************************************************/
267 : /* DeclareDeferredECWPlugin() */
268 : /************************************************************************/
269 :
270 : #ifdef PLUGIN_FILENAME
271 1686 : void DeclareDeferredECWPlugin()
272 : {
273 1686 : if (GDALGetDriverByName(ECW_DRIVER_NAME) != nullptr)
274 : {
275 302 : return;
276 : }
277 : {
278 1384 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
279 : #ifdef PLUGIN_INSTALLATION_MESSAGE
280 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
281 : PLUGIN_INSTALLATION_MESSAGE);
282 : #endif
283 1384 : ECWDriverSetCommonMetadata(poDriver);
284 1384 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
285 : }
286 : {
287 1384 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
288 : #ifdef PLUGIN_INSTALLATION_MESSAGE
289 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
290 : PLUGIN_INSTALLATION_MESSAGE);
291 : #endif
292 1384 : JP2ECWDriverSetCommonMetadata(poDriver);
293 1384 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
294 : }
295 : }
296 : #endif
|