/src/gdal/frmts/bmp/bmpdataset.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: Microsoft Windows Bitmap |
4 | | * Purpose: Read/write MS Windows Device Independent Bitmap (DIB) files |
5 | | * and OS/2 Presentation Manager bitmaps v. 1.x and v. 2.x |
6 | | * Author: Andrey Kiselev, dron@remotesensing.org |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2002, Andrey Kiselev <dron@remotesensing.org> |
10 | | * Copyright (c) 2007-2010, Even Rouault <even dot rouault at spatialys.com> |
11 | | * |
12 | | * SPDX-License-Identifier: MIT |
13 | | ****************************************************************************/ |
14 | | |
15 | | #include "cpl_string.h" |
16 | | #include "gdal_frmts.h" |
17 | | #include "gdal_pam.h" |
18 | | |
19 | | #include <limits> |
20 | | |
21 | | // Enable if you want to see lots of BMP debugging output. |
22 | | // #define BMP_DEBUG |
23 | | |
24 | | enum BMPType |
25 | | { |
26 | | BMPT_WIN4, // BMP used in Windows 3.0/NT 3.51/95 |
27 | | BMPT_WIN5, // BMP used in Windows NT 4.0/98/Me/2000/XP |
28 | | BMPT_OS21, // BMP used in OS/2 PM 1.x |
29 | | BMPT_OS22 // BMP used in OS/2 PM 2.x |
30 | | }; |
31 | | |
32 | | // Bitmap file consists of a BMPFileHeader structure followed by a |
33 | | // BMPInfoHeader structure. An array of BMPColorEntry structures (also called |
34 | | // a colour table) follows the bitmap information header structure. The colour |
35 | | // table is followed by a second array of indexes into the colour table (the |
36 | | // actual bitmap data). Data may be compressed, for 4-bpp and 8-bpp used RLE |
37 | | // compression. |
38 | | // |
39 | | // +---------------------+ |
40 | | // | BMPFileHeader | |
41 | | // +---------------------+ |
42 | | // | BMPInfoHeader | |
43 | | // +---------------------+ |
44 | | // | BMPColorEntry array | |
45 | | // +---------------------+ |
46 | | // | Colour-index array | |
47 | | // +---------------------+ |
48 | | // |
49 | | // All numbers stored in Intel order with least significant byte first. |
50 | | |
51 | | enum BMPComprMethod |
52 | | { |
53 | | BMPC_RGB = 0L, // Uncompressed |
54 | | BMPC_RLE8 = 1L, // RLE for 8 bpp images |
55 | | BMPC_RLE4 = 2L, // RLE for 4 bpp images |
56 | | BMPC_BITFIELDS = 3L, // Bitmap is not compressed and the colour table |
57 | | // consists of three DWORD color masks that specify |
58 | | // the red, green, and blue components of each pixel. |
59 | | // This is valid when used with 16- and 32-bpp bitmaps. |
60 | | BMPC_JPEG = 4L, // Indicates that the image is a JPEG image. |
61 | | BMPC_PNG = 5L // Indicates that the image is a PNG image. |
62 | | }; |
63 | | |
64 | | enum BMPLCSType // Type of logical color space. |
65 | | { |
66 | | BMPLT_CALIBRATED_RGB = 0, // This value indicates that endpoints and gamma |
67 | | // values are given in the appropriate fields. |
68 | | BMPLT_DEVICE_RGB = 1, |
69 | | BMPLT_DEVICE_CMYK = 2 |
70 | | }; |
71 | | |
72 | | typedef struct |
73 | | { |
74 | | // cppcheck-suppress unusedStructMember |
75 | | GInt32 iCIEX; |
76 | | // cppcheck-suppress unusedStructMember |
77 | | GInt32 iCIEY; |
78 | | // cppcheck-suppress unusedStructMember |
79 | | GInt32 iCIEZ; |
80 | | } BMPCIEXYZ; |
81 | | |
82 | | typedef struct // This structure contains the x, y, and z |
83 | | { // coordinates of the three colors that correspond |
84 | | // cppcheck-suppress unusedStructMember |
85 | | BMPCIEXYZ iCIERed; // to the red, green, and blue endpoints for |
86 | | // cppcheck-suppress unusedStructMember |
87 | | BMPCIEXYZ iCIEGreen; // a specified logical color space. |
88 | | // cppcheck-suppress unusedStructMember |
89 | | BMPCIEXYZ iCIEBlue; |
90 | | } BMPCIEXYZTriple; |
91 | | |
92 | | typedef struct |
93 | | { |
94 | | GByte bType[2]; // Signature "BM" |
95 | | GUInt32 iSize; // Size in bytes of the bitmap file. Should always |
96 | | // be ignored while reading because of error |
97 | | // in Windows 3.0 SDK's description of this field |
98 | | GUInt16 iReserved1; // Reserved, set as 0 |
99 | | GUInt16 iReserved2; // Reserved, set as 0 |
100 | | GUInt32 iOffBits; // Offset of the image from file start in bytes |
101 | | } BMPFileHeader; |
102 | | |
103 | | // File header size in bytes: |
104 | | constexpr int BFH_SIZE = 14; |
105 | | |
106 | | // Size of sInfoHeader.iSize in bytes |
107 | | constexpr int SIZE_OF_INFOHEADER_SIZE = 4; |
108 | | |
109 | | typedef struct |
110 | | { |
111 | | GUInt32 iSize; // Size of BMPInfoHeader structure in bytes. |
112 | | // Should be used to determine start of the |
113 | | // colour table |
114 | | GInt32 iWidth; // Image width |
115 | | GInt32 iHeight; // Image height. If positive, image has bottom left |
116 | | // origin, if negative --- top left. |
117 | | GUInt16 iPlanes; // Number of image planes (must be set to 1) |
118 | | GUInt16 iBitCount; // Number of bits per pixel (1, 4, 8, 16, 24 or 32). |
119 | | // If 0 then the number of bits per pixel is |
120 | | // specified or is implied by the JPEG or PNG format. |
121 | | BMPComprMethod iCompression; // Compression method |
122 | | GUInt32 iSizeImage; // Size of uncompressed image in bytes. May be 0 |
123 | | // for BMPC_RGB bitmaps. If iCompression is BI_JPEG |
124 | | // or BI_PNG, iSizeImage indicates the size |
125 | | // of the JPEG or PNG image buffer. |
126 | | GInt32 iXPelsPerMeter; // X resolution, pixels per meter (0 if not used) |
127 | | GInt32 iYPelsPerMeter; // Y resolution, pixels per meter (0 if not used) |
128 | | GUInt32 iClrUsed; // Size of colour table. If 0, iBitCount should |
129 | | // be used to calculate this value (1<<iBitCount) |
130 | | GUInt32 iClrImportant; // Number of important colours. If 0, all |
131 | | // colours are required |
132 | | |
133 | | // Fields above should be used for bitmaps, compatible with Windows NT 3.51 |
134 | | // and earlier. Windows 98/Me, Windows 2000/XP introduces additional fields: |
135 | | |
136 | | GUInt32 iRedMask; // Colour mask that specifies the red component |
137 | | // of each pixel, valid only if iCompression |
138 | | // is set to BI_BITFIELDS. |
139 | | GUInt32 iGreenMask; // The same for green component |
140 | | GUInt32 iBlueMask; // The same for blue component |
141 | | // cppcheck-suppress unusedStructMember |
142 | | GUInt32 iAlphaMask; // Colour mask that specifies the alpha |
143 | | // component of each pixel. |
144 | | // cppcheck-suppress unusedStructMember |
145 | | BMPLCSType iCSType; // Colour space of the DIB. |
146 | | // cppcheck-suppress unusedStructMember |
147 | | BMPCIEXYZTriple sEndpoints; // This member is ignored unless the iCSType |
148 | | // member specifies BMPLT_CALIBRATED_RGB. |
149 | | // cppcheck-suppress unusedStructMember |
150 | | GUInt32 iGammaRed; // Toned response curve for red. This member |
151 | | // is ignored unless color values are calibrated |
152 | | // RGB values and iCSType is set to |
153 | | // BMPLT_CALIBRATED_RGB. Specified in 16^16 format. |
154 | | // cppcheck-suppress unusedStructMember |
155 | | GUInt32 iGammaGreen; // Toned response curve for green. |
156 | | // cppcheck-suppress unusedStructMember |
157 | | GUInt32 iGammaBlue; // Toned response curve for blue. |
158 | | } BMPInfoHeader; |
159 | | |
160 | | // Info header size in bytes: |
161 | | constexpr unsigned int BIH_WIN4SIZE = 40; // for BMPT_WIN4 |
162 | | #if 0 |
163 | | /* Unused */ |
164 | | constexpr unsigned int BIH_WIN5SIZE = 57; // for BMPT_WIN5 |
165 | | #endif |
166 | | constexpr unsigned int BIH_OS21SIZE = 12; // for BMPT_OS21 |
167 | | constexpr unsigned int BIH_OS22SIZE = 64; // for BMPT_OS22 |
168 | | constexpr unsigned int BIH_BITMAPV5SIZE = 124; // for BITMAPV5HEADER |
169 | | |
170 | | // We will use plain byte array instead of this structure, but declaration |
171 | | // provided for reference |
172 | | typedef struct |
173 | | { |
174 | | // cppcheck-suppress unusedStructMember |
175 | | GByte bBlue; |
176 | | // cppcheck-suppress unusedStructMember |
177 | | GByte bGreen; |
178 | | // cppcheck-suppress unusedStructMember |
179 | | GByte bRed; |
180 | | // cppcheck-suppress unusedStructMember |
181 | | GByte bReserved; // Must be 0 |
182 | | } BMPColorEntry; |
183 | | |
184 | | /*****************************************************************/ |
185 | | |
186 | | static int countonbits(GUInt32 dw) |
187 | 0 | { |
188 | 0 | int r = 0; |
189 | 0 | for (int x = 0; x < 32; x++) |
190 | 0 | { |
191 | 0 | if ((dw & (1U << x)) != 0) |
192 | 0 | r++; |
193 | 0 | } |
194 | 0 | return r; |
195 | 0 | } |
196 | | |
197 | | static int findfirstonbit(GUInt32 n) |
198 | 0 | { |
199 | 0 | for (int x = 0; x < 32; x++) |
200 | 0 | { |
201 | 0 | if ((n & (1U << x)) != 0) |
202 | 0 | return x; |
203 | 0 | } |
204 | 0 | return -1; |
205 | 0 | } |
206 | | |
207 | | /************************************************************************/ |
208 | | /* ==================================================================== */ |
209 | | /* BMPDataset */ |
210 | | /* ==================================================================== */ |
211 | | /************************************************************************/ |
212 | | |
213 | | class BMPDataset final : public GDALPamDataset |
214 | | { |
215 | | friend class BMPRasterBand; |
216 | | friend class BMPComprRasterBand; |
217 | | |
218 | | BMPFileHeader sFileHeader; |
219 | | BMPInfoHeader sInfoHeader; |
220 | | int nColorElems; |
221 | | GByte *pabyColorTable; |
222 | | GDALColorTable *poColorTable; |
223 | | double adfGeoTransform[6]; |
224 | | int bGeoTransformValid; |
225 | | bool m_bNewFile = false; |
226 | | vsi_l_offset m_nFileSize = 0; |
227 | | |
228 | | char *pszFilename; |
229 | | VSILFILE *fp; |
230 | | |
231 | | protected: |
232 | | CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int, |
233 | | GDALDataType, int, BANDMAP_TYPE, GSpacing nPixelSpace, |
234 | | GSpacing nLineSpace, GSpacing nBandSpace, |
235 | | GDALRasterIOExtraArg *psExtraArg) override; |
236 | | |
237 | | public: |
238 | | BMPDataset(); |
239 | | ~BMPDataset() override; |
240 | | |
241 | | static int Identify(GDALOpenInfo *); |
242 | | static GDALDataset *Open(GDALOpenInfo *); |
243 | | static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize, |
244 | | int nBandsIn, GDALDataType eType, |
245 | | char **papszParamList); |
246 | | |
247 | | CPLErr GetGeoTransform(double *padfTransform) override; |
248 | | CPLErr SetGeoTransform(double *) override; |
249 | | }; |
250 | | |
251 | | /************************************************************************/ |
252 | | /* ==================================================================== */ |
253 | | /* BMPRasterBand */ |
254 | | /* ==================================================================== */ |
255 | | /************************************************************************/ |
256 | | |
257 | | class BMPRasterBand CPL_NON_FINAL : public GDALPamRasterBand |
258 | | { |
259 | | friend class BMPDataset; |
260 | | |
261 | | protected: |
262 | | GUInt32 nScanSize; |
263 | | unsigned int iBytesPerPixel; |
264 | | GByte *pabyScan; |
265 | | |
266 | | public: |
267 | | BMPRasterBand(BMPDataset *, int); |
268 | | ~BMPRasterBand() override; |
269 | | |
270 | | CPLErr IReadBlock(int, int, void *) override; |
271 | | CPLErr IWriteBlock(int, int, void *) override; |
272 | | GDALColorInterp GetColorInterpretation() override; |
273 | | GDALColorTable *GetColorTable() override; |
274 | | CPLErr SetColorTable(GDALColorTable *) override; |
275 | | }; |
276 | | |
277 | | /************************************************************************/ |
278 | | /* BMPRasterBand() */ |
279 | | /************************************************************************/ |
280 | | |
281 | | BMPRasterBand::BMPRasterBand(BMPDataset *poDSIn, int nBandIn) |
282 | 17 | : nScanSize(0), iBytesPerPixel(poDSIn->sInfoHeader.iBitCount / 8), |
283 | 17 | pabyScan(nullptr) |
284 | 17 | { |
285 | 17 | poDS = poDSIn; |
286 | 17 | nBand = nBandIn; |
287 | 17 | eDataType = GDT_Byte; |
288 | | |
289 | | // We will read one scanline per time. Scanlines in BMP aligned at 4-byte |
290 | | // boundary |
291 | 17 | nBlockXSize = poDS->GetRasterXSize(); |
292 | 17 | nBlockYSize = 1; |
293 | | |
294 | 17 | const auto knIntMax = std::numeric_limits<int>::max(); |
295 | 17 | if (nBlockXSize < (knIntMax - 31) / poDSIn->sInfoHeader.iBitCount) |
296 | 17 | { |
297 | 17 | nScanSize = |
298 | 17 | ((poDS->GetRasterXSize() * poDSIn->sInfoHeader.iBitCount + 31) & |
299 | 17 | ~31) / |
300 | 17 | 8; |
301 | 17 | } |
302 | 0 | else |
303 | 0 | { |
304 | | // pabyScan = NULL; |
305 | 0 | return; |
306 | 0 | } |
307 | | |
308 | | #ifdef BMP_DEBUG |
309 | | CPLDebug("BMP", "Band %d: set nBlockXSize=%d, nBlockYSize=%d, nScanSize=%d", |
310 | | nBand, nBlockXSize, nBlockYSize, nScanSize); |
311 | | #endif |
312 | | |
313 | 17 | pabyScan = static_cast<GByte *>(VSIMalloc(nScanSize)); |
314 | 17 | } |
315 | | |
316 | | /************************************************************************/ |
317 | | /* ~BMPRasterBand() */ |
318 | | /************************************************************************/ |
319 | | |
320 | | BMPRasterBand::~BMPRasterBand() |
321 | 17 | { |
322 | 17 | CPLFree(pabyScan); |
323 | 17 | } |
324 | | |
325 | | /************************************************************************/ |
326 | | /* IReadBlock() */ |
327 | | /************************************************************************/ |
328 | | |
329 | | CPLErr BMPRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff, |
330 | | void *pImage) |
331 | 1.28k | { |
332 | 1.28k | BMPDataset *poGDS = cpl::down_cast<BMPDataset *>(poDS); |
333 | 1.28k | vsi_l_offset iScanOffset = 0; |
334 | | |
335 | 1.28k | if (poGDS->sInfoHeader.iHeight > 0) |
336 | 0 | iScanOffset = poGDS->sFileHeader.iOffBits + |
337 | 0 | (poGDS->GetRasterYSize() - nBlockYOff - 1) * |
338 | 0 | static_cast<vsi_l_offset>(nScanSize); |
339 | 1.28k | else |
340 | 1.28k | iScanOffset = poGDS->sFileHeader.iOffBits + |
341 | 1.28k | nBlockYOff * static_cast<vsi_l_offset>(nScanSize); |
342 | | |
343 | 1.28k | if (VSIFSeekL(poGDS->fp, iScanOffset, SEEK_SET) < 0) |
344 | 0 | { |
345 | | // XXX: We will not report error here, because file just may be |
346 | | // in update state and data for this block will be available later. |
347 | 0 | if (poGDS->eAccess == GA_Update) |
348 | 0 | { |
349 | 0 | memset(pImage, 0, nBlockXSize); |
350 | 0 | return CE_None; |
351 | 0 | } |
352 | 0 | else |
353 | 0 | { |
354 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
355 | 0 | "Can't seek to offset " CPL_FRMT_GUIB |
356 | 0 | " in input file to read data.", |
357 | 0 | iScanOffset); |
358 | 0 | return CE_Failure; |
359 | 0 | } |
360 | 0 | } |
361 | 1.28k | if (VSIFReadL(pabyScan, 1, nScanSize, poGDS->fp) < nScanSize) |
362 | 3 | { |
363 | | // XXX |
364 | 3 | if (poGDS->eAccess == GA_Update) |
365 | 0 | { |
366 | 0 | memset(pImage, 0, nBlockXSize); |
367 | 0 | return CE_None; |
368 | 0 | } |
369 | 3 | else |
370 | 3 | { |
371 | 3 | CPLError(CE_Failure, CPLE_FileIO, |
372 | 3 | "Can't read from offset " CPL_FRMT_GUIB " in input file.", |
373 | 3 | iScanOffset); |
374 | 3 | return CE_Failure; |
375 | 3 | } |
376 | 3 | } |
377 | | |
378 | 1.28k | if (poGDS->sInfoHeader.iBitCount == 24 || |
379 | 1.28k | poGDS->sInfoHeader.iBitCount == 32) |
380 | 1.28k | { |
381 | 1.28k | GByte *pabyTemp = pabyScan + 3 - nBand; |
382 | | |
383 | 2.56k | for (int i = 0; i < nBlockXSize; i++) |
384 | 1.28k | { |
385 | | // Colour triplets in BMP file organized in reverse order: |
386 | | // blue, green, red. When we have 32-bit BMP the forth byte |
387 | | // in quadruplet should be discarded as it has no meaning. |
388 | | // That is why we always use 3 byte count in the following |
389 | | // pabyTemp index. |
390 | 1.28k | static_cast<GByte *>(pImage)[i] = *pabyTemp; |
391 | 1.28k | pabyTemp += iBytesPerPixel; |
392 | 1.28k | } |
393 | 1.28k | } |
394 | 0 | else if (poGDS->sInfoHeader.iBitCount == 8) |
395 | 0 | { |
396 | 0 | memcpy(pImage, pabyScan, nBlockXSize); |
397 | 0 | } |
398 | 0 | else if (poGDS->sInfoHeader.iBitCount == 16) |
399 | 0 | { |
400 | | // rcg, oct 7/06: Byteswap if necessary, use int16 |
401 | | // references to file pixels, expand samples to |
402 | | // 8-bit, support BMPC_BITFIELDS channel mask indicators, |
403 | | // and generalize band handling. |
404 | |
|
405 | 0 | GUInt16 *pScan16 = reinterpret_cast<GUInt16 *>(pabyScan); |
406 | | #ifdef CPL_MSB |
407 | | GDALSwapWords(pScan16, sizeof(GUInt16), nBlockXSize, 0); |
408 | | #endif |
409 | | |
410 | | // todo: make these band members and precompute. |
411 | 0 | int mask[3], shift[3], size[3]; |
412 | 0 | float fTo8bit[3]; |
413 | |
|
414 | 0 | if (poGDS->sInfoHeader.iCompression == BMPC_RGB) |
415 | 0 | { |
416 | 0 | mask[0] = 0x7c00; |
417 | 0 | mask[1] = 0x03e0; |
418 | 0 | mask[2] = 0x001f; |
419 | 0 | } |
420 | 0 | else if (poGDS->sInfoHeader.iCompression == BMPC_BITFIELDS) |
421 | 0 | { |
422 | 0 | mask[0] = poGDS->sInfoHeader.iRedMask; |
423 | 0 | mask[1] = poGDS->sInfoHeader.iGreenMask; |
424 | 0 | mask[2] = poGDS->sInfoHeader.iBlueMask; |
425 | 0 | } |
426 | 0 | else |
427 | 0 | { |
428 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Unknown 16-bit compression %d.", |
429 | 0 | poGDS->sInfoHeader.iCompression); |
430 | 0 | return CE_Failure; |
431 | 0 | } |
432 | | |
433 | 0 | for (int i = 0; i < 3; i++) |
434 | 0 | { |
435 | 0 | shift[i] = findfirstonbit(mask[i]); |
436 | 0 | size[i] = countonbits(mask[i]); |
437 | 0 | if (size[i] > 14 || size[i] == 0) |
438 | 0 | { |
439 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
440 | 0 | "Bad 16-bit channel mask %8x.", mask[i]); |
441 | 0 | return CE_Failure; |
442 | 0 | } |
443 | 0 | fTo8bit[i] = 255.0f / ((1 << size[i]) - 1); |
444 | 0 | } |
445 | | |
446 | 0 | for (int i = 0; i < nBlockXSize; i++) |
447 | 0 | { |
448 | 0 | ((GByte *)pImage)[i] = |
449 | 0 | (GByte)(0.5f + |
450 | 0 | fTo8bit[nBand - 1] * ((pScan16[i] & mask[nBand - 1]) >> |
451 | 0 | shift[nBand - 1])); |
452 | | #if 0 |
453 | | // original code |
454 | | switch ( nBand ) |
455 | | { |
456 | | case 1: // Red |
457 | | ((GByte *) pImage)[i] = pabyScan[i + 1] & 0x1F; |
458 | | break; |
459 | | |
460 | | case 2: // Green |
461 | | ((GByte *) pImage)[i] = |
462 | | ((pabyScan[i] & 0x03) << 3) | |
463 | | ((pabyScan[i + 1] & 0xE0) >> 5); |
464 | | break; |
465 | | |
466 | | case 3: // Blue |
467 | | ((GByte *) pImage)[i] = (pabyScan[i] & 0x7c) >> 2; |
468 | | break; |
469 | | default: |
470 | | break; |
471 | | } |
472 | | #endif // 0 |
473 | 0 | } |
474 | 0 | } |
475 | 0 | else if (poGDS->sInfoHeader.iBitCount == 4) |
476 | 0 | { |
477 | 0 | GByte *pabyTemp = pabyScan; |
478 | |
|
479 | 0 | for (int i = 0; i < nBlockXSize; i++) |
480 | 0 | { |
481 | | // Most significant part of the byte represents leftmost pixel |
482 | 0 | if (i & 0x01) |
483 | 0 | ((GByte *)pImage)[i] = *pabyTemp++ & 0x0F; |
484 | 0 | else |
485 | 0 | ((GByte *)pImage)[i] = (*pabyTemp & 0xF0) >> 4; |
486 | 0 | } |
487 | 0 | } |
488 | 0 | else if (poGDS->sInfoHeader.iBitCount == 1) |
489 | 0 | { |
490 | 0 | GByte *pabyTemp = pabyScan; |
491 | |
|
492 | 0 | for (int i = 0; i < nBlockXSize; i++) |
493 | 0 | { |
494 | 0 | switch (i & 0x7) |
495 | 0 | { |
496 | 0 | case 0: |
497 | 0 | ((GByte *)pImage)[i] = (*pabyTemp & 0x80) >> 7; |
498 | 0 | break; |
499 | 0 | case 1: |
500 | 0 | ((GByte *)pImage)[i] = (*pabyTemp & 0x40) >> 6; |
501 | 0 | break; |
502 | 0 | case 2: |
503 | 0 | ((GByte *)pImage)[i] = (*pabyTemp & 0x20) >> 5; |
504 | 0 | break; |
505 | 0 | case 3: |
506 | 0 | ((GByte *)pImage)[i] = (*pabyTemp & 0x10) >> 4; |
507 | 0 | break; |
508 | 0 | case 4: |
509 | 0 | ((GByte *)pImage)[i] = (*pabyTemp & 0x08) >> 3; |
510 | 0 | break; |
511 | 0 | case 5: |
512 | 0 | ((GByte *)pImage)[i] = (*pabyTemp & 0x04) >> 2; |
513 | 0 | break; |
514 | 0 | case 6: |
515 | 0 | ((GByte *)pImage)[i] = (*pabyTemp & 0x02) >> 1; |
516 | 0 | break; |
517 | 0 | case 7: |
518 | 0 | ((GByte *)pImage)[i] = *pabyTemp++ & 0x01; |
519 | 0 | break; |
520 | 0 | default: |
521 | 0 | break; |
522 | 0 | } |
523 | 0 | } |
524 | 0 | } |
525 | | |
526 | 1.28k | return CE_None; |
527 | 1.28k | } |
528 | | |
529 | | /************************************************************************/ |
530 | | /* IWriteBlock() */ |
531 | | /************************************************************************/ |
532 | | |
533 | | CPLErr BMPRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage) |
534 | 0 | { |
535 | 0 | BMPDataset *poGDS = cpl::down_cast<BMPDataset *>(poDS); |
536 | |
|
537 | 0 | CPLAssert(poGDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 && |
538 | 0 | pImage != nullptr); |
539 | |
|
540 | 0 | vsi_l_offset iScanOffset = poGDS->sFileHeader.iOffBits + |
541 | 0 | (poGDS->GetRasterYSize() - nBlockYOff - 1) * |
542 | 0 | static_cast<vsi_l_offset>(nScanSize); |
543 | 0 | if (VSIFSeekL(poGDS->fp, iScanOffset, SEEK_SET) < 0) |
544 | 0 | { |
545 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
546 | 0 | "Can't seek to offset " CPL_FRMT_GUIB |
547 | 0 | " in output file to write data.\n%s", |
548 | 0 | iScanOffset, VSIStrerror(errno)); |
549 | 0 | return CE_Failure; |
550 | 0 | } |
551 | | |
552 | 0 | if (poGDS->nBands != 1) |
553 | 0 | { |
554 | 0 | memset(pabyScan, 0, nScanSize); |
555 | 0 | VSIFReadL(pabyScan, 1, nScanSize, poGDS->fp); |
556 | 0 | VSIFSeekL(poGDS->fp, iScanOffset, SEEK_SET); |
557 | 0 | } |
558 | |
|
559 | 0 | for (int iInPixel = 0, iOutPixel = iBytesPerPixel - nBand; |
560 | 0 | iInPixel < nBlockXSize; iInPixel++, iOutPixel += poGDS->nBands) |
561 | 0 | { |
562 | 0 | pabyScan[iOutPixel] = ((GByte *)pImage)[iInPixel]; |
563 | 0 | } |
564 | |
|
565 | 0 | if (VSIFWriteL(pabyScan, 1, nScanSize, poGDS->fp) < nScanSize) |
566 | 0 | { |
567 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
568 | 0 | "Can't write block with X offset %d and Y offset %d.\n%s", |
569 | 0 | nBlockXOff, nBlockYOff, VSIStrerror(errno)); |
570 | 0 | return CE_Failure; |
571 | 0 | } |
572 | | |
573 | 0 | return CE_None; |
574 | 0 | } |
575 | | |
576 | | /************************************************************************/ |
577 | | /* GetColorTable() */ |
578 | | /************************************************************************/ |
579 | | |
580 | | GDALColorTable *BMPRasterBand::GetColorTable() |
581 | 0 | { |
582 | 0 | BMPDataset *poGDS = cpl::down_cast<BMPDataset *>(poDS); |
583 | |
|
584 | 0 | return poGDS->poColorTable; |
585 | 0 | } |
586 | | |
587 | | /************************************************************************/ |
588 | | /* SetColorTable() */ |
589 | | /************************************************************************/ |
590 | | |
591 | | CPLErr BMPRasterBand::SetColorTable(GDALColorTable *poColorTable) |
592 | 0 | { |
593 | 0 | BMPDataset *poGDS = cpl::down_cast<BMPDataset *>(poDS); |
594 | |
|
595 | 0 | if (poColorTable) |
596 | 0 | { |
597 | 0 | poGDS->sInfoHeader.iClrUsed = poColorTable->GetColorEntryCount(); |
598 | 0 | if (poGDS->sInfoHeader.iClrUsed < 1 || |
599 | 0 | poGDS->sInfoHeader.iClrUsed > (1U << poGDS->sInfoHeader.iBitCount)) |
600 | 0 | return CE_Failure; |
601 | | |
602 | 0 | VSIFSeekL(poGDS->fp, BFH_SIZE + 32, SEEK_SET); |
603 | |
|
604 | 0 | GUInt32 iULong = CPL_LSBWORD32(poGDS->sInfoHeader.iClrUsed); |
605 | 0 | VSIFWriteL(&iULong, 4, 1, poGDS->fp); |
606 | 0 | poGDS->pabyColorTable = (GByte *)CPLRealloc( |
607 | 0 | poGDS->pabyColorTable, static_cast<size_t>(poGDS->nColorElems) * |
608 | 0 | poGDS->sInfoHeader.iClrUsed); |
609 | 0 | if (!poGDS->pabyColorTable) |
610 | 0 | return CE_Failure; |
611 | | |
612 | 0 | for (unsigned int i = 0; i < poGDS->sInfoHeader.iClrUsed; i++) |
613 | 0 | { |
614 | 0 | GDALColorEntry oEntry; |
615 | |
|
616 | 0 | poColorTable->GetColorEntryAsRGB(i, &oEntry); |
617 | 0 | poGDS->pabyColorTable[i * poGDS->nColorElems + 3] = 0; |
618 | 0 | poGDS->pabyColorTable[i * poGDS->nColorElems + 2] = |
619 | 0 | (GByte)oEntry.c1; // Red |
620 | 0 | poGDS->pabyColorTable[i * poGDS->nColorElems + 1] = |
621 | 0 | (GByte)oEntry.c2; // Green |
622 | 0 | poGDS->pabyColorTable[i * poGDS->nColorElems] = |
623 | 0 | (GByte)oEntry.c3; // Blue |
624 | 0 | } |
625 | |
|
626 | 0 | VSIFSeekL(poGDS->fp, BFH_SIZE + poGDS->sInfoHeader.iSize, SEEK_SET); |
627 | 0 | if (VSIFWriteL(poGDS->pabyColorTable, 1, |
628 | 0 | static_cast<size_t>(poGDS->nColorElems) * |
629 | 0 | poGDS->sInfoHeader.iClrUsed, |
630 | 0 | poGDS->fp) < static_cast<size_t>(poGDS->nColorElems) * |
631 | 0 | poGDS->sInfoHeader.iClrUsed) |
632 | 0 | { |
633 | 0 | return CE_Failure; |
634 | 0 | } |
635 | 0 | } |
636 | 0 | else |
637 | 0 | return CE_Failure; |
638 | | |
639 | 0 | return CE_None; |
640 | 0 | } |
641 | | |
642 | | /************************************************************************/ |
643 | | /* GetColorInterpretation() */ |
644 | | /************************************************************************/ |
645 | | |
646 | | GDALColorInterp BMPRasterBand::GetColorInterpretation() |
647 | 0 | { |
648 | 0 | BMPDataset *poGDS = cpl::down_cast<BMPDataset *>(poDS); |
649 | |
|
650 | 0 | if (poGDS->sInfoHeader.iBitCount == 24 || |
651 | 0 | poGDS->sInfoHeader.iBitCount == 32 || |
652 | 0 | poGDS->sInfoHeader.iBitCount == 16) |
653 | 0 | { |
654 | 0 | if (nBand == 1) |
655 | 0 | return GCI_RedBand; |
656 | 0 | else if (nBand == 2) |
657 | 0 | return GCI_GreenBand; |
658 | 0 | else if (nBand == 3) |
659 | 0 | return GCI_BlueBand; |
660 | 0 | else |
661 | 0 | return GCI_Undefined; |
662 | 0 | } |
663 | 0 | else |
664 | 0 | { |
665 | 0 | return GCI_PaletteIndex; |
666 | 0 | } |
667 | 0 | } |
668 | | |
669 | | /************************************************************************/ |
670 | | /* ==================================================================== */ |
671 | | /* BMPComprRasterBand */ |
672 | | /* ==================================================================== */ |
673 | | /************************************************************************/ |
674 | | |
675 | | class BMPComprRasterBand final : public BMPRasterBand |
676 | | { |
677 | | friend class BMPDataset; |
678 | | |
679 | | GByte *pabyComprBuf; |
680 | | GByte *pabyUncomprBuf; |
681 | | |
682 | | public: |
683 | | BMPComprRasterBand(BMPDataset *, int); |
684 | | ~BMPComprRasterBand() override; |
685 | | |
686 | | CPLErr IReadBlock(int, int, void *) override; |
687 | | // virtual CPLErr IWriteBlock( int, int, void * ); |
688 | | }; |
689 | | |
690 | | /************************************************************************/ |
691 | | /* BMPComprRasterBand() */ |
692 | | /************************************************************************/ |
693 | | |
694 | | BMPComprRasterBand::BMPComprRasterBand(BMPDataset *poDSIn, int nBandIn) |
695 | 14 | : BMPRasterBand(poDSIn, nBandIn), pabyComprBuf(nullptr), |
696 | 14 | pabyUncomprBuf(nullptr) |
697 | 14 | { |
698 | | /* TODO: it might be interesting to avoid uncompressing the whole data */ |
699 | | /* in a single pass, especially if nXSize * nYSize is big */ |
700 | | /* We could read incrementally one row at a time */ |
701 | 14 | const auto knIntMax = std::numeric_limits<int>::max(); |
702 | 14 | if (poDS->GetRasterXSize() > knIntMax / poDS->GetRasterYSize()) |
703 | 0 | { |
704 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Too big dimensions : %d x %d", |
705 | 0 | poDS->GetRasterXSize(), poDS->GetRasterYSize()); |
706 | 0 | return; |
707 | 0 | } |
708 | | |
709 | 14 | if (poDSIn->m_nFileSize <= poDSIn->sFileHeader.iOffBits || |
710 | 14 | poDSIn->m_nFileSize - poDSIn->sFileHeader.iOffBits > knIntMax) |
711 | 0 | { |
712 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Invalid header"); |
713 | 0 | return; |
714 | 0 | } |
715 | | |
716 | 14 | const GUInt32 iComprSize = static_cast<GUInt32>( |
717 | 14 | poDSIn->m_nFileSize - poDSIn->sFileHeader.iOffBits); |
718 | 14 | const GUInt32 iUncomprSize = |
719 | 14 | poDS->GetRasterXSize() * poDS->GetRasterYSize(); |
720 | | |
721 | | #ifdef DEBUG |
722 | | CPLDebug("BMP", "RLE compression detected."); |
723 | | CPLDebug("BMP", |
724 | | "Size of compressed buffer %ld bytes," |
725 | | " size of uncompressed buffer %ld bytes.", |
726 | | (long)iComprSize, (long)iUncomprSize); |
727 | | #endif |
728 | | |
729 | 14 | pabyComprBuf = (GByte *)VSIMalloc(iComprSize); |
730 | 14 | pabyUncomprBuf = (GByte *)VSIMalloc(iUncomprSize); |
731 | 14 | if (pabyComprBuf == nullptr || pabyUncomprBuf == nullptr) |
732 | 0 | { |
733 | 0 | CPLFree(pabyComprBuf); |
734 | 0 | pabyComprBuf = nullptr; |
735 | 0 | CPLFree(pabyUncomprBuf); |
736 | 0 | pabyUncomprBuf = nullptr; |
737 | 0 | return; |
738 | 0 | } |
739 | | |
740 | 14 | if (VSIFSeekL(poDSIn->fp, poDSIn->sFileHeader.iOffBits, SEEK_SET) != 0 || |
741 | 14 | VSIFReadL(pabyComprBuf, 1, iComprSize, poDSIn->fp) < iComprSize) |
742 | 0 | { |
743 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
744 | 0 | "Can't read from offset %ld in input file.", |
745 | 0 | (long)poDSIn->sFileHeader.iOffBits); |
746 | 0 | CPLFree(pabyComprBuf); |
747 | 0 | pabyComprBuf = nullptr; |
748 | 0 | CPLFree(pabyUncomprBuf); |
749 | 0 | pabyUncomprBuf = nullptr; |
750 | 0 | return; |
751 | 0 | } |
752 | | |
753 | 14 | unsigned int i = 0; |
754 | 14 | unsigned int j = 0; |
755 | 14 | if (poDSIn->sInfoHeader.iBitCount == 8) // RLE8 |
756 | 13 | { |
757 | 5.49k | while (i < iComprSize) |
758 | 5.49k | { |
759 | 5.49k | if (pabyComprBuf[i]) |
760 | 4.68k | { |
761 | 4.68k | unsigned int iLength = pabyComprBuf[i++]; |
762 | 4.68k | if (j == iUncomprSize) |
763 | 1 | break; |
764 | 378k | while (iLength > 0 && j < iUncomprSize && i < iComprSize) |
765 | 373k | { |
766 | 373k | pabyUncomprBuf[j++] = pabyComprBuf[i]; |
767 | 373k | iLength--; |
768 | 373k | } |
769 | 4.68k | i++; |
770 | 4.68k | } |
771 | 809 | else |
772 | 809 | { |
773 | 809 | i++; |
774 | 809 | if (i == iComprSize) |
775 | 1 | break; |
776 | 808 | if (pabyComprBuf[i] == 0) // Next scanline |
777 | 627 | { |
778 | 627 | i++; |
779 | 627 | } |
780 | 181 | else if (pabyComprBuf[i] == 1) // End of image |
781 | 2 | { |
782 | 2 | break; |
783 | 2 | } |
784 | 179 | else if (pabyComprBuf[i] == 2) // Move to... |
785 | 34 | { |
786 | 34 | if (j == iUncomprSize) |
787 | 0 | break; |
788 | 34 | i++; |
789 | 34 | if (i < iComprSize - 1) |
790 | 34 | { |
791 | 34 | if (pabyComprBuf[i + 1] > |
792 | 34 | knIntMax / poDS->GetRasterXSize() || |
793 | 34 | static_cast<int>(pabyComprBuf[i + 1]) * |
794 | 33 | poDS->GetRasterXSize() > |
795 | 33 | knIntMax - |
796 | 33 | static_cast<int>(j + pabyComprBuf[i])) |
797 | 4 | break; |
798 | 30 | j += pabyComprBuf[i] + |
799 | 30 | pabyComprBuf[i + 1] * poDS->GetRasterXSize(); |
800 | 30 | i += 2; |
801 | 30 | } |
802 | 0 | else |
803 | 0 | break; |
804 | 34 | } |
805 | 145 | else // Absolute mode |
806 | 145 | { |
807 | 145 | CPLAssert(i < iComprSize); |
808 | 145 | unsigned int iLength = pabyComprBuf[i++]; |
809 | 145 | if (j == iUncomprSize) |
810 | 0 | break; |
811 | 145 | for (unsigned k = 0; |
812 | 6.21k | k < iLength && j < iUncomprSize && i < iComprSize; k++) |
813 | 6.06k | pabyUncomprBuf[j++] = pabyComprBuf[i++]; |
814 | 145 | if (i & 0x01) |
815 | 53 | i++; |
816 | 145 | } |
817 | 808 | } |
818 | 5.49k | } |
819 | 13 | } |
820 | 1 | else // RLE4 |
821 | 1 | { |
822 | 2.48k | while (i < iComprSize) |
823 | 2.48k | { |
824 | 2.48k | if (pabyComprBuf[i]) |
825 | 1.98k | { |
826 | 1.98k | unsigned int iLength = pabyComprBuf[i++]; |
827 | 1.98k | if (j == iUncomprSize) |
828 | 0 | break; |
829 | 209k | while (iLength > 0 && j < iUncomprSize && i < iComprSize) |
830 | 207k | { |
831 | 207k | if (iLength & 0x01) |
832 | 103k | pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4; |
833 | 103k | else |
834 | 103k | pabyUncomprBuf[j++] = pabyComprBuf[i] & 0x0F; |
835 | 207k | iLength--; |
836 | 207k | } |
837 | 1.98k | i++; |
838 | 1.98k | } |
839 | 500 | else |
840 | 500 | { |
841 | 500 | i++; |
842 | 500 | if (i == iComprSize) |
843 | 0 | break; |
844 | 500 | if (pabyComprBuf[i] == 0) // Next scanline |
845 | 444 | { |
846 | 444 | i++; |
847 | 444 | } |
848 | 56 | else if (pabyComprBuf[i] == 1) // End of image |
849 | 0 | { |
850 | 0 | break; |
851 | 0 | } |
852 | 56 | else if (pabyComprBuf[i] == 2) // Move to... |
853 | 0 | { |
854 | 0 | if (j == iUncomprSize) |
855 | 0 | break; |
856 | 0 | i++; |
857 | 0 | if (i < iComprSize - 1) |
858 | 0 | { |
859 | 0 | if (pabyComprBuf[i + 1] > |
860 | 0 | knIntMax / poDS->GetRasterXSize() || |
861 | 0 | static_cast<int>(pabyComprBuf[i + 1]) * |
862 | 0 | poDS->GetRasterXSize() > |
863 | 0 | knIntMax - |
864 | 0 | static_cast<int>(j + pabyComprBuf[i])) |
865 | 0 | break; |
866 | 0 | j += pabyComprBuf[i] + |
867 | 0 | pabyComprBuf[i + 1] * poDS->GetRasterXSize(); |
868 | 0 | i += 2; |
869 | 0 | } |
870 | 0 | else |
871 | 0 | break; |
872 | 0 | } |
873 | 56 | else // Absolute mode |
874 | 56 | { |
875 | 56 | CPLAssert(i < iComprSize); |
876 | 56 | unsigned int iLength = pabyComprBuf[i++]; |
877 | 56 | if (j == iUncomprSize) |
878 | 0 | break; |
879 | 56 | for (unsigned k = 0; |
880 | 6.34k | k < iLength && j < iUncomprSize && i < iComprSize; k++) |
881 | 6.29k | { |
882 | 6.29k | if (k & 0x01) |
883 | 3.14k | pabyUncomprBuf[j++] = pabyComprBuf[i++] & 0x0F; |
884 | 3.15k | else |
885 | 3.15k | pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4; |
886 | 6.29k | } |
887 | 56 | if (i & 0x01) |
888 | 26 | i++; |
889 | 56 | } |
890 | 500 | } |
891 | 2.48k | } |
892 | 1 | } |
893 | | /* Validate that we have read all compressed data (we tolerate missing */ |
894 | | /* end of image marker) and that we have filled all uncompressed data */ |
895 | 14 | if (j < iUncomprSize || (i + 1 != iComprSize && i + 2 != iComprSize)) |
896 | 14 | { |
897 | 14 | CPLFree(pabyUncomprBuf); |
898 | 14 | pabyUncomprBuf = nullptr; |
899 | 14 | } |
900 | | // rcg, release compressed buffer here. |
901 | 14 | CPLFree(pabyComprBuf); |
902 | 14 | pabyComprBuf = nullptr; |
903 | 14 | } |
904 | | |
905 | | /************************************************************************/ |
906 | | /* ~BMPComprRasterBand() */ |
907 | | /************************************************************************/ |
908 | | |
909 | | BMPComprRasterBand::~BMPComprRasterBand() |
910 | 14 | { |
911 | 14 | CPLFree(pabyComprBuf); |
912 | 14 | CPLFree(pabyUncomprBuf); |
913 | 14 | } |
914 | | |
915 | | /************************************************************************/ |
916 | | /* IReadBlock() */ |
917 | | /************************************************************************/ |
918 | | |
919 | | CPLErr BMPComprRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff, |
920 | | void *pImage) |
921 | 0 | { |
922 | 0 | memcpy(pImage, |
923 | 0 | pabyUncomprBuf + (poDS->GetRasterYSize() - nBlockYOff - 1) * |
924 | 0 | poDS->GetRasterXSize(), |
925 | 0 | nBlockXSize); |
926 | |
|
927 | 0 | return CE_None; |
928 | 0 | } |
929 | | |
930 | | /************************************************************************/ |
931 | | /* BMPDataset() */ |
932 | | /************************************************************************/ |
933 | | |
934 | | BMPDataset::BMPDataset() |
935 | 18 | : nColorElems(0), pabyColorTable(nullptr), poColorTable(nullptr), |
936 | 18 | bGeoTransformValid(FALSE), pszFilename(nullptr), fp(nullptr) |
937 | 18 | { |
938 | 18 | nBands = 0; |
939 | | |
940 | 18 | memset(&sFileHeader, 0, sizeof(sFileHeader)); |
941 | 18 | memset(&sInfoHeader, 0, sizeof(sInfoHeader)); |
942 | | |
943 | 18 | adfGeoTransform[0] = 0.0; |
944 | 18 | adfGeoTransform[1] = 1.0; |
945 | 18 | adfGeoTransform[2] = 0.0; |
946 | 18 | adfGeoTransform[3] = 0.0; |
947 | 18 | adfGeoTransform[4] = 0.0; |
948 | 18 | adfGeoTransform[5] = 1.0; |
949 | 18 | } |
950 | | |
951 | | /************************************************************************/ |
952 | | /* ~BMPDataset() */ |
953 | | /************************************************************************/ |
954 | | |
955 | | BMPDataset::~BMPDataset() |
956 | 18 | { |
957 | 18 | FlushCache(true); |
958 | | |
959 | 18 | if (m_bNewFile && fp) |
960 | 0 | { |
961 | | // Extend the file with zeroes if needed |
962 | 0 | VSIFSeekL(fp, 0, SEEK_END); |
963 | |
|
964 | 0 | if (VSIFTellL(fp) < m_nFileSize) |
965 | 0 | { |
966 | 0 | VSIFTruncateL(fp, m_nFileSize); |
967 | 0 | } |
968 | 0 | } |
969 | | |
970 | 18 | CPLFree(pabyColorTable); |
971 | 18 | if (poColorTable) |
972 | 15 | delete poColorTable; |
973 | 18 | CPLFree(pszFilename); |
974 | 18 | if (fp) |
975 | 18 | VSIFCloseL(fp); |
976 | 18 | } |
977 | | |
978 | | /************************************************************************/ |
979 | | /* GetGeoTransform() */ |
980 | | /************************************************************************/ |
981 | | |
982 | | CPLErr BMPDataset::GetGeoTransform(double *padfTransform) |
983 | 1 | { |
984 | 1 | if (bGeoTransformValid) |
985 | 0 | { |
986 | 0 | memcpy(padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6); |
987 | 0 | return CE_None; |
988 | 0 | } |
989 | | |
990 | 1 | if (GDALPamDataset::GetGeoTransform(padfTransform) == CE_None) |
991 | 0 | return CE_None; |
992 | | |
993 | | #ifdef notdef |
994 | | // See http://trac.osgeo.org/gdal/ticket/3578 |
995 | | if (sInfoHeader.iXPelsPerMeter > 0 && sInfoHeader.iYPelsPerMeter > 0) |
996 | | { |
997 | | padfTransform[1] = sInfoHeader.iXPelsPerMeter; |
998 | | padfTransform[5] = -sInfoHeader.iYPelsPerMeter; |
999 | | padfTransform[0] = -0.5 * padfTransform[1]; |
1000 | | padfTransform[3] = -0.5 * padfTransform[5]; |
1001 | | return CE_None; |
1002 | | } |
1003 | | #endif |
1004 | | |
1005 | 1 | return CE_Failure; |
1006 | 1 | } |
1007 | | |
1008 | | /************************************************************************/ |
1009 | | /* SetGeoTransform() */ |
1010 | | /************************************************************************/ |
1011 | | |
1012 | | CPLErr BMPDataset::SetGeoTransform(double *padfTransform) |
1013 | 0 | { |
1014 | 0 | if (pszFilename && bGeoTransformValid) |
1015 | 0 | { |
1016 | 0 | memcpy(adfGeoTransform, padfTransform, sizeof(double) * 6); |
1017 | |
|
1018 | 0 | CPLErr eErr = CE_None; |
1019 | 0 | if (GDALWriteWorldFile(pszFilename, "wld", adfGeoTransform) == FALSE) |
1020 | 0 | { |
1021 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Can't write world file."); |
1022 | 0 | eErr = CE_Failure; |
1023 | 0 | } |
1024 | 0 | return eErr; |
1025 | 0 | } |
1026 | | |
1027 | 0 | return GDALPamDataset::SetGeoTransform(padfTransform); |
1028 | 0 | } |
1029 | | |
1030 | | /************************************************************************/ |
1031 | | /* IRasterIO() */ |
1032 | | /* */ |
1033 | | /* Multi-band raster io handler. We will use block based */ |
1034 | | /* loading is used for multiband BMPs. That is because they */ |
1035 | | /* are effectively pixel interleaved, so processing all bands */ |
1036 | | /* for a given block together avoid extra seeks. */ |
1037 | | /************************************************************************/ |
1038 | | |
1039 | | CPLErr BMPDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, |
1040 | | int nXSize, int nYSize, void *pData, int nBufXSize, |
1041 | | int nBufYSize, GDALDataType eBufType, |
1042 | | int nBandCount, BANDMAP_TYPE panBandMap, |
1043 | | GSpacing nPixelSpace, GSpacing nLineSpace, |
1044 | | GSpacing nBandSpace, |
1045 | | GDALRasterIOExtraArg *psExtraArg) |
1046 | | |
1047 | 0 | { |
1048 | 0 | if (nBandCount > 1) |
1049 | 0 | return GDALDataset::BlockBasedRasterIO( |
1050 | 0 | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
1051 | 0 | eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, |
1052 | 0 | nBandSpace, psExtraArg); |
1053 | 0 | else |
1054 | 0 | return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, |
1055 | 0 | pData, nBufXSize, nBufYSize, eBufType, |
1056 | 0 | nBandCount, panBandMap, nPixelSpace, |
1057 | 0 | nLineSpace, nBandSpace, psExtraArg); |
1058 | 0 | } |
1059 | | |
1060 | | /************************************************************************/ |
1061 | | /* Identify() */ |
1062 | | /************************************************************************/ |
1063 | | |
1064 | | int BMPDataset::Identify(GDALOpenInfo *poOpenInfo) |
1065 | | |
1066 | 192k | { |
1067 | 192k | if (poOpenInfo->nHeaderBytes < BFH_SIZE + SIZE_OF_INFOHEADER_SIZE || |
1068 | 192k | poOpenInfo->pabyHeader[0] != 'B' || poOpenInfo->pabyHeader[1] != 'M' || |
1069 | 192k | poOpenInfo->pabyHeader[6] != 0 || poOpenInfo->pabyHeader[7] != 0 || |
1070 | 192k | poOpenInfo->pabyHeader[8] != 0 || poOpenInfo->pabyHeader[9] != 0) |
1071 | 192k | return FALSE; |
1072 | | |
1073 | 36 | uint32_t nInfoHeaderSize; |
1074 | 36 | memcpy(&nInfoHeaderSize, poOpenInfo->pabyHeader + BFH_SIZE, |
1075 | 36 | sizeof(uint32_t)); |
1076 | 36 | CPL_LSBPTR32(&nInfoHeaderSize); |
1077 | | // Check against the maximum known size |
1078 | 36 | if (nInfoHeaderSize > BIH_BITMAPV5SIZE) |
1079 | 0 | return FALSE; |
1080 | | |
1081 | 36 | return TRUE; |
1082 | 36 | } |
1083 | | |
1084 | | /************************************************************************/ |
1085 | | /* Open() */ |
1086 | | /************************************************************************/ |
1087 | | |
1088 | | GDALDataset *BMPDataset::Open(GDALOpenInfo *poOpenInfo) |
1089 | 18 | { |
1090 | 18 | if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr) |
1091 | 0 | return nullptr; |
1092 | | |
1093 | | /* -------------------------------------------------------------------- */ |
1094 | | /* Create a corresponding GDALDataset. */ |
1095 | | /* -------------------------------------------------------------------- */ |
1096 | 18 | BMPDataset *poDS = new BMPDataset(); |
1097 | 18 | poDS->eAccess = poOpenInfo->eAccess; |
1098 | | |
1099 | 18 | VSIStatBufL sStat; |
1100 | 18 | if (VSIStatL(poOpenInfo->pszFilename, &sStat) != 0) |
1101 | 0 | { |
1102 | 0 | delete poDS; |
1103 | 0 | return nullptr; |
1104 | 0 | } |
1105 | | |
1106 | | /* -------------------------------------------------------------------- */ |
1107 | | /* Read the BMPFileHeader. We need iOffBits value only */ |
1108 | | /* -------------------------------------------------------------------- */ |
1109 | 18 | memcpy(&poDS->sFileHeader.iOffBits, poOpenInfo->pabyHeader + 10, 4); |
1110 | | #ifdef CPL_MSB |
1111 | | CPL_SWAP32PTR(&poDS->sFileHeader.iOffBits); |
1112 | | #endif |
1113 | 18 | poDS->m_nFileSize = sStat.st_size; |
1114 | | |
1115 | | #ifdef BMP_DEBUG |
1116 | | CPLDebug("BMP", "File size " CPL_FRMT_GUIB " bytes.", |
1117 | | static_cast<GUIntBig>(poDS->m_nFileSize)); |
1118 | | CPLDebug("BMP", "Image offset 0x%x bytes from file start.", |
1119 | | poDS->sFileHeader.iOffBits); |
1120 | | #endif |
1121 | | |
1122 | | // Validatate iOffBits |
1123 | 18 | if (poDS->sFileHeader.iOffBits <= BFH_SIZE + SIZE_OF_INFOHEADER_SIZE || |
1124 | 18 | poDS->sFileHeader.iOffBits >= poDS->m_nFileSize) |
1125 | 0 | { |
1126 | 0 | delete poDS; |
1127 | 0 | return nullptr; |
1128 | 0 | } |
1129 | | |
1130 | | /* -------------------------------------------------------------------- */ |
1131 | | /* Read the BMPInfoHeader. */ |
1132 | | /* -------------------------------------------------------------------- */ |
1133 | 18 | poDS->fp = poOpenInfo->fpL; |
1134 | 18 | poOpenInfo->fpL = nullptr; |
1135 | | |
1136 | 18 | VSIFSeekL(poDS->fp, BFH_SIZE, SEEK_SET); |
1137 | 18 | VSIFReadL(&poDS->sInfoHeader.iSize, 1, 4, poDS->fp); |
1138 | | #ifdef CPL_MSB |
1139 | | CPL_SWAP32PTR(&poDS->sInfoHeader.iSize); |
1140 | | #endif |
1141 | | |
1142 | 18 | BMPType eBMPType; |
1143 | 18 | if (poDS->sInfoHeader.iSize == BIH_WIN4SIZE) |
1144 | 17 | eBMPType = BMPT_WIN4; |
1145 | 1 | else if (poDS->sInfoHeader.iSize == BIH_OS21SIZE) |
1146 | 1 | eBMPType = BMPT_OS21; |
1147 | 0 | else if (poDS->sInfoHeader.iSize == BIH_OS22SIZE || |
1148 | 0 | poDS->sInfoHeader.iSize == 16) |
1149 | 0 | eBMPType = BMPT_OS22; |
1150 | 0 | else |
1151 | 0 | eBMPType = BMPT_WIN5; |
1152 | | |
1153 | 18 | if (eBMPType == BMPT_WIN4 || eBMPType == BMPT_WIN5 || eBMPType == BMPT_OS22) |
1154 | 17 | { |
1155 | 17 | VSIFReadL(&poDS->sInfoHeader.iWidth, 1, 4, poDS->fp); |
1156 | 17 | VSIFReadL(&poDS->sInfoHeader.iHeight, 1, 4, poDS->fp); |
1157 | 17 | VSIFReadL(&poDS->sInfoHeader.iPlanes, 1, 2, poDS->fp); |
1158 | 17 | VSIFReadL(&poDS->sInfoHeader.iBitCount, 1, 2, poDS->fp); |
1159 | 17 | unsigned int iCompression; |
1160 | 17 | VSIFReadL(&iCompression, 1, 4, poDS->fp); |
1161 | | #ifdef CPL_MSB |
1162 | | CPL_SWAP32PTR(&iCompression); |
1163 | | #endif |
1164 | 17 | if (iCompression > BMPC_PNG) |
1165 | 0 | { |
1166 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Unsupported compression"); |
1167 | 0 | delete poDS; |
1168 | 0 | return nullptr; |
1169 | 0 | } |
1170 | 17 | poDS->sInfoHeader.iCompression = |
1171 | 17 | static_cast<BMPComprMethod>(iCompression); |
1172 | 17 | VSIFReadL(&poDS->sInfoHeader.iSizeImage, 1, 4, poDS->fp); |
1173 | 17 | VSIFReadL(&poDS->sInfoHeader.iXPelsPerMeter, 1, 4, poDS->fp); |
1174 | 17 | VSIFReadL(&poDS->sInfoHeader.iYPelsPerMeter, 1, 4, poDS->fp); |
1175 | 17 | VSIFReadL(&poDS->sInfoHeader.iClrUsed, 1, 4, poDS->fp); |
1176 | 17 | VSIFReadL(&poDS->sInfoHeader.iClrImportant, 1, 4, poDS->fp); |
1177 | | |
1178 | | // rcg, read win4/5 fields. If we're reading a |
1179 | | // legacy header that ends at iClrImportant, it turns |
1180 | | // out that the three DWORD color table entries used |
1181 | | // by the channel masks start here anyway. |
1182 | 17 | if (poDS->sInfoHeader.iCompression == BMPC_BITFIELDS) |
1183 | 0 | { |
1184 | 0 | VSIFReadL(&poDS->sInfoHeader.iRedMask, 1, 4, poDS->fp); |
1185 | 0 | VSIFReadL(&poDS->sInfoHeader.iGreenMask, 1, 4, poDS->fp); |
1186 | 0 | VSIFReadL(&poDS->sInfoHeader.iBlueMask, 1, 4, poDS->fp); |
1187 | 0 | } |
1188 | | #ifdef CPL_MSB |
1189 | | CPL_SWAP32PTR(&poDS->sInfoHeader.iWidth); |
1190 | | CPL_SWAP32PTR(&poDS->sInfoHeader.iHeight); |
1191 | | CPL_SWAP16PTR(&poDS->sInfoHeader.iPlanes); |
1192 | | CPL_SWAP16PTR(&poDS->sInfoHeader.iBitCount); |
1193 | | CPL_SWAP32PTR(&poDS->sInfoHeader.iSizeImage); |
1194 | | CPL_SWAP32PTR(&poDS->sInfoHeader.iXPelsPerMeter); |
1195 | | CPL_SWAP32PTR(&poDS->sInfoHeader.iYPelsPerMeter); |
1196 | | CPL_SWAP32PTR(&poDS->sInfoHeader.iClrUsed); |
1197 | | CPL_SWAP32PTR(&poDS->sInfoHeader.iClrImportant); |
1198 | | // rcg, swap win4/5 fields. |
1199 | | CPL_SWAP32PTR(&poDS->sInfoHeader.iRedMask); |
1200 | | CPL_SWAP32PTR(&poDS->sInfoHeader.iGreenMask); |
1201 | | CPL_SWAP32PTR(&poDS->sInfoHeader.iBlueMask); |
1202 | | #endif |
1203 | 17 | poDS->nColorElems = 4; |
1204 | 17 | } |
1205 | | |
1206 | 18 | if (eBMPType == BMPT_OS22) |
1207 | 0 | { |
1208 | 0 | poDS->nColorElems = |
1209 | 0 | 3; // FIXME: different info in different documents regarding this! |
1210 | 0 | } |
1211 | | |
1212 | 18 | if (eBMPType == BMPT_OS21) |
1213 | 1 | { |
1214 | 1 | GInt16 iShort; |
1215 | | |
1216 | 1 | VSIFReadL(&iShort, 1, 2, poDS->fp); |
1217 | 1 | poDS->sInfoHeader.iWidth = CPL_LSBWORD16(iShort); |
1218 | 1 | VSIFReadL(&iShort, 1, 2, poDS->fp); |
1219 | 1 | poDS->sInfoHeader.iHeight = CPL_LSBWORD16(iShort); |
1220 | 1 | VSIFReadL(&iShort, 1, 2, poDS->fp); |
1221 | 1 | poDS->sInfoHeader.iPlanes = CPL_LSBWORD16(iShort); |
1222 | 1 | VSIFReadL(&iShort, 1, 2, poDS->fp); |
1223 | 1 | poDS->sInfoHeader.iBitCount = CPL_LSBWORD16(iShort); |
1224 | 1 | poDS->sInfoHeader.iCompression = BMPC_RGB; |
1225 | 1 | poDS->nColorElems = 3; |
1226 | 1 | } |
1227 | | |
1228 | 18 | if (poDS->sInfoHeader.iBitCount != 1 && poDS->sInfoHeader.iBitCount != 4 && |
1229 | 18 | poDS->sInfoHeader.iBitCount != 8 && poDS->sInfoHeader.iBitCount != 16 && |
1230 | 18 | poDS->sInfoHeader.iBitCount != 24 && poDS->sInfoHeader.iBitCount != 32) |
1231 | 0 | { |
1232 | 0 | delete poDS; |
1233 | 0 | return nullptr; |
1234 | 0 | } |
1235 | | |
1236 | | #ifdef BMP_DEBUG |
1237 | | CPLDebug("BMP", |
1238 | | "Windows Device Independent Bitmap parameters:\n" |
1239 | | " info header size: %d bytes\n" |
1240 | | " width: %d\n height: %d\n planes: %d\n bpp: %d\n" |
1241 | | " compression: %d\n image size: %d bytes\n X resolution: %d\n" |
1242 | | " Y resolution: %d\n colours used: %d\n colours important: %d", |
1243 | | poDS->sInfoHeader.iSize, poDS->sInfoHeader.iWidth, |
1244 | | poDS->sInfoHeader.iHeight, poDS->sInfoHeader.iPlanes, |
1245 | | poDS->sInfoHeader.iBitCount, poDS->sInfoHeader.iCompression, |
1246 | | poDS->sInfoHeader.iSizeImage, poDS->sInfoHeader.iXPelsPerMeter, |
1247 | | poDS->sInfoHeader.iYPelsPerMeter, poDS->sInfoHeader.iClrUsed, |
1248 | | poDS->sInfoHeader.iClrImportant); |
1249 | | #endif |
1250 | | |
1251 | 18 | if (poDS->sInfoHeader.iHeight == INT_MIN) |
1252 | 0 | { |
1253 | 0 | delete poDS; |
1254 | 0 | return nullptr; |
1255 | 0 | } |
1256 | | |
1257 | 18 | poDS->nRasterXSize = poDS->sInfoHeader.iWidth; |
1258 | 18 | poDS->nRasterYSize = (poDS->sInfoHeader.iHeight > 0) |
1259 | 18 | ? poDS->sInfoHeader.iHeight |
1260 | 18 | : -poDS->sInfoHeader.iHeight; |
1261 | | |
1262 | 18 | if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0) |
1263 | 2 | { |
1264 | 2 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions : %d x %d", |
1265 | 2 | poDS->nRasterXSize, poDS->nRasterYSize); |
1266 | 2 | delete poDS; |
1267 | 2 | return nullptr; |
1268 | 2 | } |
1269 | | |
1270 | 16 | switch (poDS->sInfoHeader.iBitCount) |
1271 | 16 | { |
1272 | 1 | case 1: |
1273 | 1 | case 4: |
1274 | 15 | case 8: |
1275 | 15 | { |
1276 | 15 | poDS->nBands = 1; |
1277 | 15 | int nColorTableSize; |
1278 | 15 | int nMaxColorTableSize = 1 << poDS->sInfoHeader.iBitCount; |
1279 | | // Allocate memory for colour table and read it |
1280 | 15 | if (poDS->sInfoHeader.iClrUsed) |
1281 | 14 | { |
1282 | 14 | if (poDS->sInfoHeader.iClrUsed > (GUInt32)nMaxColorTableSize) |
1283 | 0 | { |
1284 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1285 | 0 | "Wrong value for iClrUsed: %u", |
1286 | 0 | poDS->sInfoHeader.iClrUsed); |
1287 | 0 | delete poDS; |
1288 | 0 | return nullptr; |
1289 | 0 | } |
1290 | 14 | nColorTableSize = poDS->sInfoHeader.iClrUsed; |
1291 | 14 | } |
1292 | 1 | else |
1293 | 1 | nColorTableSize = nMaxColorTableSize; |
1294 | | |
1295 | 15 | poDS->pabyColorTable = (GByte *)VSI_MALLOC2_VERBOSE( |
1296 | 15 | poDS->nColorElems, nColorTableSize); |
1297 | 15 | if (poDS->pabyColorTable == nullptr) |
1298 | 0 | { |
1299 | 0 | break; |
1300 | 0 | } |
1301 | | |
1302 | 15 | if (VSIFSeekL(poDS->fp, |
1303 | 15 | BFH_SIZE + static_cast<vsi_l_offset>( |
1304 | 15 | poDS->sInfoHeader.iSize), |
1305 | 15 | SEEK_SET) != 0 || |
1306 | 15 | VSIFReadL(poDS->pabyColorTable, poDS->nColorElems, |
1307 | 15 | nColorTableSize, poDS->fp) != (size_t)nColorTableSize) |
1308 | 0 | { |
1309 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Cannot read color table"); |
1310 | 0 | delete poDS; |
1311 | 0 | return nullptr; |
1312 | 0 | } |
1313 | | |
1314 | 15 | GDALColorEntry oEntry; |
1315 | 15 | poDS->poColorTable = new GDALColorTable(); |
1316 | 2.13k | for (int i = 0; i < nColorTableSize; i++) |
1317 | 2.11k | { |
1318 | 2.11k | oEntry.c1 = |
1319 | 2.11k | poDS->pabyColorTable[i * poDS->nColorElems + 2]; // Red |
1320 | 2.11k | oEntry.c2 = |
1321 | 2.11k | poDS->pabyColorTable[i * poDS->nColorElems + 1]; // Green |
1322 | 2.11k | oEntry.c3 = |
1323 | 2.11k | poDS->pabyColorTable[i * poDS->nColorElems]; // Blue |
1324 | 2.11k | oEntry.c4 = 255; |
1325 | | |
1326 | 2.11k | poDS->poColorTable->SetColorEntry(i, &oEntry); |
1327 | 2.11k | } |
1328 | 15 | } |
1329 | 0 | break; |
1330 | 0 | case 16: |
1331 | 1 | case 24: |
1332 | 1 | case 32: |
1333 | 1 | poDS->nBands = 3; |
1334 | 1 | break; |
1335 | 0 | default: |
1336 | 0 | delete poDS; |
1337 | 0 | return nullptr; |
1338 | 16 | } |
1339 | | |
1340 | | /* -------------------------------------------------------------------- */ |
1341 | | /* Create band information objects. */ |
1342 | | /* -------------------------------------------------------------------- */ |
1343 | 16 | if (poDS->sInfoHeader.iCompression == BMPC_RGB || |
1344 | 16 | poDS->sInfoHeader.iCompression == BMPC_BITFIELDS) |
1345 | 1 | { |
1346 | 4 | for (int iBand = 1; iBand <= poDS->nBands; iBand++) |
1347 | 3 | { |
1348 | 3 | BMPRasterBand *band = new BMPRasterBand(poDS, iBand); |
1349 | 3 | poDS->SetBand(iBand, band); |
1350 | 3 | if (band->pabyScan == nullptr) |
1351 | 0 | { |
1352 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1353 | 0 | "The BMP file is probably corrupted or too large. " |
1354 | 0 | "Image width = %d", |
1355 | 0 | poDS->nRasterXSize); |
1356 | 0 | delete poDS; |
1357 | 0 | return nullptr; |
1358 | 0 | } |
1359 | 3 | } |
1360 | 1 | } |
1361 | 15 | else if (poDS->sInfoHeader.iCompression == BMPC_RLE8 || |
1362 | 15 | poDS->sInfoHeader.iCompression == BMPC_RLE4) |
1363 | 14 | { |
1364 | 14 | for (int iBand = 1; iBand <= poDS->nBands; iBand++) |
1365 | 14 | { |
1366 | 14 | BMPComprRasterBand *band = new BMPComprRasterBand(poDS, iBand); |
1367 | 14 | poDS->SetBand(iBand, band); |
1368 | 14 | if (band->pabyUncomprBuf == nullptr) |
1369 | 14 | { |
1370 | 14 | CPLError(CE_Failure, CPLE_AppDefined, |
1371 | 14 | "The BMP file is probably corrupted or too large. " |
1372 | 14 | "Image width = %d", |
1373 | 14 | poDS->nRasterXSize); |
1374 | 14 | delete poDS; |
1375 | 14 | return nullptr; |
1376 | 14 | } |
1377 | 14 | } |
1378 | 14 | } |
1379 | 1 | else |
1380 | 1 | { |
1381 | 1 | delete poDS; |
1382 | 1 | return nullptr; |
1383 | 1 | } |
1384 | | |
1385 | | /* -------------------------------------------------------------------- */ |
1386 | | /* Check for world file. */ |
1387 | | /* -------------------------------------------------------------------- */ |
1388 | 1 | poDS->bGeoTransformValid = GDALReadWorldFile( |
1389 | 1 | poOpenInfo->pszFilename, nullptr, poDS->adfGeoTransform); |
1390 | | |
1391 | 1 | if (!poDS->bGeoTransformValid) |
1392 | 1 | poDS->bGeoTransformValid = GDALReadWorldFile( |
1393 | 1 | poOpenInfo->pszFilename, ".wld", poDS->adfGeoTransform); |
1394 | | |
1395 | | /* -------------------------------------------------------------------- */ |
1396 | | /* Initialize any PAM information. */ |
1397 | | /* -------------------------------------------------------------------- */ |
1398 | 1 | poDS->SetDescription(poOpenInfo->pszFilename); |
1399 | 1 | poDS->TryLoadXML(); |
1400 | | |
1401 | | /* -------------------------------------------------------------------- */ |
1402 | | /* Check for overviews. */ |
1403 | | /* -------------------------------------------------------------------- */ |
1404 | 1 | poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename); |
1405 | | |
1406 | 1 | return poDS; |
1407 | 16 | } |
1408 | | |
1409 | | /************************************************************************/ |
1410 | | /* Create() */ |
1411 | | /************************************************************************/ |
1412 | | |
1413 | | GDALDataset *BMPDataset::Create(const char *pszFilename, int nXSize, int nYSize, |
1414 | | int nBandsIn, GDALDataType eType, |
1415 | | char **papszOptions) |
1416 | | |
1417 | 0 | { |
1418 | 0 | if (eType != GDT_Byte) |
1419 | 0 | { |
1420 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1421 | 0 | "Attempt to create BMP dataset with an illegal\n" |
1422 | 0 | "data type (%s), only Byte supported by the format.\n", |
1423 | 0 | GDALGetDataTypeName(eType)); |
1424 | |
|
1425 | 0 | return nullptr; |
1426 | 0 | } |
1427 | | |
1428 | 0 | if (nBandsIn != 1 && nBandsIn != 3) |
1429 | 0 | { |
1430 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1431 | 0 | "BMP driver doesn't support %d bands. Must be 1 or 3.\n", |
1432 | 0 | nBandsIn); |
1433 | |
|
1434 | 0 | return nullptr; |
1435 | 0 | } |
1436 | | |
1437 | | /* -------------------------------------------------------------------- */ |
1438 | | /* Create the dataset. */ |
1439 | | /* -------------------------------------------------------------------- */ |
1440 | 0 | BMPDataset *poDS = new BMPDataset(); |
1441 | 0 | poDS->m_bNewFile = true; |
1442 | |
|
1443 | 0 | poDS->fp = VSIFOpenL(pszFilename, "wb+"); |
1444 | 0 | if (poDS->fp == nullptr) |
1445 | 0 | { |
1446 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create file %s.\n", |
1447 | 0 | pszFilename); |
1448 | 0 | delete poDS; |
1449 | 0 | return nullptr; |
1450 | 0 | } |
1451 | | |
1452 | 0 | poDS->pszFilename = CPLStrdup(pszFilename); |
1453 | | |
1454 | | /* -------------------------------------------------------------------- */ |
1455 | | /* Fill the BMPInfoHeader */ |
1456 | | /* -------------------------------------------------------------------- */ |
1457 | 0 | poDS->sInfoHeader.iSize = 40; |
1458 | 0 | poDS->sInfoHeader.iWidth = nXSize; |
1459 | 0 | poDS->sInfoHeader.iHeight = nYSize; |
1460 | 0 | poDS->sInfoHeader.iPlanes = 1; |
1461 | 0 | poDS->sInfoHeader.iBitCount = (nBandsIn == 3) ? 24 : 8; |
1462 | 0 | poDS->sInfoHeader.iCompression = BMPC_RGB; |
1463 | | |
1464 | | /* XXX: Avoid integer overflow. We can calculate size in one |
1465 | | * step using |
1466 | | * |
1467 | | * nScanSize = ((poDS->sInfoHeader.iWidth * |
1468 | | * poDS->sInfoHeader.iBitCount + 31) & ~31) / 8 |
1469 | | * |
1470 | | * formula, but we should check for overflow conditions |
1471 | | * during calculation. |
1472 | | */ |
1473 | 0 | GUInt32 nScanSize = |
1474 | 0 | (GUInt32)poDS->sInfoHeader.iWidth * poDS->sInfoHeader.iBitCount + 31; |
1475 | 0 | if (!poDS->sInfoHeader.iWidth || !poDS->sInfoHeader.iBitCount || |
1476 | 0 | (nScanSize - 31) / poDS->sInfoHeader.iBitCount != |
1477 | 0 | (GUInt32)poDS->sInfoHeader.iWidth) |
1478 | 0 | { |
1479 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
1480 | 0 | "Wrong image parameters; " |
1481 | 0 | "can't allocate space for scanline buffer"); |
1482 | 0 | delete poDS; |
1483 | |
|
1484 | 0 | return nullptr; |
1485 | 0 | } |
1486 | 0 | nScanSize = (nScanSize & ~31U) / 8; |
1487 | |
|
1488 | 0 | poDS->sInfoHeader.iXPelsPerMeter = 0; |
1489 | 0 | poDS->sInfoHeader.iYPelsPerMeter = 0; |
1490 | 0 | poDS->nColorElems = 4; |
1491 | | |
1492 | | /* -------------------------------------------------------------------- */ |
1493 | | /* Do we need colour table? */ |
1494 | | /* -------------------------------------------------------------------- */ |
1495 | 0 | if (nBandsIn == 1) |
1496 | 0 | { |
1497 | 0 | poDS->sInfoHeader.iClrUsed = 1 << poDS->sInfoHeader.iBitCount; |
1498 | 0 | poDS->pabyColorTable = |
1499 | 0 | (GByte *)CPLMalloc(static_cast<size_t>(poDS->nColorElems) * |
1500 | 0 | poDS->sInfoHeader.iClrUsed); |
1501 | 0 | for (unsigned int i = 0; i < poDS->sInfoHeader.iClrUsed; i++) |
1502 | 0 | { |
1503 | 0 | poDS->pabyColorTable[i * poDS->nColorElems] = |
1504 | 0 | poDS->pabyColorTable[i * poDS->nColorElems + 1] = |
1505 | 0 | poDS->pabyColorTable[i * poDS->nColorElems + 2] = |
1506 | 0 | poDS->pabyColorTable[i * poDS->nColorElems + 3] = |
1507 | 0 | (GByte)i; |
1508 | 0 | } |
1509 | 0 | } |
1510 | 0 | else |
1511 | 0 | { |
1512 | 0 | poDS->sInfoHeader.iClrUsed = 0; |
1513 | 0 | } |
1514 | 0 | poDS->sInfoHeader.iClrImportant = 0; |
1515 | | |
1516 | | /* -------------------------------------------------------------------- */ |
1517 | | /* Fill the BMPFileHeader */ |
1518 | | /* -------------------------------------------------------------------- */ |
1519 | |
|
1520 | 0 | poDS->sFileHeader.iOffBits = BFH_SIZE + poDS->sInfoHeader.iSize + |
1521 | 0 | poDS->sInfoHeader.iClrUsed * poDS->nColorElems; |
1522 | | |
1523 | | // From https://medium.com/@chiaracoetzee/maximum-resolution-of-bmp-image-file-8c729b3f833a |
1524 | 0 | if (nXSize > 30000 || nYSize > 30000) |
1525 | 0 | { |
1526 | 0 | CPLDebug("BMP", "Dimensions of BMP file exceed maximum supported by " |
1527 | 0 | "Adobe Photoshop CC 2014.2.2"); |
1528 | 0 | } |
1529 | 0 | if (nXSize > 2147483647 / (nYSize + 1)) |
1530 | 0 | { |
1531 | 0 | CPLDebug("BMP", "Dimensions of BMP file exceed maximum supported by " |
1532 | 0 | "Windows Photo Viewer"); |
1533 | 0 | } |
1534 | |
|
1535 | 0 | const vsi_l_offset nLargeImageSize = |
1536 | 0 | static_cast<vsi_l_offset>(nScanSize) * poDS->sInfoHeader.iHeight; |
1537 | 0 | poDS->m_nFileSize = poDS->sFileHeader.iOffBits + nLargeImageSize; |
1538 | 0 | if (nLargeImageSize > std::numeric_limits<uint32_t>::max()) |
1539 | 0 | { |
1540 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1541 | 0 | "Image too big for its size to fit in a 32 bit integer! " |
1542 | 0 | "Writing 0xFFFFFFFF in it, but that could cause compatibility " |
1543 | 0 | "problems with other readers."); |
1544 | 0 | poDS->sFileHeader.iSize = std::numeric_limits<uint32_t>::max(); |
1545 | 0 | poDS->sInfoHeader.iSizeImage = std::numeric_limits<uint32_t>::max(); |
1546 | 0 | } |
1547 | 0 | else if (poDS->m_nFileSize > std::numeric_limits<uint32_t>::max()) |
1548 | 0 | { |
1549 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1550 | 0 | "File too big for its size to fit in a 32 bit integer! " |
1551 | 0 | "Writing 0xFFFFFFFF in it, but that could cause compatibility " |
1552 | 0 | "problems with other readers."); |
1553 | 0 | poDS->sFileHeader.iSize = std::numeric_limits<uint32_t>::max(); |
1554 | 0 | poDS->sInfoHeader.iSizeImage = static_cast<uint32_t>(nLargeImageSize); |
1555 | 0 | } |
1556 | 0 | else |
1557 | 0 | { |
1558 | 0 | poDS->sFileHeader.iSize = static_cast<uint32_t>(poDS->m_nFileSize); |
1559 | 0 | poDS->sInfoHeader.iSizeImage = static_cast<uint32_t>(nLargeImageSize); |
1560 | 0 | } |
1561 | |
|
1562 | 0 | poDS->sFileHeader.bType[0] = 'B'; |
1563 | 0 | poDS->sFileHeader.bType[1] = 'M'; |
1564 | 0 | poDS->sFileHeader.iReserved1 = 0; |
1565 | 0 | poDS->sFileHeader.iReserved2 = 0; |
1566 | | |
1567 | | /* -------------------------------------------------------------------- */ |
1568 | | /* Write all structures to the file */ |
1569 | | /* -------------------------------------------------------------------- */ |
1570 | 0 | if (VSIFWriteL(&poDS->sFileHeader.bType, 1, 2, poDS->fp) != 2) |
1571 | 0 | { |
1572 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
1573 | 0 | "Write of first 2 bytes to BMP file %s failed.\n" |
1574 | 0 | "Is file system full?", |
1575 | 0 | pszFilename); |
1576 | 0 | delete poDS; |
1577 | |
|
1578 | 0 | return nullptr; |
1579 | 0 | } |
1580 | | |
1581 | 0 | GInt32 iLong; |
1582 | 0 | GUInt32 iULong; |
1583 | 0 | GUInt16 iUShort; |
1584 | |
|
1585 | 0 | iULong = CPL_LSBWORD32(poDS->sFileHeader.iSize); |
1586 | 0 | VSIFWriteL(&iULong, 4, 1, poDS->fp); |
1587 | 0 | iUShort = CPL_LSBWORD16(poDS->sFileHeader.iReserved1); |
1588 | 0 | VSIFWriteL(&iUShort, 2, 1, poDS->fp); |
1589 | 0 | iUShort = CPL_LSBWORD16(poDS->sFileHeader.iReserved2); |
1590 | 0 | VSIFWriteL(&iUShort, 2, 1, poDS->fp); |
1591 | 0 | iULong = CPL_LSBWORD32(poDS->sFileHeader.iOffBits); |
1592 | 0 | VSIFWriteL(&iULong, 4, 1, poDS->fp); |
1593 | |
|
1594 | 0 | iULong = CPL_LSBWORD32(poDS->sInfoHeader.iSize); |
1595 | 0 | VSIFWriteL(&iULong, 4, 1, poDS->fp); |
1596 | 0 | iLong = CPL_LSBWORD32(poDS->sInfoHeader.iWidth); |
1597 | 0 | VSIFWriteL(&iLong, 4, 1, poDS->fp); |
1598 | 0 | iLong = CPL_LSBWORD32(poDS->sInfoHeader.iHeight); |
1599 | 0 | VSIFWriteL(&iLong, 4, 1, poDS->fp); |
1600 | 0 | iUShort = CPL_LSBWORD16(poDS->sInfoHeader.iPlanes); |
1601 | 0 | VSIFWriteL(&iUShort, 2, 1, poDS->fp); |
1602 | 0 | iUShort = CPL_LSBWORD16(poDS->sInfoHeader.iBitCount); |
1603 | 0 | VSIFWriteL(&iUShort, 2, 1, poDS->fp); |
1604 | 0 | iULong = CPL_LSBWORD32(poDS->sInfoHeader.iCompression); |
1605 | 0 | VSIFWriteL(&iULong, 4, 1, poDS->fp); |
1606 | 0 | iULong = CPL_LSBWORD32(poDS->sInfoHeader.iSizeImage); |
1607 | 0 | VSIFWriteL(&iULong, 4, 1, poDS->fp); |
1608 | 0 | iLong = CPL_LSBWORD32(poDS->sInfoHeader.iXPelsPerMeter); |
1609 | 0 | VSIFWriteL(&iLong, 4, 1, poDS->fp); |
1610 | 0 | iLong = CPL_LSBWORD32(poDS->sInfoHeader.iYPelsPerMeter); |
1611 | 0 | VSIFWriteL(&iLong, 4, 1, poDS->fp); |
1612 | 0 | iULong = CPL_LSBWORD32(poDS->sInfoHeader.iClrUsed); |
1613 | 0 | VSIFWriteL(&iULong, 4, 1, poDS->fp); |
1614 | 0 | iULong = CPL_LSBWORD32(poDS->sInfoHeader.iClrImportant); |
1615 | 0 | VSIFWriteL(&iULong, 4, 1, poDS->fp); |
1616 | |
|
1617 | 0 | if (poDS->sInfoHeader.iClrUsed) |
1618 | 0 | { |
1619 | 0 | if (VSIFWriteL(poDS->pabyColorTable, 1, |
1620 | 0 | static_cast<size_t>(poDS->nColorElems) * |
1621 | 0 | poDS->sInfoHeader.iClrUsed, |
1622 | 0 | poDS->fp) != |
1623 | 0 | static_cast<size_t>(poDS->nColorElems) * poDS->sInfoHeader.iClrUsed) |
1624 | 0 | { |
1625 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
1626 | 0 | "Error writing color table. Is disk full?"); |
1627 | 0 | delete poDS; |
1628 | |
|
1629 | 0 | return nullptr; |
1630 | 0 | } |
1631 | 0 | } |
1632 | | |
1633 | 0 | poDS->nRasterXSize = nXSize; |
1634 | 0 | poDS->nRasterYSize = nYSize; |
1635 | 0 | poDS->eAccess = GA_Update; |
1636 | 0 | poDS->nBands = nBandsIn; |
1637 | | |
1638 | | /* -------------------------------------------------------------------- */ |
1639 | | /* Create band information objects. */ |
1640 | | /* -------------------------------------------------------------------- */ |
1641 | 0 | for (int iBand = 1; iBand <= poDS->nBands; iBand++) |
1642 | 0 | { |
1643 | 0 | poDS->SetBand(iBand, new BMPRasterBand(poDS, iBand)); |
1644 | 0 | } |
1645 | | |
1646 | | /* -------------------------------------------------------------------- */ |
1647 | | /* Do we need a world file? */ |
1648 | | /* -------------------------------------------------------------------- */ |
1649 | 0 | if (CPLFetchBool(papszOptions, "WORLDFILE", false)) |
1650 | 0 | poDS->bGeoTransformValid = TRUE; |
1651 | |
|
1652 | 0 | return (GDALDataset *)poDS; |
1653 | 0 | } |
1654 | | |
1655 | | /************************************************************************/ |
1656 | | /* GDALRegister_BMP() */ |
1657 | | /************************************************************************/ |
1658 | | |
1659 | | void GDALRegister_BMP() |
1660 | | |
1661 | 2 | { |
1662 | 2 | if (GDALGetDriverByName("BMP") != nullptr) |
1663 | 0 | return; |
1664 | | |
1665 | 2 | GDALDriver *poDriver = new GDALDriver(); |
1666 | | |
1667 | 2 | poDriver->SetDescription("BMP"); |
1668 | 2 | poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); |
1669 | 2 | poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, |
1670 | 2 | "MS Windows Device Independent Bitmap"); |
1671 | 2 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/bmp.html"); |
1672 | 2 | poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "bmp"); |
1673 | 2 | poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte"); |
1674 | 2 | poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, |
1675 | 2 | "<CreationOptionList>" |
1676 | 2 | " <Option name='WORLDFILE' type='boolean' " |
1677 | 2 | "description='Write out world file'/>" |
1678 | 2 | "</CreationOptionList>"); |
1679 | | |
1680 | 2 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
1681 | | |
1682 | 2 | poDriver->pfnOpen = BMPDataset::Open; |
1683 | 2 | poDriver->pfnCreate = BMPDataset::Create; |
1684 | 2 | poDriver->pfnIdentify = BMPDataset::Identify; |
1685 | | |
1686 | 2 | GetGDALDriverManager()->RegisterDriver(poDriver); |
1687 | 2 | } |