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