URI:
       treadbmp.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       treadbmp.c (13692B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 #include <draw.h>
            5 #include "imagefile.h"
            6 #include "bmp.h"
            7 
            8 /*
            9  MS-BMP file reader
           10  (c) 2003, I.P.Keller
           11 
           12  aims to decode *all* valid bitmap formats, although some of the
           13  flavours couldn't be verified due to lack of suitable test-files.
           14  the following flavours are supported:
           15 
           16         Bit/Pix        Orientation        Compression        Tested?
           17           1        top->bottom        n/a                yes
           18           1        bottom->top        n/a                yes
           19           4        top->bottom        no                yes
           20           4        bottom->top        no                yes
           21           4        top->bottom        RLE4                yes, but not with displacement
           22           8        top->bottom        no                yes
           23           8        bottom->top        no                yes
           24           8        top->bottom        RLE8                yes, but not with displacement
           25          16        top->bottom        no                no
           26          16        bottom->top        no                no
           27          16        top->bottom        BITMASK                no
           28          16        bottom->top        BITMASK                no
           29          24        top->bottom        n/a                yes
           30          24        bottom->top        n/a                yes
           31          32        top->bottom        no                no
           32          32        bottom->top        no                no
           33          32        top->bottom        BITMASK                no
           34          32        bottom->top        BITMASK                no
           35 
           36  OS/2 1.x bmp files are recognised as well, but testing was very limited.
           37 
           38  verifying was done with a number of test files, generated by
           39  different tools. nevertheless, the tests were in no way exhaustive
           40  enough to guarantee bug-free decoding. caveat emptor!
           41 */
           42 
           43 static short
           44 r16(Biobuf*b)
           45 {
           46         short s;
           47 
           48         s = Bgetc(b);
           49         s |= ((short)Bgetc(b)) << 8;
           50         return s;
           51 }
           52 
           53 
           54 static long
           55 r32(Biobuf*b)
           56 {
           57         long l;
           58 
           59         l = Bgetc(b);
           60         l |= ((long)Bgetc(b)) << 8;
           61         l |= ((long)Bgetc(b)) << 16;
           62         l |= ((long)Bgetc(b)) << 24;
           63         return l;
           64 }
           65 
           66 
           67 /* get highest bit set */
           68 static int
           69 msb(ulong x)
           70 {
           71         int i;
           72         for(i = 32; i; i--, x <<= 1)
           73                 if(x & 0x80000000L)
           74                         return i;
           75         return 0;
           76 }
           77 
           78 /* Load a 1-Bit encoded BMP file (uncompressed) */
           79 static int
           80 load_1T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
           81 {
           82         long ix, iy, i = 0, step_up = 0, padded_width = ((width + 31) / 32) * 32;
           83         int val = 0, n;
           84 
           85         if(height > 0) {        /* bottom-up */
           86                 i = (height - 1) * width;
           87                 step_up = -2 * width;
           88         } else
           89                 height = -height;
           90 
           91         for(iy = height; iy; iy--, i += step_up)
           92                 for(ix = 0, n = 0; ix < padded_width; ix++, n--) {
           93                         if(!n) {
           94                                 val = Bgetc(b);
           95                                 n = 8;
           96                         }
           97                         if(ix < width) {
           98                                 buf[i] = clut[val & 0x80 ? 1 : 0];
           99                                 i++;
          100                         }
          101                         val <<= 1;
          102                 }
          103         return 0;
          104 }
          105 
          106 /* Load a 4-Bit encoded BMP file (uncompressed) */
          107 static int
          108 load_4T(Biobuf* b, long width, long height, Rgb* buf, Rgb* clut)
          109 {
          110         long ix, iy, i = 0, step_up = 0, skip = (4 - (((width % 8) + 1) / 2)) & 3;
          111         uint valH, valL;
          112 
          113         if(height > 0) {        /* bottom-up */
          114                 i = (height - 1) * width;
          115                 step_up = -2 * width;
          116         } else
          117                 height = -height;
          118 
          119         for(iy = height; iy; iy--, i += step_up) {
          120                 for(ix = 0; ix < width; ) {
          121                         valH = valL = Bgetc(b) & 0xff;
          122                         valH >>= 4;
          123 
          124                         buf[i] = clut[valH];
          125                         i++; ix++;
          126 
          127                         if(ix < width) {
          128                                 valL &= 0xf;
          129                                 buf[i] = clut[valL];
          130                                 i++; ix++;
          131                         }
          132                 }
          133                 Bseek(b, skip, 1);
          134         }
          135         return 0;
          136 }
          137 
          138 /* Load a 4-Bit encoded BMP file (RLE4-compressed) */
          139 static int
          140 load_4C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
          141 {
          142         long ix, iy = height -1;
          143         uint val, valS, skip;
          144         Rgb* p;
          145 
          146         while(iy >= 0) {
          147                 ix = 0;
          148                 while(ix < width) {
          149                         val = Bgetc(b);
          150 
          151                         if(0 != val) {
          152                                 valS = (uint)Bgetc(b);
          153                                 p = &buf[ix + iy * width];
          154                                 while(val--) {
          155                                         *p = clut[0xf & (valS >> 4)];
          156                                         p++;
          157                                         ix++;
          158                                         if(0 < val) {
          159                                                 *p = clut[0xf & valS];
          160                                                 p++;
          161                                                 ix++;
          162                                                 val--;
          163                                         }
          164                                 }
          165                         } else {
          166                                 /* Special modes... */
          167                                 val = Bgetc(b);
          168                                 switch(val) {
          169                                         case 0:        /* End-Of-Line detected */
          170                                                 ix = width;
          171                                                 iy--;
          172                                                 break;
          173                                         case 1:        /* End-Of-Picture detected -->> abort */
          174                                                 ix = width;
          175                                                 iy = -1;
          176                                                 break;
          177                                         case 2:        /* Position change detected */
          178                                                 val = Bgetc(b);
          179                                                 ix += val;
          180                                                 val = Bgetc(b);
          181                                                 iy -= val;
          182                                                 break;
          183 
          184                                         default:/* Transparent data sequence detected */
          185                                                 p = &buf[ix + iy * width];
          186                                                 if((1 == (val & 3)) || (2 == (val & 3)))
          187                                                         skip = 1;
          188                                                 else
          189                                                         skip = 0;
          190 
          191                                                 while(val--) {
          192                                                         valS = (uint)Bgetc(b);
          193                                                         *p = clut[0xf & (valS >> 4)];
          194                                                         p++;
          195                                                         ix++;
          196                                                         if(0 < val) {
          197                                                                 *p = clut[0xf & valS];
          198                                                                 p++;
          199                                                                 ix++;
          200                                                                 val--;
          201                                                         }
          202                                                 }
          203                                                 if(skip)
          204                                                         Bgetc(b);
          205                                                 break;
          206                                 }
          207                         }
          208                 }
          209         }
          210         return 0;
          211 }
          212 
          213 /* Load a 8-Bit encoded BMP file (uncompressed) */
          214 static int
          215 load_8T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
          216 {
          217         long ix, iy, i = 0, step_up = 0, skip = (4 - (width % 4)) & 3;
          218 
          219         if(height > 0) {        /* bottom-up */
          220                 i = (height - 1) * width;
          221                 step_up = -2 * width;
          222         } else
          223                 height = -height;
          224 
          225         for(iy = height; iy; iy--, i += step_up) {
          226                 for(ix = 0; ix < width; ix++, i++)
          227                         buf[i] = clut[Bgetc(b) & 0xff];
          228                 Bseek(b, skip, 1);
          229         }
          230         return 0;
          231 }
          232 
          233 /* Load a 8-Bit encoded BMP file (RLE8-compressed) */
          234 static int
          235 load_8C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
          236 {
          237         long ix, iy = height -1;
          238         int val, valS, skip;
          239         Rgb* p;
          240 
          241         while(iy >= 0) {
          242                 ix = 0;
          243                 while(ix < width) {
          244                         val = Bgetc(b);
          245 
          246                         if(0 != val) {
          247                                 valS = Bgetc(b);
          248                                 p = &buf[ix + iy * width];
          249                                 while(val--) {
          250                                         *p = clut[valS];
          251                                         p++;
          252                                         ix++;
          253                                 }
          254                         } else {
          255                                 /* Special modes... */
          256                                 val = Bgetc(b);
          257                                 switch(val) {
          258                                         case 0: /* End-Of-Line detected */
          259                                                 ix = width;
          260                                                 iy--;
          261                                                 break;
          262                                         case 1: /* End-Of-Picture detected */
          263                                                 ix = width;
          264                                                 iy = -1;
          265                                                 break;
          266                                         case 2: /* Position change detected */
          267                                                 val = Bgetc(b);
          268                                                 ix += val;
          269                                                 val = Bgetc(b);
          270                                                 iy -= val;
          271                                                 break;
          272                                         default: /* Transparent (not compressed) sequence detected */
          273                                                 p = &buf[ix + iy * width];
          274                                                 if(val & 1)
          275                                                         skip = 1;
          276                                                 else
          277                                                         skip = 0;
          278 
          279                                                 while(val--) {
          280                                                         valS = Bgetc(b);
          281                                                         *p = clut[valS];
          282                                                         p++;
          283                                                         ix++;
          284                                                 }
          285                                                 if(skip)
          286                                                         /* Align data stream */
          287                                                         Bgetc(b);
          288                                                 break;
          289                                 }
          290                         }
          291                 }
          292         }
          293         return 0;
          294 }
          295 
          296 /* Load a 16-Bit encoded BMP file (uncompressed) */
          297 static int
          298 load_16(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
          299 {
          300         uchar c[2];
          301         long ix, iy, i = 0, step_up = 0;
          302 
          303         if(height > 0) {        /* bottom-up */
          304                 i = (height - 1) * width;
          305                 step_up = -2 * width;
          306         } else
          307                 height = -height;
          308 
          309         if(clut) {
          310                 unsigned mask_blue =  (unsigned)clut[0].blue +
          311                                      ((unsigned)clut[0].green << 8);
          312                 unsigned mask_green =  (unsigned)clut[1].blue +
          313                                       ((unsigned)clut[1].green << 8);
          314                 unsigned mask_red =  (unsigned)clut[2].blue +
          315                                     ((unsigned)clut[2].green << 8);
          316                 int shft_blue = msb((ulong)mask_blue) - 8;
          317                 int shft_green = msb((ulong)mask_green) - 8;
          318                 int shft_red = msb((ulong)mask_red) - 8;
          319 
          320                 for(iy = height; iy; iy--, i += step_up)
          321                         for(ix = 0; ix < width; ix++, i++) {
          322                                 unsigned val;
          323                                 Bread(b, c, sizeof(c));
          324                                 val = (unsigned)c[0] + ((unsigned)c[1] << 8);
          325 
          326                                 buf[i].alpha = 0;
          327                                 if(shft_blue >= 0)
          328                                         buf[i].blue = (uchar)((val & mask_blue) >> shft_blue);
          329                                 else
          330                                         buf[i].blue = (uchar)((val & mask_blue) << -shft_blue);
          331                                 if(shft_green >= 0)
          332                                         buf[i].green = (uchar)((val & mask_green) >> shft_green);
          333                                 else
          334                                         buf[i].green = (uchar)((val & mask_green) << -shft_green);
          335                                 if(shft_red >= 0)
          336                                         buf[i].red = (uchar)((val & mask_red) >> shft_red);
          337                                 else
          338                                         buf[i].red = (uchar)((val & mask_red) << -shft_red);
          339                         }
          340         } else
          341                 for(iy = height; iy; iy--, i += step_up)
          342                         for(ix = 0; ix < width; ix++, i++) {
          343                                 Bread(b, c, sizeof(c));
          344                                 buf[i].blue = (uchar)((c[0] << 3) & 0xf8);
          345                                 buf[i].green = (uchar)(((((unsigned)c[1] << 6) +
          346                                                         (((unsigned)c[0]) >> 2))) & 0xf8);
          347                                 buf[i].red = (uchar)((c[1] << 1) & 0xf8);
          348                         }
          349         return 0;
          350 }
          351 
          352 /* Load a 24-Bit encoded BMP file (uncompressed) */
          353 static int
          354 load_24T(Biobuf* b, long width, long height, Rgb* buf)
          355 {
          356         long ix, iy, i = 0, step_up = 0, skip = (4 - ((width * 3) % 4)) & 3;
          357 
          358         if(height > 0) {        /* bottom-up */
          359                 i = (height - 1) * width;
          360                 step_up = -2 * width;
          361         } else
          362                 height = -height;
          363 
          364         for(iy = height; iy; iy--, i += step_up) {
          365                 for(ix = 0; ix < width; ix++, i++) {
          366                         buf[i].alpha = 0;
          367                         buf[i].blue = Bgetc(b);
          368                         buf[i].green = Bgetc(b);
          369                         buf[i].red = Bgetc(b);
          370                 }
          371                 Bseek(b, skip, 1);
          372         }
          373         return 0;
          374 }
          375 
          376 /* Load a 32-Bit encoded BMP file (uncompressed) */
          377 static int
          378 load_32(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
          379 {
          380         uchar c[4];
          381         long ix, iy, i = 0, step_up = 0;
          382 
          383         if(height > 0) {        /* bottom-up */
          384                 i = (height - 1) * width;
          385                 step_up = -2 * width;
          386         } else
          387                 height = -height;
          388 
          389         if(clut) {
          390                 ulong mask_blue =  (ulong)clut[0].blue +
          391                                           ((ulong)clut[0].green << 8) +
          392                                           ((ulong)clut[0].red << 16) +
          393                                           ((ulong)clut[0].alpha << 24);
          394                 ulong mask_green =  (ulong)clut[1].blue +
          395                                            ((ulong)clut[1].green << 8) +
          396                                            ((ulong)clut[1].red << 16) +
          397                                            ((ulong)clut[1].alpha << 24);
          398                 ulong mask_red =  (ulong)clut[2].blue +
          399                                          ((ulong)clut[2].green << 8) +
          400                                          ((ulong)clut[2].red << 16) +
          401                                          ((ulong)clut[2].alpha << 24);
          402                 int shft_blue = msb(mask_blue) - 8;
          403                 int shft_green = msb(mask_green) - 8;
          404                 int shft_red = msb(mask_red) - 8;
          405 
          406                 for(iy = height; iy; iy--, i += step_up)
          407                         for(ix = 0; ix < width; ix++, i++) {
          408                                 ulong val;
          409                                 Bread(b, c, sizeof(c));
          410                                 val =  (ulong)c[0] + ((ulong)c[1] << 8) +
          411                                       ((ulong)c[2] << 16) + ((ulong)c[1] << 24);
          412 
          413                                 buf[i].alpha = 0;
          414                                 if(shft_blue >= 0)
          415                                         buf[i].blue = (uchar)((val & mask_blue) >> shft_blue);
          416                                 else
          417                                         buf[i].blue = (uchar)((val & mask_blue) << -shft_blue);
          418                                 if(shft_green >= 0)
          419                                         buf[i].green = (uchar)((val & mask_green) >> shft_green);
          420                                 else
          421                                         buf[i].green = (uchar)((val & mask_green) << -shft_green);
          422                                 if(shft_red >= 0)
          423                                         buf[i].red = (uchar)((val & mask_red) >> shft_red);
          424                                 else
          425                                         buf[i].red = (uchar)((val & mask_red) << -shft_red);
          426                         }
          427         } else
          428                 for(iy = height; iy; iy--, i += step_up)
          429                         for(ix = 0; ix < width; ix++, i++) {
          430                                 Bread(b, c, nelem(c));
          431                                 buf[i].blue = c[0];
          432                                 buf[i].green = c[1];
          433                                 buf[i].red = c[2];
          434                         }
          435         return 0;
          436 }
          437 
          438 
          439 static Rgb*
          440 ReadBMP(Biobuf *b, int *width, int *height)
          441 {
          442         int colours, num_coltab = 0;
          443         Filehdr bmfh;
          444         Infohdr bmih;
          445         Rgb clut[256];
          446         Rgb* buf;
          447 
          448         bmfh.type = r16(b);
          449         if(bmfh.type != 0x4d42)         /* signature must be 'BM' */
          450                 sysfatal("bad magic number, not a BMP file");
          451 
          452         bmfh.size = r32(b);
          453         bmfh.reserved1 = r16(b);
          454         bmfh.reserved2 = r16(b);
          455         bmfh.offbits = r32(b);
          456 
          457         memset(&bmih, 0, sizeof(bmih));
          458         bmih.size = r32(b);
          459 
          460         if(bmih.size == 0x0c) {                        /* OS/2 1.x version */
          461                 bmih.width = r16(b);
          462                 bmih.height = r16(b);
          463                 bmih.planes = r16(b);
          464                 bmih.bpp = r16(b);
          465                 bmih.compression = BMP_RGB;
          466         } else {                                /* Windows */
          467                 bmih.width = r32(b);
          468                 bmih.height = r32(b);
          469                 bmih.planes = r16(b);
          470                 bmih.bpp = r16(b);
          471                 bmih.compression = r32(b);
          472                 bmih.imagesize = r32(b);
          473                 bmih.hres = r32(b);
          474                 bmih.vres = r32(b);
          475                 bmih.colours = r32(b);
          476                 bmih.impcolours = r32(b);
          477         }
          478 
          479         if(bmih.bpp < 16) {
          480                 /* load colour table */
          481                 if(bmih.impcolours)
          482                         num_coltab = (int)bmih.impcolours;
          483                 else
          484                         num_coltab = 1 << bmih.bpp;
          485         } else if(bmih.compression == BMP_BITFIELDS &&
          486                   (bmih.bpp == 16 || bmih.bpp == 32))
          487                 /* load bitmasks */
          488                 num_coltab = 3;
          489 
          490         if(num_coltab) {
          491                 int i;
          492                 Bseek(b, bmih.size + sizeof(Infohdr), 0);
          493 
          494                 for(i = 0; i < num_coltab; i++) {
          495                         clut[i].blue  = (uchar)Bgetc(b);
          496                         clut[i].green = (uchar)Bgetc(b);
          497                         clut[i].red   = (uchar)Bgetc(b);
          498                         clut[i].alpha = (uchar)Bgetc(b);
          499                 }
          500         }
          501 
          502         *width = bmih.width;
          503         *height = bmih.height;
          504         colours = bmih.bpp;
          505 
          506         Bseek(b, bmfh.offbits, 0);
          507 
          508         if ((buf = calloc(sizeof(Rgb), *width * abs(*height))) == nil)
          509                 sysfatal("no memory");
          510 
          511         switch(colours) {
          512                 case 1:
          513                         load_1T(b, *width, *height, buf, clut);
          514                         break;
          515                 case 4:
          516                         if(bmih.compression == BMP_RLE4)
          517                                 load_4C(b, *width, *height, buf, clut);
          518                         else
          519                                 load_4T(b, *width, *height, buf, clut);
          520                         break;
          521                 case 8:
          522                         if(bmih.compression == BMP_RLE8)
          523                                 load_8C(b, *width, *height, buf, clut);
          524                         else
          525                                 load_8T(b, *width, *height, buf, clut);
          526                         break;
          527                 case 16:
          528                         load_16(b, *width, *height, buf,
          529                                 bmih.compression == BMP_BITFIELDS ? clut : nil);
          530                         break;
          531                 case 24:
          532                         load_24T(b, *width, *height, buf);
          533                         break;
          534                 case 32:
          535                         load_32(b, *width, *height, buf,
          536                                 bmih.compression == BMP_BITFIELDS ? clut : nil);
          537                         break;
          538         }
          539         return buf;
          540 }
          541 
          542 Rawimage**
          543 Breadbmp(Biobuf *bp, int colourspace)
          544 {
          545         Rawimage *a, **array;
          546         int c, width, height;
          547         uchar *r, *g, *b;
          548         Rgb *s, *e;
          549         Rgb *bmp;
          550         char ebuf[128];
          551 
          552         a = nil;
          553         bmp = nil;
          554         array = nil;
          555         USED(a);
          556         USED(bmp);
          557         if (colourspace != CRGB) {
          558                 errstr(ebuf, sizeof ebuf);        /* throw it away */
          559                 werrstr("ReadRGB: unknown colour space %d", colourspace);
          560                 return nil;
          561         }
          562 
          563         if ((bmp = ReadBMP(bp, &width, &height)) == nil)
          564                 return nil;
          565 
          566         if ((a = calloc(sizeof(Rawimage), 1)) == nil)
          567                 goto Error;
          568 
          569         for (c = 0; c  < 3; c++)
          570                 if ((a->chans[c] = calloc(width, height)) == nil)
          571                         goto Error;
          572 
          573         if ((array = calloc(sizeof(Rawimage *), 2)) == nil)
          574                 goto Error;
          575         array[0] = a;
          576         array[1] = nil;
          577 
          578         a->nchans = 3;
          579         a->chandesc = CRGB;
          580         a->chanlen = width * height;
          581         a->r = Rect(0, 0, width, height);
          582 
          583         s = bmp;
          584         e = s + width * height;
          585         r = a->chans[0];
          586         g = a->chans[1];
          587         b = a->chans[2];
          588 
          589         do {
          590                 *r++ = s->red;
          591                 *g++ = s->green;
          592                 *b++ = s->blue;
          593         }while(++s < e);
          594 
          595         free(bmp);
          596         return array;
          597 
          598 Error:
          599         if (a)
          600                 for (c = 0; c < 3; c++)
          601                         if (a->chans[c])
          602                                 free(a->chans[c]);
          603         if (a)
          604                 free(a);
          605         if (array)
          606                 free(array);
          607         if (bmp)
          608                 free(bmp);
          609         return nil;
          610 
          611 }
          612 
          613 Rawimage**
          614 readbmp(int fd, int colorspace)
          615 {
          616         Rawimage * *a;
          617         Biobuf b;
          618 
          619         if (Binit(&b, fd, OREAD) < 0)
          620                 return nil;
          621         a = Breadbmp(&b, colorspace);
          622         Bterm(&b);
          623         return a;
          624 }