1// Aseprite
2// Copyright (C) 2019-2022 Igara Studio S.A.
3// Copyright (C) 2001-2018 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7//
8// bmp.c - Based on the code of Seymour Shlien and Jonas Petersen.
9//
10// Info about BMP format:
11// https://en.wikipedia.org/wiki/BMP_file_format
12// http://justsolve.archiveteam.org/wiki/BMP
13// https://docs.microsoft.com/en-us/windows/win32/gdi/bitmap-header-types
14
15#ifdef HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#include "app/file/file.h"
20#include "app/file/file_format.h"
21#include "app/file/format_options.h"
22#include "base/cfile.h"
23#include "base/file_handle.h"
24#include "doc/doc.h"
25#include "fmt/format.h"
26
27namespace app {
28
29// Max supported .bmp size (to filter out invalid image sizes)
30const uint32_t kMaxBmpSize = 1024*1024*128; // 128 MB
31
32using namespace base;
33
34class BmpFormat : public FileFormat {
35 enum {
36 BMP_OPTIONS_FORMAT_WINDOWS = 12,
37 BMP_OPTIONS_FORMAT_OS2 = 40,
38 BMP_OPTIONS_COMPRESSION_RGB = 0,
39 BMP_OPTIONS_COMPRESSION_RLE8 = 1,
40 BMP_OPTIONS_COMPRESSION_RLE4 = 2,
41 BMP_OPTIONS_COMPRESSION_BITFIELDS = 3
42 };
43
44 // Data for BMP files
45 class BmpOptions : public FormatOptions
46 {
47 public:
48 int format; // bmp format.
49 int compression; // bmp compression.
50 int bits_per_pixel; // Bits per pixel.
51 uint32_t red_mask; // Mask for red channel.
52 uint32_t green_mask; // Mask for green channel.
53 uint32_t blue_mask; // Mask for blue channel.
54 uint32_t alpha_mask; // Mask for alpha channel.
55 };
56
57 const char* onGetName() const override {
58 return "bmp";
59 }
60
61 void onGetExtensions(base::paths& exts) const override {
62 exts.push_back("bmp");
63 }
64
65 dio::FileFormat onGetDioFormat() const override {
66 return dio::FileFormat::BMP_IMAGE;
67 }
68
69 int onGetFlags() const override {
70 return
71 FILE_SUPPORT_LOAD |
72 FILE_SUPPORT_SAVE |
73 FILE_SUPPORT_RGB |
74 FILE_SUPPORT_RGBA |
75 FILE_SUPPORT_GRAY |
76 FILE_SUPPORT_INDEXED |
77 FILE_SUPPORT_SEQUENCES |
78 FILE_ENCODE_ABSTRACT_IMAGE;
79 }
80
81 bool onLoad(FileOp* fop) override;
82#ifdef ENABLE_SAVE
83 bool onSave(FileOp* fop) override;
84#endif
85};
86
87FileFormat* CreateBmpFormat()
88{
89 return new BmpFormat;
90}
91
92#define BI_RGB 0
93#define BI_RLE8 1
94#define BI_RLE4 2
95#define BI_BITFIELDS 3
96#define BI_ALPHABITFIELDS 6
97
98#define OS2FILEHEADERSIZE 14
99
100#define OS2INFOHEADERSIZE 12
101#define OS22INFOHEADERSIZE16 16
102#define OS22INFOHEADERSIZE24 24
103#define WININFOHEADERSIZE 40
104#define BV2INFOHEADERSIZE 52
105#define BV3INFOHEADERSIZE 56
106#define OS22INFOHEADERSIZE60 60
107#define OS22INFOHEADERSIZE64 64
108#define BV4INFOHEADERSIZE 108
109#define BV5INFOHEADERSIZE 124
110
111struct BITMAPFILEHEADER
112{
113 uint32_t bfType;
114 uint32_t bfSize;
115 uint16_t bfReserved1;
116 uint16_t bfReserved2;
117 uint32_t bfOffBits;
118};
119
120// Used for all Info Header Sizes.
121// Contains only the parameters needed to load the image.
122struct BITMAPINFOHEADER
123{
124 uint32_t biSize;
125 uint32_t biWidth;
126 uint32_t biHeight;
127 uint16_t biBitCount;
128 uint32_t biCompression;
129 uint32_t biClrUsed;
130 uint32_t rMask;
131 uint32_t gMask;
132 uint32_t bMask;
133 uint32_t aMask;
134
135 bool isRGBMasks() const
136 {
137 return biSize >= BV2INFOHEADERSIZE ||
138 biCompression == BI_BITFIELDS ||
139 biCompression == BI_ALPHABITFIELDS;
140 };
141 bool isAlphaMask() const
142 {
143 return biSize >= BV3INFOHEADERSIZE ||
144 biCompression == BI_ALPHABITFIELDS;
145 };
146};
147
148struct WINBMPINFOHEADER // Size: 16 to 64
149{
150 uint32_t biWidth;
151 uint32_t biHeight;
152 uint16_t biPlanes;
153 uint16_t biBitCount;
154 uint32_t biCompression;
155 uint32_t biSizeImage;
156 uint32_t biXPelsPerMeter;
157 uint32_t biYPelsPerMeter;
158 uint32_t biClrUsed;
159 uint32_t biClrImportant;
160
161 uint32_t redMask;
162 uint32_t greenMask;
163 uint32_t blueMask;
164 uint32_t alphaMask;
165};
166
167struct OS2BMPINFOHEADER // Size: 12.
168{
169 uint16_t biWidth;
170 uint16_t biHeight;
171 uint16_t biPlanes;
172 uint16_t biBitCount;
173};
174
175// TO DO: support ICC profiles and colorimetry
176struct CIEXYZ
177{
178 uint32_t ciexyzX; // Fix Point: 2 bits integer part, 30 bits to fractional part
179 uint32_t ciexyzY;
180 uint32_t ciexyzZ;
181};
182
183struct CIEXYZTRIPLE
184{
185 CIEXYZ ciexyzRed;
186 CIEXYZ ciexyzGreen;
187 CIEXYZ ciexyzBlue;
188};
189
190struct BMPV4HEADER // Size: 108.
191{
192 uint32_t bV4Width;
193 uint32_t bV4Height;
194 uint16_t bV4Planes;
195 uint16_t bV4BitCount;
196 uint32_t bV4Compression;
197 uint32_t bV4SizeImage;
198 uint32_t bV4XPelsPerMeter;
199 uint32_t bV4YPelsPerMeter;
200 uint32_t bV4ClrUsed;
201 uint32_t bV4ClrImportant;
202
203 uint32_t bV4RedMask;
204 uint32_t bV4GreenMask;
205 uint32_t bV4BlueMask;
206 uint32_t bV4AlphaMask;
207// TO DO: support ICC profiles and colorimetry
208 uint32_t bV4CSType;
209 CIEXYZTRIPLE bV4Endpoints;
210 uint32_t bV4GammaRed;
211 uint32_t bV4GammaGreen;
212 uint32_t bV4GammaBlue;
213};
214
215struct BMPV5HEADER // Size: 124.
216{
217 uint32_t bV5Width;
218 uint32_t bV5Height;
219 uint16_t bV5Planes;
220 uint16_t bV5BitCount;
221 uint32_t bV5Compression;
222
223 uint32_t bV5SizeImage;
224 uint32_t bV5XPelsPerMeter;
225 uint32_t bV5YPelsPerMeter;
226 uint32_t bV5ClrUsed;
227 uint32_t bV5ClrImportant;
228
229 uint32_t bV5RedMask;
230 uint32_t bV5GreenMask;
231 uint32_t bV5BlueMask;
232 uint32_t bV5AlphaMask;
233// TO DO: support ICC profiles and colorimetry
234 uint32_t bV5CSType;
235 CIEXYZTRIPLE bV5Endpoints;
236
237 uint32_t bV5GammaRed;
238 uint32_t bV5GammaGreen;
239 uint32_t bV5GammaBlue;
240
241 uint32_t bV5Intent;
242 uint32_t bV5ProfileData;
243 uint32_t bV5ProfileSize;
244 uint32_t bV5Reserved;
245};
246
247/* read_bmfileheader:
248 * Reads a BMP file header and check that it has the BMP magic number.
249 */
250static int read_bmfileheader(FILE *f, BITMAPFILEHEADER *fileheader)
251{
252 fileheader->bfType = fgetw(f);
253 fileheader->bfSize = fgetl(f);
254 fileheader->bfReserved1 = fgetw(f);
255 fileheader->bfReserved2 = fgetw(f);
256 fileheader->bfOffBits = fgetl(f);
257
258 if (fileheader->bfType != 19778)
259 return -1;
260
261 return 0;
262}
263
264/* read_win_bminfoheader:
265 * Reads information from a BMP file header.
266 */
267static int read_win_bminfoheader(FILE *f, BITMAPINFOHEADER *infoheader)
268{
269 WINBMPINFOHEADER win_infoheader;
270
271 int biSize = infoheader->biSize;
272
273 if (biSize != OS22INFOHEADERSIZE16 &&
274 biSize != OS22INFOHEADERSIZE24 &&
275 biSize != WININFOHEADERSIZE &&
276 biSize != BV2INFOHEADERSIZE &&
277 biSize != BV3INFOHEADERSIZE &&
278 biSize != OS22INFOHEADERSIZE60 &&
279 biSize != OS22INFOHEADERSIZE64)
280 return -1;
281
282 win_infoheader.biWidth = fgetl(f);
283 win_infoheader.biHeight = fgetl(f);
284 win_infoheader.biPlanes = fgetw(f);
285 win_infoheader.biBitCount = fgetw(f); // = 16 bytes
286
287 win_infoheader.redMask = 0;
288 win_infoheader.greenMask = 0;
289 win_infoheader.blueMask = 0;
290 win_infoheader.alphaMask = 0;
291 if (biSize == OS22INFOHEADERSIZE16)
292 win_infoheader.biCompression = BI_RGB; // = 16 bytes
293 else {
294 ASSERT(biSize >= OS22INFOHEADERSIZE24);
295 win_infoheader.biCompression = fgetl(f);
296 win_infoheader.biSizeImage = fgetl(f); // = 24 bytes
297
298 if (biSize >= WININFOHEADERSIZE) {
299 win_infoheader.biXPelsPerMeter = fgetl(f);
300 win_infoheader.biYPelsPerMeter = fgetl(f);
301 win_infoheader.biClrUsed = fgetl(f);
302 win_infoheader.biClrImportant = fgetl(f); // = 40 bytes (WININFOHEADERSIZE)
303
304 // 'biCompression' is needed to execute
305 // infoheader->isRGBMasks() and infoheader->isAlphaMask()
306 infoheader->biCompression = win_infoheader.biCompression;
307
308 if (infoheader->isRGBMasks()) {
309 win_infoheader.redMask = fgetl(f);
310 win_infoheader.greenMask = fgetl(f);
311 win_infoheader.blueMask = fgetl(f); // = 52 bytes (BV2INFOHEADERSIZE)
312 if (infoheader->isAlphaMask()) {
313 win_infoheader.alphaMask = fgetl(f); // = 56 bytes (BV3INFOHEADERSIZE)
314 if (biSize >= OS22INFOHEADERSIZE60) {
315 fgetl(f); // <--discarded // = 60 bytes
316 if (biSize == OS22INFOHEADERSIZE64)
317 fgetl(f); // <--discarded // = 64 bytes
318 }
319 }
320 }
321 }
322 }
323
324 infoheader->biWidth = win_infoheader.biWidth;
325 infoheader->biHeight = win_infoheader.biHeight;
326 infoheader->biBitCount = win_infoheader.biBitCount;
327 infoheader->biCompression = win_infoheader.biCompression;
328 infoheader->biClrUsed = win_infoheader.biClrUsed;
329 infoheader->rMask = win_infoheader.redMask;
330 infoheader->gMask = win_infoheader.greenMask;
331 infoheader->bMask = win_infoheader.blueMask;
332 infoheader->aMask = win_infoheader.alphaMask;
333
334 return 0;
335}
336
337/* read_os2_bminfoheader:
338 * Reads information from an OS/2 format BMP file header.
339 */
340static int read_os2_bminfoheader(FILE *f, BITMAPINFOHEADER *infoheader)
341{
342 OS2BMPINFOHEADER os2_infoheader;
343
344 os2_infoheader.biWidth = fgetw(f);
345 os2_infoheader.biHeight = fgetw(f);
346 os2_infoheader.biPlanes = fgetw(f);
347 os2_infoheader.biBitCount = fgetw(f);
348
349 infoheader->biWidth = os2_infoheader.biWidth;
350 infoheader->biHeight = os2_infoheader.biHeight;
351 infoheader->biBitCount = os2_infoheader.biBitCount;
352 infoheader->biCompression = 0;
353 infoheader->biClrUsed = -1; // Not defined in this format
354
355 return 0;
356}
357
358/* read_v4_bminfoheader:
359 * Reads information from an V4 format BMP file header.
360 */
361static int read_v4_bminfoheader(FILE *f, BITMAPINFOHEADER *infoheader)
362{
363 BMPV4HEADER v4_infoheader;
364
365 v4_infoheader.bV4Width = fgetl(f);
366 v4_infoheader.bV4Height = fgetl(f);
367 v4_infoheader.bV4Planes = fgetw(f);
368 v4_infoheader.bV4BitCount = fgetw(f);
369 v4_infoheader.bV4Compression = fgetl(f);
370
371 v4_infoheader.bV4SizeImage = fgetl(f);
372 v4_infoheader.bV4XPelsPerMeter = fgetl(f);
373 v4_infoheader.bV4YPelsPerMeter = fgetl(f);
374 v4_infoheader.bV4ClrUsed = fgetl(f);
375 v4_infoheader.bV4ClrImportant = fgetl(f);
376
377 v4_infoheader.bV4RedMask = fgetl(f);
378 v4_infoheader.bV4GreenMask = fgetl(f);
379 v4_infoheader.bV4BlueMask = fgetl(f);
380 v4_infoheader.bV4AlphaMask = fgetl(f);
381
382 // TO DO: support ICC profiles and colorimetry
383 v4_infoheader.bV4CSType = fgetl(f);
384
385 // CIEXYZTRIPLE {
386 v4_infoheader.bV4Endpoints.ciexyzRed.ciexyzX = fgetl(f);
387 v4_infoheader.bV4Endpoints.ciexyzRed.ciexyzY = fgetl(f);
388 v4_infoheader.bV4Endpoints.ciexyzRed.ciexyzZ = fgetl(f);
389
390 v4_infoheader.bV4Endpoints.ciexyzGreen.ciexyzX = fgetl(f);
391 v4_infoheader.bV4Endpoints.ciexyzGreen.ciexyzY = fgetl(f);
392 v4_infoheader.bV4Endpoints.ciexyzGreen.ciexyzZ = fgetl(f);
393
394 v4_infoheader.bV4Endpoints.ciexyzBlue.ciexyzX = fgetl(f);
395 v4_infoheader.bV4Endpoints.ciexyzBlue.ciexyzY = fgetl(f);
396 v4_infoheader.bV4Endpoints.ciexyzBlue.ciexyzZ = fgetl(f);
397 // } CIEXYZTRIPLE
398
399 v4_infoheader.bV4GammaRed = fgetl(f);
400 v4_infoheader.bV4GammaGreen = fgetl(f);
401 v4_infoheader.bV4GammaBlue = fgetl(f);
402
403 infoheader->biWidth = v4_infoheader.bV4Width;
404 infoheader->biHeight = v4_infoheader.bV4Height;
405 infoheader->biBitCount = v4_infoheader.bV4BitCount;
406 infoheader->biCompression = v4_infoheader.bV4Compression;
407 infoheader->biClrUsed = v4_infoheader.bV4ClrUsed;
408
409 infoheader->rMask = v4_infoheader.bV4RedMask;
410 infoheader->gMask = v4_infoheader.bV4GreenMask;
411 infoheader->bMask = v4_infoheader.bV4BlueMask;
412 infoheader->aMask = v4_infoheader.bV4AlphaMask;
413
414 return 0;
415}
416
417/* read_v5_bminfoheader:
418 * Reads information from an V5 format BMP file header.
419 */
420static int read_v5_bminfoheader(FILE *f, BITMAPINFOHEADER *infoheader)
421{
422 BMPV5HEADER v5_infoheader;
423
424 v5_infoheader.bV5Width = fgetl(f);
425 v5_infoheader.bV5Height = fgetl(f);
426 v5_infoheader.bV5Planes = fgetw(f);
427 v5_infoheader.bV5BitCount = fgetw(f);
428 v5_infoheader.bV5Compression = fgetl(f);
429
430 v5_infoheader.bV5SizeImage = fgetl(f);
431 v5_infoheader.bV5XPelsPerMeter = fgetl(f);
432 v5_infoheader.bV5YPelsPerMeter = fgetl(f);
433 v5_infoheader.bV5ClrUsed = fgetl(f);
434 v5_infoheader.bV5ClrImportant = fgetl(f);
435
436 v5_infoheader.bV5RedMask = fgetl(f);
437 v5_infoheader.bV5GreenMask = fgetl(f);
438 v5_infoheader.bV5BlueMask = fgetl(f);
439 v5_infoheader.bV5AlphaMask = fgetl(f);
440
441 // TO DO: support ICC profiles and colorimetry
442 v5_infoheader.bV5CSType = fgetl(f);
443
444 // CIEXYZTRIPLE {
445 v5_infoheader.bV5Endpoints.ciexyzRed.ciexyzX = fgetl(f);
446 v5_infoheader.bV5Endpoints.ciexyzRed.ciexyzY = fgetl(f);
447 v5_infoheader.bV5Endpoints.ciexyzRed.ciexyzZ = fgetl(f);
448
449 v5_infoheader.bV5Endpoints.ciexyzGreen.ciexyzX = fgetl(f);
450 v5_infoheader.bV5Endpoints.ciexyzGreen.ciexyzY = fgetl(f);
451 v5_infoheader.bV5Endpoints.ciexyzGreen.ciexyzZ = fgetl(f);
452
453 v5_infoheader.bV5Endpoints.ciexyzBlue.ciexyzX = fgetl(f);
454 v5_infoheader.bV5Endpoints.ciexyzBlue.ciexyzY = fgetl(f);
455 v5_infoheader.bV5Endpoints.ciexyzBlue.ciexyzZ = fgetl(f);
456 // } CIEXYZTRIPLE
457
458 v5_infoheader.bV5GammaRed = fgetl(f);
459 v5_infoheader.bV5GammaGreen = fgetl(f);
460 v5_infoheader.bV5GammaBlue = fgetl(f);
461
462 v5_infoheader.bV5Intent = fgetl(f);
463 v5_infoheader.bV5ProfileData = fgetl(f);
464 v5_infoheader.bV5ProfileSize = fgetl(f);
465 fgetl(f); // <-- Reserved DWORD
466
467 infoheader->biWidth = v5_infoheader.bV5Width;
468 infoheader->biHeight = v5_infoheader.bV5Height;
469 infoheader->biBitCount = v5_infoheader.bV5BitCount;
470 infoheader->biCompression = v5_infoheader.bV5Compression;
471 infoheader->biClrUsed = v5_infoheader.bV5ClrUsed;
472
473 infoheader->rMask = v5_infoheader.bV5RedMask;
474 infoheader->gMask = v5_infoheader.bV5GreenMask;
475 infoheader->bMask = v5_infoheader.bV5BlueMask;
476 infoheader->aMask = v5_infoheader.bV5AlphaMask;
477
478 return 0;
479}
480
481/* read_bmicolors:
482 * Loads the color palette for 1,4,8 bit formats.
483 */
484static void read_bmicolors(FileOp* fop, int bytes, FILE *f, bool win_flag)
485{
486 int i, j, r, g, b;
487
488 for (i=j=0; i+3 <= bytes && j < 256; ) {
489 b = fgetc(f);
490 g = fgetc(f);
491 r = fgetc(f);
492
493 fop->sequenceSetColor(j, r, g, b);
494
495 j++;
496 i += 3;
497
498 if (win_flag && i < bytes) {
499 fgetc(f);
500 i++;
501 }
502 }
503
504 // Set the number of colors in the palette
505 fop->sequenceSetNColors(j);
506
507 for (; i<bytes; i++)
508 fgetc(f);
509}
510
511/* read_1bit_line:
512 * Support function for reading the 1 bit bitmap file format.
513 */
514static void read_1bit_line(int length, FILE *f, Image *image, int line)
515{
516 unsigned char b[32];
517 unsigned long n;
518 int i, j, k;
519 int pix;
520
521 for (i=0; i<length; i++) {
522 j = i % 32;
523 if (j == 0) {
524 n = fgetl(f);
525 n =
526 ((n&0x000000ff)<<24) |
527 ((n&0x0000ff00)<< 8) |
528 ((n&0x00ff0000)>> 8) |
529 ((n&0xff000000)>>24);
530 for (k=0; k<32; k++) {
531 b[31-k] = (char)(n & 1);
532 n = n >> 1;
533 }
534 }
535 pix = b[j];
536 put_pixel(image, i, line, pix);
537 }
538}
539
540/* read_2bit_line (not standard):
541 * Support function for reading the 2 bit bitmap file format.
542 */
543static void read_2bit_line(int length, FILE *f, Image *image, int line)
544{
545 unsigned char b[16];
546 unsigned long n;
547 int i, j, k;
548 int temp;
549 int pix;
550
551 for (i=0; i<length; i++) {
552 j = i % 16;
553 if (j == 0) {
554 n = fgetl(f);
555 for (k=0; k<4; k++) {
556 temp = n & 255;
557 b[k*4+3] = temp & 3;
558 temp = temp >> 2;
559 b[k*4+2] = temp & 3;
560 temp = temp >> 2;
561 b[k*4+1] = temp & 3;
562 temp = temp >> 2;
563 b[k*4] = temp & 3;
564 n = n >> 8;
565 }
566 }
567 pix = b[j];
568 put_pixel(image, i, line, pix);
569 }
570}
571
572/* read_4bit_line:
573 * Support function for reading the 4 bit bitmap file format.
574 */
575static void read_4bit_line(int length, FILE *f, Image *image, int line)
576{
577 unsigned char b[8];
578 unsigned long n;
579 int i, j, k;
580 int temp;
581 int pix;
582
583 for (i=0; i<length; i++) {
584 j = i % 8;
585 if (j == 0) {
586 n = fgetl(f);
587 for (k=0; k<4; k++) {
588 temp = n & 255;
589 b[k*2+1] = temp & 15;
590 temp = temp >> 4;
591 b[k*2] = temp & 15;
592 n = n >> 8;
593 }
594 }
595 pix = b[j];
596 put_pixel(image, i, line, pix);
597 }
598}
599
600/* read_8bit_line:
601 * Support function for reading the 8 bit bitmap file format.
602 */
603static void read_8bit_line(int length, FILE *f, Image *image, int line)
604{
605 unsigned char b[4];
606 unsigned long n;
607 int i, j, k;
608 int pix;
609
610 for (i=0; i<length; i++) {
611 j = i % 4;
612 if (j == 0) {
613 n = fgetl(f);
614 for (k=0; k<4; k++) {
615 b[k] = (char)(n & 255);
616 n = n >> 8;
617 }
618 }
619 pix = b[j];
620 put_pixel(image, i, line, pix);
621 }
622}
623
624static void read_16bit_line(int length, FILE *f, Image *image, int line, bool& withAlpha)
625{
626 int i, r, g, b, a, word;
627
628 for (i=0; i<length; i++) {
629 word = fgetw(f);
630
631 r = (word >> 10) & 0x1f;
632 g = (word >> 5) & 0x1f;
633 b = (word) & 0x1f;
634 a = (word & 0x8000 ? 255 : 0);
635 if (a)
636 withAlpha = true;
637 put_pixel(image, i, line,
638 rgba(scale_5bits_to_8bits(r),
639 scale_5bits_to_8bits(g),
640 scale_5bits_to_8bits(b), a));
641 }
642
643 i = (2*i) % 4;
644 if (i > 0)
645 while (i++ < 4)
646 fgetc(f);
647}
648
649static void read_24bit_line(int length, FILE *f, Image *image, int line)
650{
651 int i, r, g, b;
652
653 for (i=0; i<length; i++) {
654 b = fgetc(f);
655 g = fgetc(f);
656 r = fgetc(f);
657 put_pixel(image, i, line, rgba(r, g, b, 255));
658 }
659
660 i = (3*i) % 4;
661 if (i > 0)
662 while (i++ < 4)
663 fgetc(f);
664}
665
666static void read_32bit_line(int length, FILE *f, Image *image, int line,
667 bool& withAlpha)
668{
669 int i, r, g, b, a;
670
671 for (i=0; i<length; i++) {
672 b = fgetc(f);
673 g = fgetc(f);
674 r = fgetc(f);
675 a = fgetc(f);
676 if (a)
677 withAlpha = true;
678 put_pixel(image, i, line, rgba(r, g, b, a));
679 }
680}
681
682/* read_image:
683 * For reading the noncompressed BMP image format.
684 */
685static void read_image(FILE *f, Image *image, const BITMAPINFOHEADER *infoheader, FileOp *fop, bool& withAlpha)
686{
687 int i, line, height, dir;
688
689 height = (int)infoheader->biHeight;
690 line = height < 0 ? 0: height-1;
691 dir = height < 0 ? 1: -1;
692 height = ABS(height);
693
694 for (i=0; i<height; i++, line+=dir) {
695 switch (infoheader->biBitCount) {
696 case 1: read_1bit_line(infoheader->biWidth, f, image, line); break;
697 case 2: read_2bit_line(infoheader->biWidth, f, image, line); break;
698 case 4: read_4bit_line(infoheader->biWidth, f, image, line); break;
699 case 8: read_8bit_line(infoheader->biWidth, f, image, line); break;
700 case 16: read_16bit_line(infoheader->biWidth, f, image, line, withAlpha); break;
701 case 24: read_24bit_line(infoheader->biWidth, f, image, line); break;
702 case 32: read_32bit_line(infoheader->biWidth, f, image, line, withAlpha); break;
703 }
704
705 fop->setProgress((float)(i+1) / (float)(height));
706 if (fop->isStop())
707 break;
708 }
709
710 if ((infoheader->biBitCount == 32 ||
711 infoheader->biBitCount == 16 ) && !withAlpha) {
712 LockImageBits<RgbTraits> imageBits(image, image->bounds());
713 auto imgIt = imageBits.begin(), imgEnd = imageBits.end();
714 for (; imgIt != imgEnd; ++imgIt)
715 *imgIt |= 0xff000000;
716 }
717}
718
719/* read_rle8_compressed_image:
720 * For reading the 8 bit RLE compressed BMP image format.
721 *
722 * @note This support compressed top-down bitmaps, the MSDN says that
723 * they can't exist, but Photoshop can create them.
724 */
725static void read_rle8_compressed_image(FILE *f, Image *image, const BITMAPINFOHEADER *infoheader)
726{
727 unsigned char count, val, val0;
728 int j, pos, line, height, dir;
729 int eolflag, eopicflag;
730
731 eopicflag = 0;
732
733 height = (int)infoheader->biHeight;
734 line = height < 0 ? 0: height-1;
735 dir = height < 0 ? 1: -1;
736 height = ABS(height);
737
738 while (eopicflag == 0) {
739 pos = 0; /* x position in bitmap */
740 eolflag = 0; /* end of line flag */
741
742 while ((eolflag == 0) && (eopicflag == 0)) {
743 count = fgetc(f);
744 val = fgetc(f);
745
746 if (count > 0) { /* repeat pixel count times */
747 for (j=0;j<count;j++) {
748 put_pixel(image, pos, line, val);
749 pos++;
750 }
751 }
752 else {
753 switch (val) {
754
755 case 0: /* end of line flag */
756 eolflag=1;
757 break;
758
759 case 1: /* end of picture flag */
760 eopicflag=1;
761 break;
762
763 case 2: /* displace picture */
764 count = fgetc(f);
765 val = fgetc(f);
766 pos += count;
767 line += val*dir;
768 break;
769
770 default: /* read in absolute mode */
771 for (j=0; j<val; j++) {
772 val0 = fgetc(f);
773 put_pixel(image, pos, line, val0);
774 pos++;
775 }
776
777 if (j%2 == 1)
778 val0 = fgetc(f); /* align on word boundary */
779 break;
780
781 }
782 }
783
784 if (pos-1 > (int)infoheader->biWidth)
785 eolflag=1;
786 }
787
788 line += dir;
789 if (line < 0 || line >= height)
790 eopicflag = 1;
791 }
792}
793
794/* read_rle4_compressed_image:
795 * For reading the 4 bit RLE compressed BMP image format.
796 *
797 * @note This support compressed top-down bitmaps, the MSDN says that
798 * they can't exist, but Photoshop can create them.
799 */
800static void read_rle4_compressed_image(FILE *f, Image *image, const BITMAPINFOHEADER *infoheader)
801{
802 unsigned char b[8];
803 unsigned char count;
804 unsigned short val0, val;
805 int j, k, pos, line, height, dir;
806 int eolflag, eopicflag;
807
808 eopicflag = 0; /* end of picture flag */
809
810 height = (int)infoheader->biHeight;
811 line = height < 0 ? 0: height-1;
812 dir = height < 0 ? 1: -1;
813 height = ABS(height);
814
815 while (eopicflag == 0) {
816 pos = 0;
817 eolflag = 0; /* end of line flag */
818
819 while ((eolflag == 0) && (eopicflag == 0)) {
820 count = fgetc(f);
821 val = fgetc(f);
822
823 if (count > 0) { /* repeat pixels count times */
824 b[1] = val & 15;
825 b[0] = (val >> 4) & 15;
826 for (j=0; j<count; j++) {
827 put_pixel(image, pos, line, b[j%2]);
828 pos++;
829 }
830 }
831 else {
832 switch (val) {
833
834 case 0: /* end of line */
835 eolflag=1;
836 break;
837
838 case 1: /* end of picture */
839 eopicflag=1;
840 break;
841
842 case 2: /* displace image */
843 count = fgetc(f);
844 val = fgetc(f);
845 pos += count;
846 line += val*dir;
847 break;
848
849 default: /* read in absolute mode */
850 for (j=0; j<val; j++) {
851 if ((j%4) == 0) {
852 val0 = fgetw(f);
853 for (k=0; k<2; k++) {
854 b[2*k+1] = val0 & 15;
855 val0 = val0 >> 4;
856 b[2*k] = val0 & 15;
857 val0 = val0 >> 4;
858 }
859 }
860 put_pixel(image, pos, line, b[j%4]);
861 pos++;
862 }
863 break;
864 }
865 }
866
867 if (pos-1 > (int)infoheader->biWidth)
868 eolflag=1;
869 }
870
871 line += dir;
872 if (line < 0 || line >= height)
873 eopicflag = 1;
874 }
875}
876
877static uint32_t calc_shift(const uint32_t channelMask, int& channelBits)
878{
879 uint32_t channelShift = 0;
880 uint32_t mask = 0;
881 if (channelMask) {
882 mask = ~channelMask;
883 while (mask & 1) {
884 ++channelShift;
885 mask >>= 1;
886 }
887 if (mask) {
888 mask = ~mask;
889 while (mask & 1) {
890 channelBits++;
891 mask >>= 1;
892 }
893 }
894 else
895 channelBits = 32 - channelShift;
896 }
897 else
898 channelBits = 8;
899 return channelShift;
900}
901
902static int read_bitfields_image(FILE *f, Image *image, BITMAPINFOHEADER *infoheader,
903 uint32_t rmask, uint32_t gmask, uint32_t bmask,
904 uint32_t amask, bool& withAlpha)
905{
906 uint32_t buffer, rshift, gshift, bshift, ashift;
907 int rbits = 0, gbits = 0, bbits = 0, abits = 0;
908 int i, j, k, line, height, dir, r, g, b, a;
909 int bits_per_pixel;
910 int bytes_per_pixel;
911
912 height = (int)infoheader->biHeight;
913 line = height < 0 ? 0: height-1;
914 dir = height < 0 ? 1: -1;
915 height = ABS(height);
916
917 /* calculate shifts */
918 rshift = calc_shift(rmask, rbits);
919 gshift = calc_shift(gmask, gbits);
920 bshift = calc_shift(bmask, bbits);
921 ashift = calc_shift(amask, abits);
922
923 /* calculate bits-per-pixel and bytes-per-pixel */
924 bits_per_pixel = infoheader->biBitCount;
925 bytes_per_pixel = ((bits_per_pixel / 8) +
926 ((bits_per_pixel % 8) > 0 ? 1: 0));
927
928 for (i=0; i<height; i++, line+=dir) {
929 for (j=0; j<(int)infoheader->biWidth; j++) {
930 /* read the DWORD, WORD or BYTE in little-endian order */
931 buffer = 0;
932 for (k=0; k<bytes_per_pixel; k++)
933 buffer |= fgetc(f) << (k<<3);
934
935 r = (buffer & rmask) >> rshift;
936 g = (buffer & gmask) >> gshift;
937 b = (buffer & bmask) >> bshift;
938 a = (buffer & amask) >> ashift;
939
940 r = (rbits == 8 ? r : scale_xxbits_to_8bits(rbits, r) );
941 g = (gbits == 8 ? g : scale_xxbits_to_8bits(gbits, g) );
942 b = (bbits == 8 ? b : scale_xxbits_to_8bits(bbits, b) );
943 a = (abits == 8 ? a : scale_xxbits_to_8bits(abits, a) );
944
945 if (a)
946 withAlpha = true;
947 put_pixel_fast<RgbTraits>(image, j, line, rgba(r, g, b, a));
948 }
949
950 j = (bytes_per_pixel*j) % 4;
951 if (j > 0)
952 while (j++ < 4)
953 fgetc(f);
954 }
955
956 if (!withAlpha) {
957 LockImageBits<RgbTraits> imageBits(image, image->bounds());
958 auto imgIt = imageBits.begin(), imgEnd = imageBits.end();
959 for (; imgIt != imgEnd; ++imgIt)
960 *imgIt |= 0xff000000;
961 }
962
963 return 0;
964}
965
966bool BmpFormat::onLoad(FileOp *fop)
967{
968 uint32_t rmask, gmask, bmask, amask;
969 BITMAPFILEHEADER fileheader;
970 BITMAPINFOHEADER infoheader;
971 PixelFormat pixelFormat;
972 int format;
973
974 FileHandle handle(open_file_with_exception(fop->filename(), "rb"));
975 FILE* f = handle.get();
976
977 if (read_bmfileheader(f, &fileheader) != 0)
978 return false;
979
980 infoheader.biSize = fgetl(f);
981
982 if (infoheader.biSize >= 16 && infoheader.biSize <= 64) {
983 format = BMP_OPTIONS_FORMAT_WINDOWS;
984
985 if (read_win_bminfoheader(f, &infoheader) != 0) {
986 return false;
987 }
988 if (infoheader.biCompression != BI_BITFIELDS &&
989 infoheader.biCompression != BI_ALPHABITFIELDS)
990 read_bmicolors(fop,
991 fileheader.bfOffBits - infoheader.biSize - OS2FILEHEADERSIZE,
992 f, true);
993 else if (infoheader.biBitCount <= 8)
994 return false;
995 }
996 else if (infoheader.biSize == OS2INFOHEADERSIZE) {
997 format = BMP_OPTIONS_FORMAT_OS2;
998
999 if (read_os2_bminfoheader(f, &infoheader) != 0) {
1000 return false;
1001 }
1002 /* compute number of colors recorded */
1003 if (infoheader.biCompression != BI_BITFIELDS &&
1004 infoheader.biCompression != BI_ALPHABITFIELDS)
1005 read_bmicolors(fop,
1006 fileheader.bfOffBits - infoheader.biSize - OS2FILEHEADERSIZE,
1007 f, false);
1008 else if (infoheader.biBitCount <= 8)
1009 return false;
1010 }
1011 else if (infoheader.biSize == BV4INFOHEADERSIZE) {
1012 format = BMP_OPTIONS_FORMAT_WINDOWS;
1013
1014 if (read_v4_bminfoheader(f, &infoheader) != 0) {
1015 return false;
1016 }
1017 /* compute number of colors recorded */
1018 if (infoheader.biCompression != BI_BITFIELDS &&
1019 infoheader.biCompression != BI_ALPHABITFIELDS)
1020 read_bmicolors(fop,
1021 fileheader.bfOffBits - infoheader.biSize - OS2FILEHEADERSIZE,
1022 f, true);
1023 else if (infoheader.biBitCount <= 8)
1024 return false;
1025 }
1026 else if (infoheader.biSize == BV5INFOHEADERSIZE) {
1027 format = BMP_OPTIONS_FORMAT_WINDOWS;
1028
1029 if (read_v5_bminfoheader(f, &infoheader) != 0) {
1030 return false;
1031 }
1032 /* compute number of colors recorded */
1033 if (infoheader.biCompression != BI_BITFIELDS &&
1034 infoheader.biCompression != BI_ALPHABITFIELDS)
1035 read_bmicolors(fop,
1036 fileheader.bfOffBits - infoheader.biSize - OS2FILEHEADERSIZE,
1037 f, true);
1038 else if (infoheader.biBitCount <= 8)
1039 return false;
1040 }
1041 else {
1042 return false;
1043 }
1044
1045 // Check compatible Compression
1046 if (infoheader.biCompression == 4 ||
1047 infoheader.biCompression == 5 ||
1048 infoheader.biCompression > 6) {
1049 fop->setError("Unsupported BMP compression.\n");
1050 return false;
1051 }
1052
1053 // Check image size is valid
1054 {
1055 if (int(infoheader.biWidth) < 1 ||
1056 ABS(int(infoheader.biHeight)) == 0) {
1057 fop->setError("Invalid BMP size.\n");
1058 return false;
1059 }
1060
1061 uint32_t size = infoheader.biWidth * uint32_t(ABS(int(infoheader.biHeight)));
1062 if (infoheader.biBitCount >= 8)
1063 size *= (infoheader.biBitCount / 8);
1064 else if (8 / infoheader.biBitCount > 0)
1065 size /= (8 / infoheader.biBitCount);
1066
1067 if (size > kMaxBmpSize) {
1068 fop->setError(fmt::format("BMP size unsupported ({:.2f} MB > {:.2f} MB).\n",
1069 size / 1024.0 / 1024.0,
1070 kMaxBmpSize / 1024.0 / 1024.0).c_str());
1071 return false;
1072 }
1073 }
1074
1075 if ((infoheader.biBitCount == 32) ||
1076 (infoheader.biBitCount == 24) ||
1077 (infoheader.biBitCount == 16))
1078 pixelFormat = IMAGE_RGB;
1079 else
1080 pixelFormat = IMAGE_INDEXED;
1081
1082 /* bitfields have the 'mask' for each component */
1083 if (infoheader.isRGBMasks()) {
1084 rmask = infoheader.rMask;
1085 gmask = infoheader.gMask;
1086 bmask = infoheader.bMask;
1087 amask = (infoheader.isAlphaMask() ? infoheader.aMask : 0);
1088 }
1089 else
1090 rmask = gmask = bmask = amask = 0;
1091
1092 ImageRef image = fop->sequenceImage(pixelFormat,
1093 infoheader.biWidth,
1094 ABS((int)infoheader.biHeight));
1095 if (!image) {
1096 return false;
1097 }
1098
1099 if (pixelFormat == IMAGE_RGB)
1100 clear_image(image.get(),
1101 rgba(0, 0, 0, (infoheader.isAlphaMask() ? 0 : 255)));
1102 else
1103 clear_image(image.get(), 0);
1104
1105 // We indirectly calculate 'on the fly' if the BMP file
1106 // has all its pixels with alpha value equal to 0 (i.e. BMP
1107 // without alpha channel) or if there is at least one pixel
1108 // with non 0 alpha (i.e. BMP works with alpha channel).
1109 // The result of this analysis will be stored in the boolean 'withAlpha'.
1110 bool withAlpha = false;
1111 switch (infoheader.biCompression) {
1112
1113 case BI_RGB:
1114 read_image(f, image.get(), &infoheader, fop, withAlpha);
1115 break;
1116
1117 case BI_RLE8:
1118 read_rle8_compressed_image(f, image.get(), &infoheader);
1119 break;
1120
1121 case BI_RLE4:
1122 read_rle4_compressed_image(f, image.get(), &infoheader);
1123 break;
1124
1125 case BI_BITFIELDS:
1126 case BI_ALPHABITFIELDS:
1127 if (read_bitfields_image(f, image.get(), &infoheader,
1128 rmask, gmask, bmask, amask, withAlpha) < 0) {
1129 fop->setError("Unsupported bitfields in the BMP file.\n");
1130 return false;
1131 }
1132 break;
1133
1134 default:
1135 fop->setError("Unsupported BMP compression.\n");
1136 return false;
1137 }
1138
1139 if (ferror(f)) {
1140 fop->setError("Error reading file.\n");
1141 return false;
1142 }
1143
1144 // Setup the file-data.
1145 if (!fop->formatOptions()) {
1146 auto bmp_options = std::make_shared<BmpOptions>();
1147
1148 bmp_options->format = format;
1149 bmp_options->compression = infoheader.biCompression;
1150 bmp_options->bits_per_pixel = infoheader.biBitCount;
1151 bmp_options->red_mask = rmask;
1152 bmp_options->green_mask = gmask;
1153 bmp_options->blue_mask = bmask;
1154 if (withAlpha) {
1155 bmp_options->alpha_mask = amask;
1156 fop->sequenceSetHasAlpha(true);
1157 }
1158 else
1159 bmp_options->alpha_mask = 0;
1160 fop->setLoadedFormatOptions(bmp_options);
1161 }
1162
1163 return true;
1164}
1165
1166#ifdef ENABLE_SAVE
1167bool BmpFormat::onSave(FileOp *fop)
1168{
1169 const FileAbstractImage* img = fop->abstractImage();
1170 const ImageSpec spec = img->spec();
1171 const int w = spec.width();
1172 const int h = spec.height();
1173 int bfSize;
1174 int biSizeImage;
1175 int ncolors = fop->sequenceGetNColors();
1176 int bpp = 0;
1177 bool withAlpha = img->needAlpha();
1178
1179 switch (spec.colorMode()) {
1180 case ColorMode::RGB:
1181 if (withAlpha)
1182 bpp = 32;
1183 else
1184 bpp = 24;
1185 break;
1186 case ColorMode::GRAYSCALE:
1187 bpp = 8;
1188 break;
1189 case ColorMode::INDEXED: {
1190 if (ncolors > 16)
1191 bpp = 8;
1192 else if (ncolors > 2)
1193 bpp = 4;
1194 else
1195 bpp = 1;
1196 ncolors = (1 << bpp);
1197 break;
1198 }
1199 default:
1200 // TODO save ColorMode::BITMAP as 1bpp bmp?
1201 // Invalid image format
1202 fop->setError("Unsupported color mode.\n");
1203 return false;
1204 }
1205
1206 int filler = int((32 - ((w*bpp-1) & 31)-1) / 8);
1207 int c, i, j, r, g, b;
1208
1209 if (bpp <= 8) {
1210 biSizeImage = (w + filler)*bpp/8 * h;
1211 bfSize = (WININFOHEADERSIZE + OS2FILEHEADERSIZE // header
1212 + ncolors*4 // palette
1213 + biSizeImage); // image data
1214 }
1215 else {
1216 biSizeImage = (w*bpp/8 + filler) * h;
1217 if (withAlpha)
1218 bfSize = BV3INFOHEADERSIZE +
1219 OS2FILEHEADERSIZE + biSizeImage; // header + image data
1220 else
1221 bfSize = WININFOHEADERSIZE +
1222 OS2FILEHEADERSIZE + biSizeImage; // header + image data
1223 }
1224
1225 FileHandle handle(open_file_with_exception_sync_on_close(fop->filename(), "wb"));
1226 FILE* f = handle.get();
1227
1228 /* file_header */
1229 fputw(0x4D42, f); /* bfType ("BM") */
1230 fputl(bfSize, f); /* bfSize */
1231 fputw(0, f); /* bfReserved1 */
1232 fputw(0, f); /* bfReserved2 */
1233
1234 if (bpp <= 8) {
1235 fputl(WININFOHEADERSIZE + OS2FILEHEADERSIZE +
1236 ncolors * 4, f); /* bfOffBits */
1237 /* info_header */
1238 fputl(WININFOHEADERSIZE, f); /* biSize */
1239 }
1240 else if (withAlpha) {
1241 fputl(BV3INFOHEADERSIZE + OS2FILEHEADERSIZE, f); /* bfOffBits -taking account RBGA masks- */
1242 /* info_header */
1243 fputl(BV3INFOHEADERSIZE, f); /* biSize */
1244 }
1245 else {
1246 fputl(WININFOHEADERSIZE + OS2FILEHEADERSIZE, f); /* bfOffBits */
1247 /* info_header */
1248 fputl(WININFOHEADERSIZE, f); /* biSize */
1249 }
1250
1251 fputl(w, f); /* biWidth */
1252 fputl(h, f); /* biHeight */
1253 fputw(1, f); /* biPlanes */
1254 fputw(bpp, f); /* biBitCount */
1255 if (withAlpha) /* biCompression */
1256 fputl(BI_BITFIELDS, f);
1257 else
1258 fputl(BI_RGB, f);
1259 fputl(biSizeImage, f); /* biSizeImage */
1260 fputl(0xB12, f); /* biXPelsPerMeter (0xB12 = 72 dpi) */
1261 fputl(0xB12, f); /* biYPelsPerMeter */
1262
1263 if (bpp <= 8) {
1264 fputl(ncolors, f); /* biClrUsed */
1265 fputl(ncolors, f); /* biClrImportant */
1266
1267 // Save the palette
1268 for (i=0; i<ncolors; i++) {
1269 fop->sequenceGetColor(i, &r, &g, &b);
1270 fputc(b, f);
1271 fputc(g, f);
1272 fputc(r, f);
1273 fputc(0, f);
1274 }
1275 }
1276 else {
1277 fputl(0, f); /* biClrUsed */
1278 fputl(0, f); /* biClrImportant */
1279 if (withAlpha) {
1280 fputl(0x00ff0000, f);
1281 fputl(0x0000ff00, f);
1282 fputl(0x000000ff, f);
1283 fputl(0xff000000, f);
1284 }
1285 }
1286
1287 // Only used in indexed mode
1288 int colorsPerByte = std::max(1, 8/bpp);
1289 int colorMask;
1290 switch (bpp) {
1291 case 8: colorMask = 0xFF; break;
1292 case 4: colorMask = 0x0F; break;
1293 case 1: colorMask = 0x01; break;
1294 default: colorMask = 0; break;
1295 }
1296
1297 // Save image pixels (from bottom to top)
1298 for (i=h-1; i>=0; i--) {
1299 switch (spec.colorMode()) {
1300 case ColorMode::RGB: {
1301 auto scanline = (const uint32_t*)img->getScanline(i);
1302 for (j=0; j<w; ++j) {
1303 c = scanline[j];
1304 fputc(rgba_getb(c), f);
1305 fputc(rgba_getg(c), f);
1306 fputc(rgba_getr(c), f);
1307 if (withAlpha)
1308 fputc(rgba_geta(c), f);
1309 }
1310 break;
1311 }
1312 case ColorMode::GRAYSCALE: {
1313 auto scanline = (const uint16_t*)img->getScanline(i);
1314 for (j=0; j<w; ++j) {
1315 c = scanline[j];
1316 fputc(graya_getv(c), f);
1317 }
1318 break;
1319 }
1320 case ColorMode::INDEXED: {
1321 auto scanline = (const uint8_t*)img->getScanline(i);
1322 for (j=0; j<w; ) {
1323 uint8_t value = 0;
1324 for (int k=colorsPerByte-1; k>=0 && j<w; --k, ++j) {
1325 c = scanline[j];
1326 value |= (c & colorMask) << (bpp*k);
1327 }
1328 fputc(value, f);
1329 }
1330 break;
1331 }
1332 }
1333
1334 for (j=0; j<filler; j++)
1335 fputc(0, f);
1336
1337 fop->setProgress((float)(h-i) / (float)h);
1338 }
1339
1340 if (ferror(f)) {
1341 fop->setError("Error writing file.\n");
1342 return false;
1343 }
1344 else {
1345 return true;
1346 }
1347}
1348#endif
1349
1350} // namespace app
1351