/src/gdal/frmts/raw/krodataset.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: KRO format reader/writer |
4 | | * Purpose: Implementation of KOLOR Raw Format |
5 | | * Author: Even Rouault, <even dot rouault at spatialys.com> |
6 | | * Financial Support: SITES (http://www.sites.fr) |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_string.h" |
15 | | #include "gdal_frmts.h" |
16 | | #include "rawdataset.h" |
17 | | |
18 | | #include <algorithm> |
19 | | |
20 | | // http://www.autopano.net/wiki-en/Format_KRO |
21 | | |
22 | | /************************************************************************/ |
23 | | /* ==================================================================== */ |
24 | | /* KRODataset */ |
25 | | /* ==================================================================== */ |
26 | | /************************************************************************/ |
27 | | |
28 | | class KRODataset final : public RawDataset |
29 | | { |
30 | | VSILFILE *fpImage = nullptr; // image data file. |
31 | | |
32 | | CPL_DISALLOW_COPY_ASSIGN(KRODataset) |
33 | | |
34 | | CPLErr Close() override; |
35 | | |
36 | | public: |
37 | 0 | KRODataset() = default; |
38 | | ~KRODataset(); |
39 | | |
40 | | static GDALDataset *Open(GDALOpenInfo *); |
41 | | static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize, |
42 | | int nBandsIn, GDALDataType eType, |
43 | | char **papszOptions); |
44 | | static int Identify(GDALOpenInfo *); |
45 | | }; |
46 | | |
47 | | /************************************************************************/ |
48 | | /* ==================================================================== */ |
49 | | /* KRODataset */ |
50 | | /* ==================================================================== */ |
51 | | /************************************************************************/ |
52 | | |
53 | | /************************************************************************/ |
54 | | /* ~KRODataset() */ |
55 | | /************************************************************************/ |
56 | | |
57 | | KRODataset::~KRODataset() |
58 | | |
59 | 0 | { |
60 | 0 | KRODataset::Close(); |
61 | 0 | } |
62 | | |
63 | | /************************************************************************/ |
64 | | /* Close() */ |
65 | | /************************************************************************/ |
66 | | |
67 | | CPLErr KRODataset::Close() |
68 | 0 | { |
69 | 0 | CPLErr eErr = CE_None; |
70 | 0 | if (nOpenFlags != OPEN_FLAGS_CLOSED) |
71 | 0 | { |
72 | 0 | if (KRODataset::FlushCache(true) != CE_None) |
73 | 0 | eErr = CE_Failure; |
74 | |
|
75 | 0 | if (fpImage) |
76 | 0 | { |
77 | 0 | if (VSIFCloseL(fpImage) != 0) |
78 | 0 | { |
79 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
80 | 0 | eErr = CE_Failure; |
81 | 0 | } |
82 | 0 | } |
83 | |
|
84 | 0 | if (GDALPamDataset::Close() != CE_None) |
85 | 0 | eErr = CE_Failure; |
86 | 0 | } |
87 | 0 | return eErr; |
88 | 0 | } |
89 | | |
90 | | /************************************************************************/ |
91 | | /* Identify() */ |
92 | | /************************************************************************/ |
93 | | |
94 | | int KRODataset::Identify(GDALOpenInfo *poOpenInfo) |
95 | | |
96 | 177k | { |
97 | 177k | if (poOpenInfo->nHeaderBytes < 20) |
98 | 173k | return FALSE; |
99 | | |
100 | 3.49k | if (!STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader), |
101 | 3.49k | "KRO\x01")) |
102 | 3.49k | return FALSE; |
103 | | |
104 | 0 | return TRUE; |
105 | 3.49k | } |
106 | | |
107 | | /************************************************************************/ |
108 | | /* Open() */ |
109 | | /************************************************************************/ |
110 | | |
111 | | GDALDataset *KRODataset::Open(GDALOpenInfo *poOpenInfo) |
112 | | |
113 | 0 | { |
114 | 0 | if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr) |
115 | 0 | return nullptr; |
116 | | |
117 | | /* -------------------------------------------------------------------- */ |
118 | | /* Create a corresponding GDALDataset. */ |
119 | | /* -------------------------------------------------------------------- */ |
120 | 0 | auto poDS = std::make_unique<KRODataset>(); |
121 | 0 | poDS->eAccess = poOpenInfo->eAccess; |
122 | 0 | std::swap(poDS->fpImage, poOpenInfo->fpL); |
123 | | |
124 | | /* -------------------------------------------------------------------- */ |
125 | | /* Read the file header. */ |
126 | | /* -------------------------------------------------------------------- */ |
127 | 0 | char achHeader[20] = {'\0'}; |
128 | 0 | CPL_IGNORE_RET_VAL(VSIFReadL(achHeader, 1, 20, poDS->fpImage)); |
129 | |
|
130 | 0 | int nXSize; |
131 | 0 | memcpy(&nXSize, achHeader + 4, 4); |
132 | 0 | CPL_MSBPTR32(&nXSize); |
133 | |
|
134 | 0 | int nYSize = 0; |
135 | 0 | memcpy(&nYSize, achHeader + 8, 4); |
136 | 0 | CPL_MSBPTR32(&nYSize); |
137 | |
|
138 | 0 | int nDepth = 0; |
139 | 0 | memcpy(&nDepth, achHeader + 12, 4); |
140 | 0 | CPL_MSBPTR32(&nDepth); |
141 | |
|
142 | 0 | int nComp = 0; |
143 | 0 | memcpy(&nComp, achHeader + 16, 4); |
144 | 0 | CPL_MSBPTR32(&nComp); |
145 | |
|
146 | 0 | if (!GDALCheckDatasetDimensions(nXSize, nYSize) || |
147 | 0 | !GDALCheckBandCount(nComp, FALSE)) |
148 | 0 | { |
149 | 0 | return nullptr; |
150 | 0 | } |
151 | | |
152 | 0 | poDS->nRasterXSize = nXSize; |
153 | 0 | poDS->nRasterYSize = nYSize; |
154 | |
|
155 | 0 | GDALDataType eDT = GDT_Unknown; |
156 | 0 | if (nDepth == 8) |
157 | 0 | { |
158 | 0 | eDT = GDT_Byte; |
159 | 0 | } |
160 | 0 | else if (nDepth == 16) |
161 | 0 | { |
162 | 0 | eDT = GDT_UInt16; |
163 | 0 | } |
164 | 0 | else if (nDepth == 32) |
165 | 0 | { |
166 | 0 | eDT = GDT_Float32; |
167 | 0 | } |
168 | 0 | else |
169 | 0 | { |
170 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Unhandled depth : %d", nDepth); |
171 | 0 | return nullptr; |
172 | 0 | } |
173 | | |
174 | 0 | const int nDataTypeSize = nDepth / 8; |
175 | |
|
176 | 0 | if (nComp == 0 || nDataTypeSize == 0 || |
177 | 0 | poDS->nRasterXSize > INT_MAX / (nComp * nDataTypeSize)) |
178 | 0 | { |
179 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
180 | 0 | "Too large width / number of bands"); |
181 | 0 | return nullptr; |
182 | 0 | } |
183 | | |
184 | 0 | vsi_l_offset nExpectedSize = static_cast<vsi_l_offset>(poDS->nRasterXSize) * |
185 | 0 | poDS->nRasterYSize * nComp * |
186 | 0 | nDataTypeSize + |
187 | 0 | 20; |
188 | 0 | VSIFSeekL(poDS->fpImage, 0, SEEK_END); |
189 | 0 | if (VSIFTellL(poDS->fpImage) < nExpectedSize) |
190 | 0 | { |
191 | 0 | CPLError(CE_Failure, CPLE_FileIO, "File too short"); |
192 | 0 | return nullptr; |
193 | 0 | } |
194 | | |
195 | | /* -------------------------------------------------------------------- */ |
196 | | /* Create bands. */ |
197 | | /* -------------------------------------------------------------------- */ |
198 | 0 | for (int iBand = 0; iBand < nComp; iBand++) |
199 | 0 | { |
200 | 0 | auto poBand = RawRasterBand::Create( |
201 | 0 | poDS.get(), iBand + 1, poDS->fpImage, 20 + nDataTypeSize * iBand, |
202 | 0 | nComp * nDataTypeSize, poDS->nRasterXSize * nComp * nDataTypeSize, |
203 | 0 | eDT, RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN, |
204 | 0 | RawRasterBand::OwnFP::NO); |
205 | 0 | if (!poBand) |
206 | 0 | return nullptr; |
207 | 0 | if (nComp == 3 || nComp == 4) |
208 | 0 | { |
209 | 0 | poBand->SetColorInterpretation( |
210 | 0 | static_cast<GDALColorInterp>(GCI_RedBand + iBand)); |
211 | 0 | } |
212 | 0 | poDS->SetBand(iBand + 1, std::move(poBand)); |
213 | 0 | } |
214 | | |
215 | 0 | if (nComp > 1) |
216 | 0 | poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); |
217 | | |
218 | | /* -------------------------------------------------------------------- */ |
219 | | /* Initialize any PAM information. */ |
220 | | /* -------------------------------------------------------------------- */ |
221 | 0 | poDS->SetDescription(poOpenInfo->pszFilename); |
222 | 0 | poDS->TryLoadXML(); |
223 | | |
224 | | /* -------------------------------------------------------------------- */ |
225 | | /* Check for overviews. */ |
226 | | /* -------------------------------------------------------------------- */ |
227 | 0 | poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename); |
228 | |
|
229 | 0 | return poDS.release(); |
230 | 0 | } |
231 | | |
232 | | /************************************************************************/ |
233 | | /* Create() */ |
234 | | /************************************************************************/ |
235 | | |
236 | | GDALDataset *KRODataset::Create(const char *pszFilename, int nXSize, int nYSize, |
237 | | int nBandsIn, GDALDataType eType, |
238 | | char ** /* papszOptions */) |
239 | 0 | { |
240 | 0 | if (eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Float32) |
241 | 0 | { |
242 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
243 | 0 | "Attempt to create KRO file with unsupported data type '%s'.", |
244 | 0 | GDALGetDataTypeName(eType)); |
245 | 0 | return nullptr; |
246 | 0 | } |
247 | 0 | if (nXSize == 0 || nYSize == 0 || nBandsIn == 0) |
248 | 0 | { |
249 | 0 | return nullptr; |
250 | 0 | } |
251 | | |
252 | | /* -------------------------------------------------------------------- */ |
253 | | /* Try to create file. */ |
254 | | /* -------------------------------------------------------------------- */ |
255 | 0 | VSILFILE *fp = VSIFOpenL(pszFilename, "wb"); |
256 | 0 | if (fp == nullptr) |
257 | 0 | { |
258 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
259 | 0 | "Attempt to create file `%s' failed.", pszFilename); |
260 | 0 | return nullptr; |
261 | 0 | } |
262 | | |
263 | 0 | size_t nRet = VSIFWriteL("KRO\01", 4, 1, fp); |
264 | | |
265 | | /* -------------------------------------------------------------------- */ |
266 | | /* Create a file level header. */ |
267 | | /* -------------------------------------------------------------------- */ |
268 | 0 | int nTmp = nXSize; |
269 | 0 | CPL_MSBPTR32(&nTmp); |
270 | 0 | nRet += VSIFWriteL(&nTmp, 4, 1, fp); |
271 | |
|
272 | 0 | nTmp = nYSize; |
273 | 0 | CPL_MSBPTR32(&nTmp); |
274 | 0 | nRet += VSIFWriteL(&nTmp, 4, 1, fp); |
275 | |
|
276 | 0 | nTmp = GDALGetDataTypeSizeBits(eType); |
277 | 0 | CPL_MSBPTR32(&nTmp); |
278 | 0 | nRet += VSIFWriteL(&nTmp, 4, 1, fp); |
279 | |
|
280 | 0 | nTmp = nBandsIn; |
281 | 0 | CPL_MSBPTR32(&nTmp); |
282 | 0 | nRet += VSIFWriteL(&nTmp, 4, 1, fp); |
283 | | |
284 | | /* -------------------------------------------------------------------- */ |
285 | | /* Zero out image data */ |
286 | | /* -------------------------------------------------------------------- */ |
287 | |
|
288 | 0 | CPL_IGNORE_RET_VAL(VSIFSeekL(fp, |
289 | 0 | static_cast<vsi_l_offset>(nXSize) * nYSize * |
290 | 0 | GDALGetDataTypeSizeBytes(eType) * |
291 | 0 | nBandsIn - |
292 | 0 | 1, |
293 | 0 | SEEK_CUR)); |
294 | 0 | GByte byNul = 0; |
295 | 0 | nRet += VSIFWriteL(&byNul, 1, 1, fp); |
296 | 0 | if (VSIFCloseL(fp) != 0) |
297 | 0 | { |
298 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
299 | 0 | return nullptr; |
300 | 0 | } |
301 | | |
302 | 0 | if (nRet != 6) |
303 | 0 | return nullptr; |
304 | | |
305 | 0 | return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_Update)); |
306 | 0 | } |
307 | | |
308 | | /************************************************************************/ |
309 | | /* GDALRegister_KRO() */ |
310 | | /************************************************************************/ |
311 | | |
312 | | void GDALRegister_KRO() |
313 | | |
314 | 2 | { |
315 | 2 | if (GDALGetDriverByName("KRO") != nullptr) |
316 | 0 | return; |
317 | | |
318 | 2 | GDALDriver *poDriver = new GDALDriver(); |
319 | | |
320 | 2 | poDriver->SetDescription("KRO"); |
321 | 2 | poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); |
322 | 2 | poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "KOLOR Raw"); |
323 | 2 | poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "kro"); |
324 | 2 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
325 | 2 | poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, |
326 | 2 | "Byte UInt16 Float32"); |
327 | | |
328 | 2 | poDriver->pfnIdentify = KRODataset::Identify; |
329 | 2 | poDriver->pfnOpen = KRODataset::Open; |
330 | 2 | poDriver->pfnCreate = KRODataset::Create; |
331 | | |
332 | 2 | GetGDALDriverManager()->RegisterDriver(poDriver); |
333 | 2 | } |