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