1 #ifdef _MSC_VER
2 #pragma warning(disable : 4996)
3 #endif
4 
5 #include <memory>
6 
7 #include "tiio_bmp.h"
8 // #include "bmp/filebmp.h"
9 #include "tpixel.h"
10 #include "tpixelgr.h"
11 #include "tproperty.h"
12 //#include "tconvert.h"
13 #include <stdio.h>
14 
15 //=========================================================
16 
17 namespace {
18 
19 //=========================================================
20 
21 typedef struct {
22   UINT bfSize;
23   UINT bfOffBits;
24   UINT biSize;
25   UINT biWidth;
26   UINT biHeight;
27   UINT biPlanes;
28   UINT biBitCount;
29   UINT biCompression;
30   UINT biSizeImage;
31   UINT biXPelsPerMeter;
32   UINT biYPelsPerMeter;
33   UINT biClrUsed;
34   UINT biClrImportant;
35   UINT biFilesize;
36   UINT biPad;
37 
38 } BMP_HEADER;
39 
40 const int BMP_WIN_SIZE = 40;
41 const int BMP_OS2_SIZE = 64;
42 
43 const int BMP_BI_RGB  = 0;
44 const int BMP_BI_RLE8 = 1;
45 const int BMP_BI_RLE4 = 2;
46 
47 //=========================================================
48 
getshort(FILE * fp)49 UINT getshort(FILE *fp) {
50   int c = getc(fp), c1 = getc(fp);
51 
52   return ((UINT)c) + (((UINT)c1) << 8);
53 }
54 
55 //---------------------------------------------------------
56 
getint(FILE * fp)57 UINT getint(FILE *fp) {
58   int c = getc(fp), c1 = getc(fp), c2 = getc(fp), c3 = getc(fp);
59 
60   return (((UINT)c) << 0) + (((UINT)c1) << 8) + (((UINT)c2) << 16) +
61          (((UINT)c3) << 24);
62 }
63 
64 //---------------------------------------------------------
65 
putshort(FILE * fp,int i)66 void putshort(FILE *fp, int i) {
67   int c = (((UINT)i)) & 0xff, c1 = (((UINT)i) >> 8) & 0xff;
68 
69   putc(c, fp);
70   putc(c1, fp);
71 }
72 
73 //---------------------------------------------------------
74 
putint(FILE * fp,int i)75 void putint(FILE *fp, int i) {
76   int c = ((UINT)i) & 0xff, c1 = (((UINT)i) >> 8) & 0xff,
77       c2 = (((UINT)i) >> 16) & 0xff, c3 = (((UINT)i) >> 24) & 0xff;
78 
79   putc(c, fp);
80   putc(c1, fp);
81   putc(c2, fp);
82   putc(c3, fp);
83 }
84 
85 //=========================================================
86 
87 }  // namespace
88 
89 //=========================================================
90 
91 class BmpReader final : public Tiio::Reader {
92   FILE *m_chan;
93   BMP_HEADER m_header;
94   char *m_line;
95   int m_lineSize;
96   std::unique_ptr<TPixel[]> m_cmap;
97   bool m_corrupted;
98   typedef int (BmpReader::*ReadLineMethod)(char *buffer, int x0, int x1,
99                                            int shrink);
100   ReadLineMethod m_readLineMethod;
101 
102 public:
103   BmpReader();
104   ~BmpReader();
105 
106   void open(FILE *file) override;
107 
108   int readNoLine(char *buffer, int x0, int x1, int shrink);
109 
skipBytes(int count)110   void skipBytes(int count) {
111     // fseek(m_chan, count, SEEK_CUR);	//inefficiente se count è piccolo
112     for (int i = 0; i < count; i++) {
113       getc(m_chan);
114     }
115   }
116 
117   int read1Line(char *buffer, int x0, int x1, int shrink);
118   int read4Line(char *buffer, int x0, int x1, int shrink);
119   int read8Line(char *buffer, int x0, int x1, int shrink);
120   int read8LineRle(char *buffer, int x0, int x1, int shrink);
121   int read16m555Line(char *buffer, int x0, int x1, int shrink);
122   int read16m565Line(char *buffer, int x0, int x1, int shrink);
123   int read24Line(char *buffer, int x0, int x1, int shrink);
124   int read32Line(char *buffer, int x0, int x1, int shrink);
125 
126   void readLine(char *buffer, int x0, int x1, int shrink) override;
skipLines(int lineCount)127   int skipLines(int lineCount) override {
128     fseek(m_chan, lineCount * m_lineSize, SEEK_CUR);
129     /* for(int i=0;i<lineCount;i++)
130             skipBytes(m_lineSize);*/
131     return lineCount;
132   }
133 
134 private:
135   void readHeader();
136 };
137 
138 //---------------------------------------------------------
139 
BmpReader()140 BmpReader::BmpReader()
141     : m_chan(0), m_corrupted(false), m_readLineMethod(&BmpReader::readNoLine) {
142   memset(&m_header, 0, sizeof m_header);
143 }
144 
145 //---------------------------------------------------------
146 
~BmpReader()147 BmpReader::~BmpReader() {}
148 
149 //---------------------------------------------------------
150 
open(FILE * file)151 void BmpReader::open(FILE *file) {
152   m_chan = file;
153   readHeader();
154 }
155 
156 //---------------------------------------------------------
157 
readHeader()158 void BmpReader::readHeader() {
159   if (!m_chan) return;
160 
161   fseek(m_chan, 0L, SEEK_END);
162   m_header.biFilesize = ftell(m_chan);
163   fseek(m_chan, 0L, 0);
164 
165   /* read the file type (first two bytes) */
166   char signature[2];
167   signature[0] = fgetc(m_chan);
168   signature[1] = fgetc(m_chan);
169   if (signature[0] != 'B' || signature[1] != 'M') {
170     m_corrupted = true;
171     return;
172   }
173 
174   m_header.bfSize = getint(m_chan);
175 
176   /* reserved and ignored */
177   getshort(m_chan);
178   getshort(m_chan);
179 
180   m_header.bfOffBits = getint(m_chan);
181   m_header.biSize    = getint(m_chan);
182 
183   if ((int)m_header.biSize == BMP_WIN_SIZE ||
184       (int)m_header.biSize == BMP_OS2_SIZE) {
185     m_header.biWidth         = getint(m_chan);
186     m_header.biHeight        = getint(m_chan);
187     m_header.biPlanes        = getshort(m_chan);
188     m_header.biBitCount      = getshort(m_chan);
189     m_header.biCompression   = getint(m_chan);
190     m_header.biSizeImage     = getint(m_chan);
191     m_header.biXPelsPerMeter = getint(m_chan);
192     m_header.biYPelsPerMeter = getint(m_chan);
193     m_header.biClrUsed       = getint(m_chan);
194     m_header.biClrImportant  = getint(m_chan);
195   } else  // old bitmap format
196   {
197     m_header.biWidth    = getshort(m_chan);
198     m_header.biHeight   = getshort(m_chan);
199     m_header.biPlanes   = getshort(m_chan);
200     m_header.biBitCount = getshort(m_chan);
201 
202     /* Not in old versions so have to compute them */
203     m_header.biSizeImage =
204         (((m_header.biPlanes * m_header.biBitCount * m_header.biWidth) + 31) /
205          32) *
206         4 * m_header.biHeight;
207 
208     m_header.biCompression   = BMP_BI_RGB;
209     m_header.biXPelsPerMeter = 0;
210     m_header.biYPelsPerMeter = 0;
211     m_header.biClrUsed       = 0;
212     m_header.biClrImportant  = 0;
213   }
214   m_header.biPad = 0;
215 
216   m_info.m_lx = m_header.biWidth;
217   m_info.m_ly = m_header.biHeight;
218 
219   /*
220 BMP_HEADER *hd = m_header;
221 if ((int)hd->biSize != BMP_WIN_OS2_OLD)
222 {
223 // skip ahead to colormap, using biSize
224 int c = hd->biSize - 40;  // 40 bytes read from biSize to biClrImportant
225 for (int i=0; i<c; i++) getc(m_chan);
226 hd->biPad = hd->bfOffBits - (hd->biSize + 14);
227 }
228 else
229 hd->biPad = 0;
230 */
231 
232   // load up colormap, if any
233   if (m_header.biBitCount < 16) {
234     int cmaplen =
235         (m_header.biClrUsed) ? m_header.biClrUsed : 1 << m_header.biBitCount;
236     assert(cmaplen <= 256);
237     m_cmap.reset(new TPixel[256]);
238     TPixel32 *pix = m_cmap.get();
239     for (int i = 0; i < cmaplen; i++) {
240       pix->b = getc(m_chan);
241       pix->g = getc(m_chan);
242       pix->r = getc(m_chan);
243       pix->m = 255;
244       getc(m_chan);
245       ++pix;
246     }
247   }
248 
249   /*
250 if (hd->biSize != BMP_WIN_OS2_OLD)
251 {
252 // Waste any unused bytes between the colour map (if present)
253 // and the start of the actual bitmap data.
254 while (hd->biPad > 0)
255 {
256   (void) getc(m_chan);
257   hd->biPad--;
258 }
259 }
260 */
261   // get information about the portion of the image to load
262   // get_info_region(&info, x1, y1, x2, y2, scale,
263   //		  (int)hd->biWidth,   (int)hd->biHeight, TNZ_BOTLEFT);
264 
265   //   skip_bmp_lines(fp, hd->biWidth, (unsigned int)(info.startScanRow-1),
266   //   (unsigned int)SEEK_CUR, subtype);
267 
268   int lx = m_info.m_lx;
269 
270   switch (m_header.biBitCount) {
271   case 1:
272     m_info.m_samplePerPixel = 1;
273     m_readLineMethod        = &BmpReader::read1Line;
274     m_lineSize              = (lx + 7) / 8;
275     break;
276   case 4:
277     m_info.m_samplePerPixel                           = 1;
278     if (m_header.biCompression == 0) m_readLineMethod = &BmpReader::read4Line;
279     m_lineSize                                        = (lx + 1) / 2;
280     break;
281   case 8:
282     m_info.m_samplePerPixel = 1;
283     if (m_header.biCompression == 0)
284       m_readLineMethod = &BmpReader::read8Line;
285     else if (m_header.biCompression == 1)
286       m_readLineMethod = &BmpReader::read8LineRle;
287     m_lineSize         = lx;
288     break;
289   case 16:
290     m_info.m_samplePerPixel = 3;
291     if (m_header.biCompression == 0)  // BI_RGB
292       m_readLineMethod = &BmpReader::read16m555Line;
293     else if (m_header.biCompression == 3)  // BI_BITFIELDS
294     {
295       unsigned int rmask = 0, gmask = 0, bmask = 0;
296       rmask = getint(m_chan);
297       gmask = getint(m_chan);
298       bmask = getint(m_chan);
299       if (gmask == 0x7E0)
300         m_readLineMethod = &BmpReader::read16m565Line;
301       else
302         m_readLineMethod = &BmpReader::read16m555Line;
303     }
304     m_lineSize = lx * 2;
305     break;
306   case 24:
307     m_info.m_samplePerPixel = 3;
308     m_readLineMethod        = &BmpReader::read24Line;
309     m_lineSize              = lx * 3;
310     break;
311   case 32:
312     m_info.m_samplePerPixel = 3;
313     m_readLineMethod        = &BmpReader::read32Line;
314     m_lineSize              = lx * 4;
315     break;
316   }
317   m_lineSize += 3 - ((m_lineSize + 3) & 3);
318   fseek(m_chan, m_header.bfOffBits, SEEK_SET);
319 }
320 
321 //---------------------------------------------------------
322 
readLine(char * buffer,int x0,int x1,int shrink)323 void BmpReader::readLine(char *buffer, int x0, int x1, int shrink) {
324   (this->*m_readLineMethod)(buffer, x0, x1, shrink);
325 }
326 
327 //---------------------------------------------------------
328 
readNoLine(char * buffer,int x0,int x1,int shrink)329 int BmpReader::readNoLine(char *buffer, int x0, int x1, int shrink) {
330   skipBytes(m_lineSize);
331   return 0;
332 }
333 
334 //---------------------------------------------------------
335 
read1Line(char * buffer,int x0,int x1,int shrink)336 int BmpReader::read1Line(char *buffer, int x0, int x1, int shrink) {
337   TPixel32 *pix = (TPixel32 *)buffer;
338 
339   // pix += x0;
340   if (x0 > 0) skipBytes(x0 / 8);
341 
342   TPixel32 *endPix = pix + x1 + 1;
343 
344   int value = 0;
345   int index = x0;
346 
347   if (x0 % 8 != 0) {
348     value = getc(m_chan);
349     for (index = x0; index < x0 + 8 - (x0 % 8); index += shrink) {
350       pix[index] = m_cmap[(value >> (7 - ((index) % 8))) & 1];
351     }
352   }
353   value         = getc(m_chan);
354   int prevBlock = index / 8;
355   for (int j = index; pix + j < endPix; j += shrink) {
356     if (j / 8 > prevBlock) value = getc(m_chan);
357     prevBlock                    = j / 8;
358     pix[j]                       = m_cmap[(value >> (7 - (j % 8))) & 1];
359   }
360 
361   /*
362 while(pix+8<=endPix)
363 {
364 value = getc(m_chan);
365 pix[0] = m_cmap[(value>>7)&1];
366 pix[1] = m_cmap[(value>>6)&1];
367 pix[2] = m_cmap[(value>>5)&1];
368 pix[3] = m_cmap[(value>>4)&1];
369 pix[4] = m_cmap[(value>>3)&1];
370 pix[5] = m_cmap[(value>>2)&1];
371 pix[6] = m_cmap[(value>>1)&1];
372 pix[7] = m_cmap[(value>>0)&1];
373    pix +=8*shrink;
374    if(shrink>1) value = getc(m_chan);
375 //pix+=8*shrink;
376    //if(pix+8<endPix && shrink>1) skipBytes(shrink-1);
377 }
378 if(pix<endPix)
379 {
380 value = getc(m_chan);
381 assert(pix+8>=endPix);
382 for(int j=0; pix+j<endPix; j++)
383 pix[j] = m_cmap[(value>>(7-j))&1];
384 }		*/
385   if ((m_info.m_lx - x1) / 8 > 0) {
386     skipBytes((m_info.m_lx - x1) / 8);
387   }
388 
389   int bytes = (m_info.m_lx + 7) / 8;
390   if (m_lineSize - bytes > 0) skipBytes(m_lineSize - bytes);
391   return 0;
392 }
393 
394 //---------------------------------------------------------
395 
read4Line(char * buffer,int x0,int x1,int shrink)396 int BmpReader::read4Line(char *buffer, int x0, int x1, int shrink) {
397   TPixel32 *pix = (TPixel32 *)buffer;
398   pix += 2 * x0;
399   if (x0 > 0) skipBytes(x0 / 2);
400   TPixel32 *endPix = pix + x1 - x0 + 1;
401 
402   int value;
403   while (pix + 2 <= endPix) {
404     value  = getc(m_chan);
405     pix[0] = m_cmap[value & 0xF];
406     pix[1] = m_cmap[(value >> 4) & 0xF];
407     // pix+=2*shrink;
408     pix++;
409     // if(pix+2<=endPix && shrink>1) skipBytes(shrink-1);
410   }
411   if (pix < endPix) {
412     value  = getc(m_chan);
413     pix[0] = m_cmap[value & 0xF];
414   }
415   if ((m_info.m_lx - x1) / 2 > 0) {
416     skipBytes((m_info.m_lx - x1) / 2);
417   }
418 
419   int bytes = (m_info.m_lx + 1) / 2;
420   if (m_lineSize - bytes) skipBytes(m_lineSize - bytes);
421   return 0;
422 }
423 
424 //---------------------------------------------------------
425 
read8Line(char * buffer,int x0,int x1,int shrink)426 int BmpReader::read8Line(char *buffer, int x0, int x1, int shrink) {
427   TPixel32 *pix = (TPixel32 *)buffer;
428 
429   if (x0 > 0) skipBytes(x0);
430   pix += x0;
431   TPixel32 *endPix = pix + x1 - x0 + 1;
432 
433   while (pix < endPix) {
434     int value = getc(m_chan);
435     *pix++    = m_cmap[value];
436     if (pix < endPix && shrink > 1) {
437       skipBytes(shrink - 1);
438       pix += (shrink - 1);
439     }
440   }
441 
442   if (m_info.m_lx - x1 - 1 > 0) {
443     skipBytes(m_info.m_lx - x1 - 1);
444   }
445 
446   if (m_lineSize - m_info.m_lx > 0) skipBytes(m_lineSize - m_info.m_lx);
447   return 0;
448 }
449 
450 //---------------------------------------------------------
451 
read8LineRle(char * buffer,int x0,int x1,int shrink)452 int BmpReader::read8LineRle(char *buffer, int x0, int x1, int shrink) {
453   TPixel32 *pix = (TPixel32 *)buffer;
454   if (x0 > 0) skipBytes(x0);
455   pix += x0;
456   TPixel32 *endPix = pix + (x1 - x0 + 1);
457 
458   while (pix < endPix) {
459     int i, count = getc(m_chan);
460     assert(count >= 0);
461     if (count == 0) {
462       int pixels = getc(m_chan);
463 
464       assert(pixels >= 0 && pixels != 2);
465       if (pixels < 3) return 0;
466       for (i = 0; i < pixels; i++) *pix++ = m_cmap[getc(m_chan)];
467       if ((1 + 1 + pixels) & 0x1) getc(m_chan);
468     } else {
469       int value = getc(m_chan);
470       assert(value >= 0);
471       for (i = 0; i < count; i++) *pix++ = m_cmap[value];
472     }
473     if (pix < endPix && shrink > 1) {
474       skipBytes(shrink - 1);
475       pix += (shrink - 1);
476     }
477   }
478 
479   if (m_info.m_lx - x1 - 1 > 0) {
480     skipBytes(m_info.m_lx - x1 - 1);
481   }
482 
483   if (m_lineSize - m_info.m_lx > 0) skipBytes(m_lineSize - m_info.m_lx);
484   int val = getc(m_chan);
485   assert(val == 0);  // end scanline or end bmp
486   val = getc(m_chan);
487   assert(val == 0 || val == 1);
488   return 0;
489 }
490 
491 //---------------------------------------------------------
492 
read16m555Line(char * buffer,int x0,int x1,int shrink)493 int BmpReader::read16m555Line(char *buffer, int x0, int x1, int shrink) {
494   TPixel32 *pix = (TPixel32 *)buffer;
495   if (x0 > 0) skipBytes(2 * x0);
496   pix += x0;
497   TPixel32 *endPix = pix + x1 - x0 + 1;
498 
499   while (pix < endPix) {
500     int v  = getshort(m_chan);
501     int r  = (v >> 10) & 0x1F;
502     int g  = (v >> 5) & 0x1F;
503     int b  = v & 0x1F;
504     pix->r = r << 3 | r >> 2;
505     pix->g = g << 3 | g >> 2;
506     pix->b = b << 3 | b >> 2;
507     pix->m = 255;
508     pix += shrink;
509     if (pix < endPix && shrink > 1) skipBytes(2 * (shrink - 1));
510   }
511   if (m_info.m_lx - x1 - 1 > 0) skipBytes(2 * (m_info.m_lx - x1 - 1));
512 
513   if (m_lineSize - 2 * m_info.m_lx > 0) skipBytes(m_lineSize - 2 * m_info.m_lx);
514   return 0;
515 }
516 
517 //---------------------------------------------------------
518 
read16m565Line(char * buffer,int x0,int x1,int shrink)519 int BmpReader::read16m565Line(char *buffer, int x0, int x1, int shrink) {
520   TPixel32 *pix = (TPixel32 *)buffer;
521   if (x0 > 0) skipBytes(2 * x0);
522   pix += x0;
523   TPixel32 *endPix = pix + x1 - x0 + 1;
524 
525   while (pix < endPix) {
526     int v  = getshort(m_chan);
527     int r  = (v >> 11) & 0x1F;
528     int g  = (v >> 5) & 0x3F;
529     int b  = v & 0x1F;
530     pix->r = r << 3 | r >> 2;
531     pix->g = g << 2 | g >> 4;
532     pix->b = b << 3 | b >> 2;
533     pix->m = 255;
534     pix += shrink;
535     if (pix < endPix && shrink > 1) skipBytes(2 * (shrink - 1));
536   }
537   if (m_info.m_lx - x1 - 1 > 0) skipBytes(2 * (m_info.m_lx - x1 - 1));
538 
539   if (m_lineSize - 2 * m_info.m_lx > 0) skipBytes(m_lineSize - 2 * m_info.m_lx);
540   return 0;
541 }
542 //---------------------------------------------------------
543 
read24Line(char * buffer,int x0,int x1,int shrink)544 int BmpReader::read24Line(char *buffer, int x0, int x1, int shrink) {
545   TPixel32 *pix = (TPixel32 *)buffer;
546   if (x0 > 0) skipBytes(3 * x0);
547   pix += x0;
548   TPixel32 *endPix = pix + x1 - x0 + 1;
549 
550   while (pix < endPix) {
551     pix->b = getc(m_chan);
552     pix->g = getc(m_chan);
553     pix->r = getc(m_chan);
554     pix->m = 255;
555     pix += shrink;
556     if (pix < endPix && shrink > 1) skipBytes(3 * (shrink - 1));
557   }
558   if (m_info.m_lx - x1 - 1 > 0) skipBytes(3 * (m_info.m_lx - x1 - 1));
559 
560   if (m_lineSize - 3 * m_info.m_lx > 0) skipBytes(m_lineSize - 3 * m_info.m_lx);
561   return 0;
562 }
563 
564 //---------------------------------------------------------
565 
read32Line(char * buffer,int x0,int x1,int shrink)566 int BmpReader::read32Line(char *buffer, int x0, int x1, int shrink) {
567   TPixel32 *pix = (TPixel32 *)buffer;
568   if (x0 > 0) skipBytes(4 * x0);
569   pix += x0;
570   TPixel32 *endPix = pix + x1 - x0 + 1;
571 
572   while (pix < endPix) {
573     pix->b = getc(m_chan);
574     pix->g = getc(m_chan);
575     pix->r = getc(m_chan);
576     pix->m = getc(m_chan);
577     pix += shrink;
578     if (pix < endPix && shrink > 1) skipBytes(4 * (shrink - 1));
579   }
580   if (m_info.m_lx - x1 - 1 > 0) skipBytes(4 * (m_info.m_lx - x1 - 1));
581   if (m_lineSize - 4 * m_info.m_lx > 0) skipBytes(m_lineSize - 4 * m_info.m_lx);
582   return 0;
583 }
584 
585 //=========================================================
586 
587 class BmpWriter final : public Tiio::Writer {
588   FILE *m_chan;
589   int m_bitPerPixel;
590   int m_compression;
591 
592 public:
593   BmpWriter();
594   ~BmpWriter();
595 
596   void open(FILE *file, const TImageInfo &info) override;
597 
flush()598   void flush() override { fflush(m_chan); }
599 
600   void writeLine(char *buffer) override;
601 
602   // for now opentoonz does not support bmp format with alpha channel
writeAlphaSupported() const603   bool writeAlphaSupported() const override { return false; }
604 };
605 
606 //---------------------------------------------------------
607 
BmpWriter()608 BmpWriter::BmpWriter() : m_chan(0) {}
609 
610 //---------------------------------------------------------
611 
~BmpWriter()612 BmpWriter::~BmpWriter() { delete m_properties; }
613 
614 //---------------------------------------------------------
615 
open(FILE * file,const TImageInfo & info)616 void BmpWriter::open(FILE *file, const TImageInfo &info) {
617   m_chan = file;
618 
619   m_info = info;
620   int lx = m_info.m_lx;
621   int ly = m_info.m_ly;
622 
623   if (!m_properties) m_properties = new Tiio::BmpWriterProperties();
624 
625   TEnumProperty *p =
626       (TEnumProperty *)(m_properties->getProperty("Bits Per Pixel"));
627   assert(p);
628   std::string str = ::to_string(p->getValue());
629   m_bitPerPixel   = atoi(str.c_str());
630 
631   int cmapSize                  = 0;
632   std::vector<TPixel> *colormap = 0;
633 
634   if (m_bitPerPixel == 8) {
635     TPointerProperty *colormapProp =
636         (TPointerProperty *)(m_properties->getProperty("Colormap"));
637     if (colormapProp) {
638       colormap = (std::vector<TPixel> *)colormapProp->getValue();
639       cmapSize = colormap->size();
640     } else
641       cmapSize = 256;
642   }
643 
644   assert(m_bitPerPixel == 8 || m_bitPerPixel == 24);
645 
646   int bytePerLine =
647       ((lx * m_bitPerPixel + 31) / 32) * (m_bitPerPixel == 8 ? 1 : 4);
648 
649   int fileSize = 14                  // file header
650                  + 40                // info header
651                  + cmapSize * 4      // colormap size
652                  + bytePerLine * ly  // image size
653       ;
654   int offset      = 14 + 40 + cmapSize * 4;
655   int compression = 0;
656   int imageSize   = bytePerLine * ly;
657 
658   putc('B', m_chan);
659   putc('M', m_chan);  // BMP file magic number
660 
661   putint(m_chan, fileSize);
662   putshort(m_chan, 0);  // reserved1
663   putshort(m_chan, 0);  // reserved2
664 
665   putint(m_chan, offset);  // offset from BOfile to BObitmap
666 
667   putint(m_chan, 40);               // size of bitmap info header
668   putint(m_chan, m_info.m_lx);      // width
669   putint(m_chan, m_info.m_ly);      // height
670   putshort(m_chan, 1);              // must be '1'
671   putshort(m_chan, m_bitPerPixel);  // 1,4,8, or 24
672   putint(m_chan, compression);      // BMP_BI_RGB, BMP_BI_RLE8 or BMP_BI_RLE4
673   putint(m_chan, imageSize);        // size of raw image data
674   putint(m_chan, 0);                // dpi * 39" per meter
675   putint(m_chan, 0);                // dpi * 39" per meter
676   putint(m_chan, cmapSize);         // colors used in cmap
677   putint(m_chan, 0);                // same as above
678 
679   if (colormap)
680     for (int i = 0; i < (int)colormap->size(); i++) {
681       putc((*colormap)[i].b, m_chan);
682       putc((*colormap)[i].g, m_chan);
683       putc((*colormap)[i].r, m_chan);
684       putc(0, m_chan);
685     }
686   else
687     for (int i = 0; i < cmapSize; i++)  // palette!!
688     {
689       putc(i, m_chan);
690       putc(i, m_chan);
691       putc(i, m_chan);
692       putc(0, m_chan);
693     }
694 }
695 
696 //---------------------------------------------------------
697 
writeLine(char * buffer)698 void BmpWriter::writeLine(char *buffer) {
699   int lx = m_info.m_lx;
700 
701   int j;
702 
703   switch (m_bitPerPixel) {
704   case 24: {
705     TPixel32 *pix = (TPixel32 *)buffer;
706     for (j = 0; j < lx; j++) {
707       putc(pix->b, m_chan);
708       putc(pix->g, m_chan);
709       putc(pix->r, m_chan);
710       ++pix;
711     }
712     int bytes = lx * 3;
713     while (bytes & 3) {
714       putc(0, m_chan);
715       bytes++;
716     }
717     break;
718   }
719   case 8: {
720     TPixelGR8 *pix = (TPixelGR8 *)buffer;
721     for (j = 0; j < lx; j++) {
722       putc(pix->value, m_chan);
723       ++pix;
724     }
725     while (lx & 3) {
726       putc(0, m_chan);
727       lx++;
728     }
729     break;
730   }
731   default:
732     assert(false);
733   }
734   ftell(m_chan);
735 }
736 
737 //=========================================================
738 
makeBmpReader()739 Tiio::Reader *Tiio::makeBmpReader() { return new BmpReader(); }
740 
741 //=========================================================
742 
makeBmpWriter()743 Tiio::Writer *Tiio::makeBmpWriter() { return new BmpWriter(); }
744 
745 //=========================================================
746 
BmpWriterProperties()747 Tiio::BmpWriterProperties::BmpWriterProperties()
748     : m_pixelSize("Bits Per Pixel") {
749   m_pixelSize.addValue(L"24 bits");
750   m_pixelSize.addValue(L"8 bits (Greyscale)");
751   bind(m_pixelSize);
752 }
753 
updateTranslation()754 void Tiio::BmpWriterProperties::updateTranslation() {
755   m_pixelSize.setQStringName(tr("Bits Per Pixel"));
756   m_pixelSize.setItemUIName(L"24 bits", tr("24 bits"));
757   m_pixelSize.setItemUIName(L"8 bits (Greyscale)", tr("8 bits (Greyscale)"));
758 }
759