diff options
author | rsc <devnull@localhost> | 2004-04-21 22:19:33 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2004-04-21 22:19:33 +0000 |
commit | 28994509cc11ac6a5443054dfae1fedfb69039bc (patch) | |
tree | 9d5adcd11af2708db0ecc246e008c308ca0f97d4 /src/cmd/jpg/readbmp.c | |
parent | a01e58366c54804f15f84d6e21d13f2e4080977a (diff) | |
download | plan9port-28994509cc11ac6a5443054dfae1fedfb69039bc.tar.gz plan9port-28994509cc11ac6a5443054dfae1fedfb69039bc.tar.bz2 plan9port-28994509cc11ac6a5443054dfae1fedfb69039bc.zip |
Why not?
Diffstat (limited to 'src/cmd/jpg/readbmp.c')
-rw-r--r-- | src/cmd/jpg/readbmp.c | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/src/cmd/jpg/readbmp.c b/src/cmd/jpg/readbmp.c new file mode 100644 index 00000000..154cb48d --- /dev/null +++ b/src/cmd/jpg/readbmp.c @@ -0,0 +1,626 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include "imagefile.h" +#include "bmp.h" + +/* + MS-BMP file reader + (c) 2003, I.P.Keller + + aims to decode *all* valid bitmap formats, although some of the + flavours couldn't be verified due to lack of suitable test-files. + the following flavours are supported: + + Bit/Pix Orientation Compression Tested? + 1 top->bottom n/a yes + 1 bottom->top n/a yes + 4 top->bottom no yes + 4 bottom->top no yes + 4 top->bottom RLE4 yes, but not with displacement + 8 top->bottom no yes + 8 bottom->top no yes + 8 top->bottom RLE8 yes, but not with displacement + 16 top->bottom no no + 16 bottom->top no no + 16 top->bottom BITMASK no + 16 bottom->top BITMASK no + 24 top->bottom n/a yes + 24 bottom->top n/a yes + 32 top->bottom no no + 32 bottom->top no no + 32 top->bottom BITMASK no + 32 bottom->top BITMASK no + + OS/2 1.x bmp files are recognised as well, but testing was very limited. + + verifying was done with a number of test files, generated by + different tools. nevertheless, the tests were in no way exhaustive + enough to guarantee bug-free decoding. caveat emptor! +*/ + +static short +r16(Biobuf*b) +{ + short s; + + s = Bgetc(b); + s |= ((short)Bgetc(b)) << 8; + return s; +} + + +static long +r32(Biobuf*b) +{ + long l; + + l = Bgetc(b); + l |= ((long)Bgetc(b)) << 8; + l |= ((long)Bgetc(b)) << 16; + l |= ((long)Bgetc(b)) << 24; + return l; +} + + +/* get highest bit set */ +static int +msb(ulong x) +{ + int i; + for(i = 32; i; i--, x <<= 1) + if(x & 0x80000000L) + return i; + return 0; +} + +/* Load a 1-Bit encoded BMP file (uncompressed) */ +static int +load_1T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + long ix, iy, i = 0, step_up = 0, padded_width = ((width + 31) / 32) * 32; + int val = 0, n; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + for(iy = height; iy; iy--, i += step_up) + for(ix = 0, n = 0; ix < padded_width; ix++, n--) { + if(!n) { + val = Bgetc(b); + n = 8; + } + if(ix < width) { + buf[i] = clut[val & 0x80 ? 1 : 0]; + i++; + } + val <<= 1; + } + return 0; +} + +/* Load a 4-Bit encoded BMP file (uncompressed) */ +static int +load_4T(Biobuf* b, long width, long height, Rgb* buf, Rgb* clut) +{ + long ix, iy, i = 0, step_up = 0, skip = (4 - (((width % 8) + 1) / 2)) & 3; + uint valH, valL; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + for(iy = height; iy; iy--, i += step_up) { + for(ix = 0; ix < width; ) { + valH = valL = Bgetc(b) & 0xff; + valH >>= 4; + + buf[i] = clut[valH]; + i++; ix++; + + if(ix < width) { + valL &= 0xf; + buf[i] = clut[valL]; + i++; ix++; + } + } + Bseek(b, skip, 1); + } + return 0; +} + +/* Load a 4-Bit encoded BMP file (RLE4-compressed) */ +static int +load_4C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + long ix, iy = height -1; + uint val, valS, skip; + Rgb* p; + + while(iy >= 0) { + ix = 0; + while(ix < width) { + val = Bgetc(b); + + if(0 != val) { + valS = (uint)Bgetc(b); + p = &buf[ix + iy * width]; + while(val--) { + *p = clut[0xf & (valS >> 4)]; + p++; + ix++; + if(0 < val) { + *p = clut[0xf & valS]; + p++; + ix++; + val--; + } + } + } else { + /* Special modes... */ + val = Bgetc(b); + switch(val) { + case 0: /* End-Of-Line detected */ + ix = width; + iy--; + break; + case 1: /* End-Of-Picture detected -->> abort */ + ix = width; + iy = -1; + break; + case 2: /* Position change detected */ + val = Bgetc(b); + ix += val; + val = Bgetc(b); + iy -= val; + break; + + default:/* Transparent data sequence detected */ + p = &buf[ix + iy * width]; + if((1 == (val & 3)) || (2 == (val & 3))) + skip = 1; + else + skip = 0; + + while(val--) { + valS = (uint)Bgetc(b); + *p = clut[0xf & (valS >> 4)]; + p++; + ix++; + if(0 < val) { + *p = clut[0xf & valS]; + p++; + ix++; + val--; + } + } + if(skip) + Bgetc(b); + break; + } + } + } + } + return 0; +} + +/* Load a 8-Bit encoded BMP file (uncompressed) */ +static int +load_8T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + long ix, iy, i = 0, step_up = 0, skip = (4 - (width % 4)) & 3; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + for(iy = height; iy; iy--, i += step_up) { + for(ix = 0; ix < width; ix++, i++) + buf[i] = clut[Bgetc(b) & 0xff]; + Bseek(b, skip, 1); + } + return 0; +} + +/* Load a 8-Bit encoded BMP file (RLE8-compressed) */ +static int +load_8C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + long ix, iy = height -1; + int val, valS, skip; + Rgb* p; + + while(iy >= 0) { + ix = 0; + while(ix < width) { + val = Bgetc(b); + + if(0 != val) { + valS = Bgetc(b); + p = &buf[ix + iy * width]; + while(val--) { + *p = clut[valS]; + p++; + ix++; + } + } else { + /* Special modes... */ + val = Bgetc(b); + switch(val) { + case 0: /* End-Of-Line detected */ + ix = width; + iy--; + break; + case 1: /* End-Of-Picture detected */ + ix = width; + iy = -1; + break; + case 2: /* Position change detected */ + val = Bgetc(b); + ix += val; + val = Bgetc(b); + iy -= val; + break; + default: /* Transparent (not compressed) sequence detected */ + p = &buf[ix + iy * width]; + if(val & 1) + skip = 1; + else + skip = 0; + + while(val--) { + valS = Bgetc(b); + *p = clut[valS]; + p++; + ix++; + } + if(skip) + /* Align data stream */ + Bgetc(b); + break; + } + } + } + } + return 0; +} + +/* Load a 16-Bit encoded BMP file (uncompressed) */ +static int +load_16(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + uchar c[2]; + long ix, iy, i = 0, step_up = 0; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + if(clut) { + unsigned mask_blue = (unsigned)clut[0].blue + + ((unsigned)clut[0].green << 8); + unsigned mask_green = (unsigned)clut[1].blue + + ((unsigned)clut[1].green << 8); + unsigned mask_red = (unsigned)clut[2].blue + + ((unsigned)clut[2].green << 8); + int shft_blue = msb((ulong)mask_blue) - 8; + int shft_green = msb((ulong)mask_green) - 8; + int shft_red = msb((ulong)mask_red) - 8; + + for(iy = height; iy; iy--, i += step_up) + for(ix = 0; ix < width; ix++, i++) { + unsigned val; + Bread(b, c, sizeof(c)); + val = (unsigned)c[0] + ((unsigned)c[1] << 8); + + buf[i].alpha = 0; + if(shft_blue >= 0) + buf[i].blue = (uchar)((val & mask_blue) >> shft_blue); + else + buf[i].blue = (uchar)((val & mask_blue) << -shft_blue); + if(shft_green >= 0) + buf[i].green = (uchar)((val & mask_green) >> shft_green); + else + buf[i].green = (uchar)((val & mask_green) << -shft_green); + if(shft_red >= 0) + buf[i].red = (uchar)((val & mask_red) >> shft_red); + else + buf[i].red = (uchar)((val & mask_red) << -shft_red); + } + } else + for(iy = height; iy; iy--, i += step_up) + for(ix = 0; ix < width; ix++, i++) { + Bread(b, c, sizeof(c)); + buf[i].blue = (uchar)((c[0] << 3) & 0xf8); + buf[i].green = (uchar)(((((unsigned)c[1] << 6) + + (((unsigned)c[0]) >> 2))) & 0xf8); + buf[i].red = (uchar)((c[1] << 1) & 0xf8); + } + return 0; +} + +/* Load a 24-Bit encoded BMP file (uncompressed) */ +static int +load_24T(Biobuf* b, long width, long height, Rgb* buf) +{ + long ix, iy, i = 0, step_up = 0, skip = (4 - ((width * 3) % 4)) & 3; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + for(iy = height; iy; iy--, i += step_up) { + for(ix = 0; ix < width; ix++, i++) { + buf[i].alpha = 0; + buf[i].blue = Bgetc(b); + buf[i].green = Bgetc(b); + buf[i].red = Bgetc(b); + } + Bseek(b, skip, 1); + } + return 0; +} + +/* Load a 32-Bit encoded BMP file (uncompressed) */ +static int +load_32(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + uchar c[4]; + long ix, iy, i = 0, step_up = 0; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + if(clut) { + ulong mask_blue = (ulong)clut[0].blue + + ((ulong)clut[0].green << 8) + + ((ulong)clut[0].red << 16) + + ((ulong)clut[0].alpha << 24); + ulong mask_green = (ulong)clut[1].blue + + ((ulong)clut[1].green << 8) + + ((ulong)clut[1].red << 16) + + ((ulong)clut[1].alpha << 24); + ulong mask_red = (ulong)clut[2].blue + + ((ulong)clut[2].green << 8) + + ((ulong)clut[2].red << 16) + + ((ulong)clut[2].alpha << 24); + int shft_blue = msb(mask_blue) - 8; + int shft_green = msb(mask_green) - 8; + int shft_red = msb(mask_red) - 8; + + for(iy = height; iy; iy--, i += step_up) + for(ix = 0; ix < width; ix++, i++) { + ulong val; + Bread(b, c, sizeof(c)); + val = (ulong)c[0] + ((ulong)c[1] << 8) + + ((ulong)c[2] << 16) + ((ulong)c[1] << 24); + + buf[i].alpha = 0; + if(shft_blue >= 0) + buf[i].blue = (uchar)((val & mask_blue) >> shft_blue); + else + buf[i].blue = (uchar)((val & mask_blue) << -shft_blue); + if(shft_green >= 0) + buf[i].green = (uchar)((val & mask_green) >> shft_green); + else + buf[i].green = (uchar)((val & mask_green) << -shft_green); + if(shft_red >= 0) + buf[i].red = (uchar)((val & mask_red) >> shft_red); + else + buf[i].red = (uchar)((val & mask_red) << -shft_red); + } + } else + for(iy = height; iy; iy--, i += step_up) + for(ix = 0; ix < width; ix++, i++) { + Bread(b, c, nelem(c)); + buf[i].blue = c[0]; + buf[i].green = c[1]; + buf[i].red = c[2]; + } + return 0; +} + + +static Rgb* +ReadBMP(Biobuf *b, int *width, int *height) +{ + int colours, num_coltab = 0; + Filehdr bmfh; + Infohdr bmih; + Rgb clut[256]; + Rgb* buf; + + bmfh.type = r16(b); + if(bmfh.type != 0x4d42) /* signature must be 'BM' */ + sysfatal("bad magic number, not a BMP file"); + + bmfh.size = r32(b); + bmfh.reserved1 = r16(b); + bmfh.reserved2 = r16(b); + bmfh.offbits = r32(b); + + memset(&bmih, 0, sizeof(bmih)); + bmih.size = r32(b); + + if(bmih.size == 0x0c) { /* OS/2 1.x version */ + bmih.width = r16(b); + bmih.height = r16(b); + bmih.planes = r16(b); + bmih.bpp = r16(b); + bmih.compression = BMP_RGB; + } else { /* Windows */ + bmih.width = r32(b); + bmih.height = r32(b); + bmih.planes = r16(b); + bmih.bpp = r16(b); + bmih.compression = r32(b); + bmih.imagesize = r32(b); + bmih.hres = r32(b); + bmih.vres = r32(b); + bmih.colours = r32(b); + bmih.impcolours = r32(b); + } + + if(bmih.bpp < 16) { + /* load colour table */ + if(bmih.impcolours) + num_coltab = (int)bmih.impcolours; + else + num_coltab = 1 << bmih.bpp; + } else if(bmih.compression == BMP_BITFIELDS && + (bmih.bpp == 16 || bmih.bpp == 32)) + /* load bitmasks */ + num_coltab = 3; + + if(num_coltab) { + int i; + Bseek(b, bmih.size + sizeof(Infohdr), 0); + + for(i = 0; i < num_coltab; i++) { + clut[i].blue = (uchar)Bgetc(b); + clut[i].green = (uchar)Bgetc(b); + clut[i].red = (uchar)Bgetc(b); + clut[i].alpha = (uchar)Bgetc(b); + } + } + + *width = bmih.width; + *height = bmih.height; + colours = bmih.bpp; + + Bseek(b, bmfh.offbits, 0); + + if ((buf = calloc(sizeof(Rgb), *width * abs(*height))) == nil) + sysfatal("no memory"); + + switch(colours) { + case 1: + load_1T(b, *width, *height, buf, clut); + break; + case 4: + if(bmih.compression == BMP_RLE4) + load_4C(b, *width, *height, buf, clut); + else + load_4T(b, *width, *height, buf, clut); + break; + case 8: + if(bmih.compression == BMP_RLE8) + load_8C(b, *width, *height, buf, clut); + else + load_8T(b, *width, *height, buf, clut); + break; + case 16: + load_16(b, *width, *height, buf, + bmih.compression == BMP_BITFIELDS ? clut : nil); + break; + case 24: + load_24T(b, *width, *height, buf); + break; + case 32: + load_32(b, *width, *height, buf, + bmih.compression == BMP_BITFIELDS ? clut : nil); + break; + } + return buf; +} + +Rawimage** +Breadbmp(Biobuf *bp, int colourspace) +{ + Rawimage *a, **array; + int c, width, height; + uchar *r, *g, *b; + Rgb *s, *e; + Rgb *bmp; + char ebuf[128]; + + a = nil; + bmp = nil; + array = nil; + USED(a); + USED(bmp); + if (colourspace != CRGB) { + errstr(ebuf, sizeof ebuf); /* throw it away */ + werrstr("ReadRGB: unknown colour space %d", colourspace); + return nil; + } + + if ((bmp = ReadBMP(bp, &width, &height)) == nil) + return nil; + + if ((a = calloc(sizeof(Rawimage), 1)) == nil) + goto Error; + + for (c = 0; c < 3; c++) + if ((a->chans[c] = calloc(width, height)) == nil) + goto Error; + + if ((array = calloc(sizeof(Rawimage *), 2)) == nil) + goto Error; + array[0] = a; + array[1] = nil; + + a->nchans = 3; + a->chandesc = CRGB; + a->chanlen = width * height; + a->r = Rect(0, 0, width, height); + + s = bmp; + e = s + width * height; + r = a->chans[0]; + g = a->chans[1]; + b = a->chans[2]; + + do { + *r++ = s->red; + *g++ = s->green; + *b++ = s->blue; + }while(++s < e); + + free(bmp); + return array; + +Error: + if (a) + for (c = 0; c < 3; c++) + if (a->chans[c]) + free(a->chans[c]); + if (a) + free(a); + if (array) + free(array); + if (bmp) + free(bmp); + return nil; + +} + +Rawimage** +readbmp(int fd, int colorspace) +{ + Rawimage * *a; + Biobuf b; + + if (Binit(&b, fd, OREAD) < 0) + return nil; + a = Breadbmp(&b, colorspace); + Bterm(&b); + return a; +} + + |