aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/jpg/readbmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/jpg/readbmp.c')
-rw-r--r--src/cmd/jpg/readbmp.c626
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;
+}
+
+