summaryrefslogtreecommitdiff
path: root/src/bmp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bmp.cpp')
-rw-r--r--src/bmp.cpp284
1 files changed, 284 insertions, 0 deletions
diff --git a/src/bmp.cpp b/src/bmp.cpp
new file mode 100644
index 0000000..f69e58d
--- /dev/null
+++ b/src/bmp.cpp
@@ -0,0 +1,284 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define PLUGIN_ID 0
+
+namespace rw {
+
+// NB: this has padding and cannot be streamed directly!
+struct BMPheader
+{
+ uint16 magic;
+ uint32 size;
+ uint16 reserved[2];
+ uint32 offset;
+
+ bool32 read(Stream *stream);
+ void write(Stream *stream);
+};
+
+// This one is aligned and can be streamed directly
+struct DIBheader
+{
+ uint32 headerSize;
+ int32 width;
+ int32 height;
+ int16 numPlanes;
+ int16 depth;
+ uint32 compression;
+ uint32 imgSize;
+ int32 hres;
+ int32 vres;
+ int32 paletteLen;
+ int32 numImportant;
+ // end of 40 btyes
+
+ uint32 rmask, gmask, bmask, amask;
+};
+
+bool32
+BMPheader::read(Stream *stream)
+{
+ magic = stream->readU16();
+ size = stream->readU32();
+ reserved[0] = stream->readU16();
+ reserved[1] = stream->readU16();
+ offset = stream->readU32();
+ return magic == 0x4D42;
+}
+
+void
+BMPheader::write(Stream *stream)
+{
+
+ stream->writeU16(magic);
+ stream->writeU32(size);
+ stream->writeU16(reserved[0]);
+ stream->writeU16(reserved[1]);
+ stream->writeU32(offset);
+}
+
+Image*
+readBMP(const char *filename)
+{
+ ASSERTLITTLE;
+ Image *image;
+ uint32 length;
+ uint8 *data;
+ StreamMemory file;
+ int i, x, y;
+
+ bool32 noalpha;
+ int pad;
+
+ data = getFileContents(filename, &length);
+ if(data == nil)
+ return nil;
+ file.open(data, length);
+
+ /* read headers */
+ BMPheader bmp;
+ DIBheader dib;
+ if(!bmp.read(&file))
+ goto lose;
+ file.read8(&dib, sizeof(dib));
+ file.seek(dib.headerSize-sizeof(dib)); // skip the part of the header we're ignoring
+ if(dib.headerSize <= 16){
+ dib.compression = 0;
+ dib.paletteLen = 0;
+ }
+
+ noalpha = true;
+
+ // Recognize 32 bit formats
+ if(dib.compression == 3){
+ if(dib.rmask != 0xFF0000 ||
+ dib.gmask != 0x00FF00 ||
+ dib.bmask != 0x0000FF)
+ goto lose;
+ dib.compression = 0;
+ if(dib.headerSize > 52 && dib.amask == 0xFF000000)
+ noalpha = false;
+ }
+
+ if(dib.compression != 0)
+ goto lose;
+
+ image = Image::create(dib.width, dib.height, dib.depth);
+ image->allocate();
+
+
+ if(image->palette){
+ int len = 1<<dib.depth;
+ uint8 (*color)[4] = (uint8 (*)[4])image->palette;
+ for(i = 0; i < len; i++){
+ color[i][2] = file.readU8(); // blue
+ color[i][1] = file.readU8(); // green
+ color[i][0] = file.readU8(); // red
+ color[i][3] = file.readU8(); // alpha
+ if(noalpha)
+ color[i][3] = 0xFF;
+ }
+ }
+
+ file.seek(bmp.offset, 0);
+
+ pad = image->width*image->bpp % 4;
+
+ uint8 *px, *line;
+ line = image->pixels + (image->height-1)*image->stride;
+ for(y = 0; y < image->height; y++){
+ px = line;
+ for(x = 0; x < image->width; x++){
+ switch(image->depth){
+ case 4:
+ i = file.readU8();;
+ px[x+0] = (i>>4)&0xF;
+ px[x+1] = i&0xF;
+ x++;
+ break;
+
+ case 8:
+ px[x] = file.readU8();
+ break;
+
+ case 16:
+ // TODO: what format is this even? and what does Image expect?
+ px[x*2 + 0] = file.readU8();
+ px[x*2 + 1] = file.readU8();
+ break;
+
+ case 24:
+ px[x*3 + 2] = file.readU8();
+ px[x*3 + 1] = file.readU8();
+ px[x*3 + 0] = file.readU8();
+ break;
+
+ case 32:
+ px[x*4 + 2] = file.readU8();
+ px[x*4 + 1] = file.readU8();
+ px[x*4 + 0] = file.readU8();
+ px[x*4 + 3] = file.readU8();
+ if(noalpha)
+ px[x*4 + 3] = 0xFF;
+ break;
+
+ default:
+ goto lose;
+ }
+ }
+
+ line -= image->stride;
+ file.seek(pad);
+ }
+
+
+ file.close();
+ rwFree(data);
+ return image;
+
+lose:
+ file.close();
+ rwFree(data);
+ return nil;
+}
+
+/* can't write alpha */
+void
+writeBMP(Image *image, const char *filename)
+{
+ ASSERTLITTLE;
+ uint8 *p;
+ StreamFile file;
+ if(!file.open(filename, "wb")){
+ RWERROR((ERR_FILE, filename));
+ return;
+ }
+
+ int32 pallen = image->depth > 8 ? 0 :
+ image->depth == 4 ? 16 : 256;
+ int32 stride = image->width*image->depth/8;
+ int32 depth = image->depth == 32 ? 24 : image->depth;
+ stride = stride+3 & ~3;
+
+ // File headers
+ BMPheader bmp;
+ bmp.magic = 0x4D42; // BM
+ bmp.size = 0x36 + 4*pallen + image->height*stride;
+ bmp.reserved[0] = 0;
+ bmp.reserved[1] = 0;
+ bmp.offset = 0x36 + 4*pallen;
+ bmp.write(&file);
+
+ DIBheader dib;
+ dib.headerSize = 0x28;
+ dib.width = image->width;
+ dib.height = image->height;
+ dib.numPlanes = 1;
+ dib.depth = depth;
+ dib.compression = 0;
+ dib.imgSize = 0;
+ dib.hres = 2835; // 72dpi
+ dib.vres = 2835; // 72dpi
+ dib.paletteLen = 0;
+ dib.numImportant = 0;
+ file.write8(&dib, dib.headerSize);
+
+ for(int i = 0; i < pallen; i++){
+ file.writeU8(image->palette[i*4+2]);
+ file.writeU8(image->palette[i*4+1]);
+ file.writeU8(image->palette[i*4+0]);
+ file.writeU8(0xFF);
+ }
+
+ uint8 *line = image->pixels + (image->height-1)*image->stride;
+ int32 n;
+ for(int y = 0; y < image->height; y++){
+ p = line;
+ for(int x = 0; x < image->width; x++){
+ switch(image->depth){
+ case 4:
+ file.writeU8((p[0]&0xF)<<4 | (p[1]&0xF));
+ p += 2;
+ x++;
+ break;
+ case 8:
+ file.writeU8(*p++);
+ break;
+ case 16:
+ file.writeU8(p[0]);
+ file.writeU8(p[1]);
+ p += 2;
+ break;
+ case 24:
+ file.writeU8(p[2]);
+ file.writeU8(p[1]);
+ file.writeU8(p[0]);
+ p += 3;
+ break;
+ case 32:
+ file.writeU8(p[2]);
+ file.writeU8(p[1]);
+ file.writeU8(p[0]);
+ p += 4;
+ }
+ }
+ n = (p-line) % 4;
+ while(n--)
+ file.writeU8(0);
+ line -= image->stride;
+ }
+
+ file.close();
+}
+
+}