diff --git a/src/bmp.cpp b/src/bmp.cpp index 683d5ea..6a2b317 100644 --- a/src/bmp.cpp +++ b/src/bmp.cpp @@ -19,11 +19,189 @@ 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 *afilename) +{ + Image *image; + char *filename; + uint32 length; + uint8 *data; + StreamMemory file; + int i, x, y; + + filename = Image::getFilename(afilename); + if(filename == nil) + return nil; + data = getFileContents(filename, &length); + rwFree(filename); + if(data == nil) + return nil; + file.open(data, length); + + /* read headers */ + BMPheader bmp; + DIBheader dib; + if(!bmp.read(&file)) + goto lose; + file.read(&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; + } + + bool32 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<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); + + int 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) { - uint8 buf[54]; uint8 *p; StreamFile file; if(!file.open(filename, "wb")){ @@ -37,35 +215,34 @@ writeBMP(Image *image, const char *filename) int32 depth = image->depth == 32 ? 24 : image->depth; stride = stride+3 & ~3; - // File header - p = buf; - *p++ = 'B'; - *p++ = 'M'; - *(uint32*)p = 0x36 + 4*pallen + image->height*stride; p += 4; - *(uint16*)p = 0; p += 2; // reserved - *(uint16*)p = 0; p += 2; - *(uint32*)p = 0x36 + 4*pallen; p += 4; // data offset + // 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); - // DIB header - *(uint32*)p = 0x28; p += 4; // header size - *(int32*)p = image->width; p += 4; - *(int32*)p = image->height; p += 4; - *(int16*)p = 1; p += 2; // number of planes - *(int16*)p = depth; p += 2; - *(uint32*)p = 0; p += 4; // compression: none - *(int32*)p = 0; p += 4; // size, not needed in our case - *(int32*)p = 2835; p += 4; // x resolution, 72dpi - *(int32*)p = 2835; p += 4; // y resolution - *(int32*)p = 0; p += 4; // number of used palette colors: max - *(int32*)p = 0; p += 4; // important pixels - - file.write(buf, 54); + 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.write(&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(0); + file.writeU8(0xFF); } uint8 *line = image->pixels + (image->height-1)*image->stride; diff --git a/src/d3d/d3d.cpp b/src/d3d/d3d.cpp index c6746e4..e1eb01f 100644 --- a/src/d3d/d3d.cpp +++ b/src/d3d/d3d.cpp @@ -392,6 +392,7 @@ rasterCreateTexture(Raster *raster) natras->texture = createTexture(raster->width, raster->height, raster->format & Raster::MIPMAP ? levels : 1, format); + assert(natras->texture && "couldn't create d3d texture"); } static void @@ -495,6 +496,7 @@ rasterFromImage(Raster *raster, Image *image) if(image->depth <= 8 && !isP8supported){ truecolimg = Image::create(image->width, image->height, image->depth); truecolimg->pixels = image->pixels; + truecolimg->stride = image->stride; truecolimg->palette = image->palette; truecolimg->unindex(); image = truecolimg; diff --git a/src/rwobjects.h b/src/rwobjects.h index 91ca74f..4accfb5 100644 --- a/src/rwobjects.h +++ b/src/rwobjects.h @@ -149,6 +149,7 @@ struct Image Image *readTGA(const char *filename); void writeTGA(Image *image, const char *filename); +Image *readBMP(const char *filename); void writeBMP(Image *image, const char *filename); // used to emulate d3d and xbox textures diff --git a/src/texture.cpp b/src/texture.cpp index fd97217..8a6b882 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -263,6 +263,7 @@ defaultFindCB(const char *name) } +// TODO: no hardcoded file endings // TODO: actually read the mask! static Texture* defaultReadCB(const char *name, const char *mask) @@ -273,6 +274,11 @@ defaultReadCB(const char *name, const char *mask) strcpy(n, name); strcat(n, ".tga"); img = readTGA(n); + if(img == nil){ + strcpy(n, name); + strcat(n, ".bmp"); + img = readBMP(n); + } rwFree(n); if(img){ tex = Texture::create(Raster::createFromImage(img)); diff --git a/tools/clumpview/main.cpp b/tools/clumpview/main.cpp index bac5cc1..13df56f 100644 --- a/tools/clumpview/main.cpp +++ b/tools/clumpview/main.cpp @@ -170,6 +170,7 @@ InitRW(void) initFont(); + rw::d3d::isP8supported = false; tex = rw::Texture::read("maze", nil); tex2 = rw::Texture::read("checkers", nil); @@ -264,14 +265,16 @@ im2dtest(void) verts[i].setScreenZ(rw::im2d::GetNearZ()); verts[i].setCameraZ(Scene.camera->nearPlane); verts[i].setRecipCameraZ(recipZ); -// verts[i].setColor(vs[i].r, vs[i].g, vs[i].b, vs[i].a); - verts[i].setColor(255, 255, 255, 255); + verts[i].setColor(vs[i].r, vs[i].g, vs[i].b, vs[i].a); + if(dosoftras) + verts[i].setColor(255, 255, 255, 255); verts[i].setU(vs[i].u + 0.5f/640.0f, recipZ); verts[i].setV(vs[i].v + 0.5f/448.0f, recipZ); } -// rw::SetRenderStatePtr(rw::TEXTURERASTER, tex2->raster); - rw::SetRenderStatePtr(rw::TEXTURERASTER, testras); + rw::SetRenderStatePtr(rw::TEXTURERASTER, tex->raster); + if(dosoftras) + rw::SetRenderStatePtr(rw::TEXTURERASTER, testras); rw::SetRenderState(rw::TEXTUREADDRESS, rw::Texture::WRAP); rw::SetRenderState(rw::TEXTUREFILTER, rw::Texture::NEAREST); rw::SetRenderState(rw::VERTEXALPHA, 1); @@ -347,8 +350,8 @@ void drawtest(void); extern void endSoftras(void); if(dosoftras){ endSoftras(); - im2dtest(); } + im2dtest(); // Scene.clump->render(); // im3dtest();