[DEV] Add bitmap export with Alpha correct (reverse ingeneer of BMP, do not ask me how it work ...)

This commit is contained in:
Edouard DUPIN 2015-11-23 21:26:51 +01:00
parent 85af6d85d0
commit a1f83ee5da
10 changed files with 169 additions and 44 deletions

View File

@ -28,7 +28,7 @@ esvg::PaintState::PaintState() :
}
void esvg::PaintState::clear() {
fill = etk::color::none;
fill = etk::color::black;
stroke = etk::color::none;
strokeWidth = 1.0;
viewPort.setValue(255,255);

View File

@ -8,6 +8,7 @@
#include <esvg/debug.h>
#include <esvg/Renderer.h>
#include <etk/os/FSNode.h>
// 4 is for the RGBA ...
#define DATA_ALLOCATION_ELEMENT (4)
@ -163,29 +164,146 @@ void esvg::Renderer::print(const esvg::render::Weight& _weightFill,
#endif
// Writing the buffer to a .PPM file, assuming it has
// RGB-structure, one byte per color component
//--------------------------------------------------
void esvg::Renderer::writePpm(std::string fileName) {
void esvg::Renderer::writePPM(const std::string& _fileName) {
if (m_buffer.size() == 0) {
return;
}
FILE* fd = fopen(fileName.c_str(), "wb");
if(fd != nullptr) {
int32_t sizeX = m_size.x();
int32_t sizeY = m_size.y();
#if DEBUG
sizeX *= m_factor;
sizeY *= m_factor;
#endif
SVG_DEBUG("Generate ppm : " << m_size << " debug size=" << ivec2(sizeX,sizeY));
fprintf(fd, "P6 %d %d 255 ", sizeX, sizeY);
for (int32_t iii=0 ; iii<sizeX*sizeY; iii++) {
etk::Color<uint8_t,3> tmp = m_buffer[iii];
fwrite(&tmp, 1, 3, fd);
}
fclose(fd);
etk::FSNode tmpFile(_fileName);
if(tmpFile.fileOpenWrite() == false) {
SVG_ERROR("Can not find the file name=\"" << tmpFile << "\"");
return;
}
int32_t sizeX = m_size.x();
int32_t sizeY = m_size.y();
#if DEBUG
sizeX *= m_factor;
sizeY *= m_factor;
#endif
SVG_DEBUG("Generate ppm : " << m_size << " debug size=" << ivec2(sizeX,sizeY));
char tmpValue[1024];
sprintf(tmpValue, "P6 %d %d 255 ", sizeX, sizeY);
tmpFile.fileWrite(tmpValue,1,sizeof(tmpValue));
for (int32_t iii=0 ; iii<sizeX*sizeY; iii++) {
etk::Color<uint8_t,3> tmp = m_buffer[iii];
tmpFile.fileWrite(&tmp, 1, 3);
}
tmpFile.fileClose();
}
#define PLOPPP
extern "C" {
#pragma pack(push,1)
struct bitmapFileHeader {
int16_t bfType;
int32_t bfSize;
int32_t bfReserved;
int32_t bfOffBits;
};
struct bitmapInfoHeader {
int32_t biSize;
int32_t biWidth;
int32_t biHeight;
int16_t biPlanes;
int16_t biBitCount;
int32_t biCompression;
int32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
#ifndef PLOPPP
int32_t biClrUsed;
int32_t biClrImportant;
#else
// https://en.wikipedia.org/wiki/BMP_file_format / example 2
int32_t biPaletteNumber;
int32_t biImportantColor;
int32_t biBitMaskRed;
int32_t biBitMaskGreen;
int32_t biBitMaskBlue;
int32_t biBitMaskAlpha;
int32_t biLCSColorSpace;
int32_t biUnused[16];
#endif
};
#pragma pack(pop)
}
void esvg::Renderer::writeBMP(const std::string& _fileName) {
if (m_buffer.size() == 0) {
return;
}
etk::FSNode tmpFile(_fileName);
if(tmpFile.fileOpenWrite() == false) {
SVG_ERROR("Can not find the file name=\"" << tmpFile << "\"");
return;
}
struct bitmapFileHeader fileHeader;
struct bitmapInfoHeader infoHeader;
int32_t sizeX = m_size.x();
int32_t sizeY = m_size.y();
#if DEBUG
sizeX *= m_factor;
sizeY *= m_factor;
#endif
fileHeader.bfType = 0x4D42;
fileHeader.bfSize = sizeof(struct bitmapFileHeader) + sizeof(struct bitmapInfoHeader) + sizeX*sizeY*4;
fileHeader.bfReserved = 0;
fileHeader.bfOffBits = sizeof(struct bitmapFileHeader) + sizeof(struct bitmapInfoHeader);
infoHeader.biSize = sizeof(struct bitmapInfoHeader);
infoHeader.biWidth = sizeX;
infoHeader.biHeight = sizeY;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 32;
#ifndef PLOPPP
infoHeader.biCompression = 0;
#else
infoHeader.biCompression = 3;
#endif
infoHeader.biSizeImage = sizeX*sizeY*4;
infoHeader.biXPelsPerMeter = 75;
infoHeader.biYPelsPerMeter = 75;
#ifndef PLOPPP
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0;
#else
infoHeader.biPaletteNumber = 0;
infoHeader.biImportantColor = 0;
infoHeader.biBitMaskRed = 0xFF000000;
infoHeader.biBitMaskGreen = 0x00FF0000;
infoHeader.biBitMaskBlue =0x0000FF00;
infoHeader.biBitMaskAlpha = 0x000000FF;
infoHeader.biLCSColorSpace = 0x73524742; // "???"
for (int32_t jjj=0; jjj<16; ++jjj) {
infoHeader.biUnused[jjj] = 0;
}
infoHeader.biUnused[12] = 0x00000002;
#endif
// get the data :
tmpFile.fileWrite(&fileHeader, sizeof(struct bitmapFileHeader), 1);
tmpFile.fileWrite(&infoHeader, sizeof(struct bitmapInfoHeader), 1);
uint8_t data[16];
for(int32_t yyy=sizeY-1; yyy>=0; --yyy) {
for(int32_t xxx=0; xxx<sizeX; ++xxx) {
const etk::Color<uint8_t,4>& tmpColor = m_buffer[sizeX*yyy + xxx];
uint8_t* pointer = data;
#ifndef PLOPPP
*pointer++ = tmpColor.a();
*pointer++ = tmpColor.r();
*pointer++ = tmpColor.g();
*pointer++ = tmpColor.b();
#else
*pointer++ = tmpColor.a();
*pointer++ = tmpColor.b();
*pointer++ = tmpColor.g();
*pointer++ = tmpColor.r();
#endif
tmpFile.fileWrite(data,1,4);
}
}
tmpFile.fileClose();
}

View File

@ -50,7 +50,8 @@ namespace esvg {
void setNumberSubScanLine(int32_t _value);
int32_t getNumberSubScanLine() const;
public:
void writePpm(std::string fileName);
void writePPM(const std::string& _fileName);
void writeBMP(const std::string& _fileName);
protected:
etk::Color<float,4> mergeColor(etk::Color<float,4> _base, const etk::Color<float,4>& _integration);
public:

View File

@ -85,7 +85,7 @@ void esvg::Document::generateTestFile()
std::string tmpFileOut = "yyy_out_";
tmpFileOut += m_fileName;
tmpFileOut += ".ppm";
m_renderedElement->writePpm(tmpFileOut);
m_renderedElement->writePPM(tmpFileOut);
}
@ -117,7 +117,13 @@ void esvg::Document::generateAnImage(const ivec2& _size, const std::string& _fil
//basicTrans *= etk::mat2Translate(vec2(width/3, height/3));
draw(*m_renderedElement, basicTrans);
m_renderedElement->writePpm(_fileName);
if (etk::end_with(_fileName, ".ppm") == true) {
m_renderedElement->writePPM(_fileName);
} else if (etk::end_with(_fileName, ".bmp") == true) {
m_renderedElement->writeBMP(_fileName);
} else {
SVG_ERROR("Can not store with this extention : " << _fileName << " not in .bmp/.ppm");
}
}
/*
void esvg::Document::generateAnImage(draw::Image& _output) {

View File

@ -21,7 +21,7 @@ TEST(TestCircle, fill) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestCircle_fill.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestCircle_fill.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestCircle_fill.bmp", g_visualDebug);
}
TEST(TestCircle, stroke) {
@ -32,7 +32,7 @@ TEST(TestCircle, stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestCircle_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestCircle_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestCircle_stroke.bmp", g_visualDebug);
}
TEST(TestCircle, fill_and_stroke) {
@ -43,5 +43,5 @@ TEST(TestCircle, fill_and_stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestCircle_fill_and_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestCircle_fill_and_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestCircle_fill_and_stroke.bmp", g_visualDebug);
}

View File

@ -21,7 +21,7 @@ TEST(TestEllipse, fill) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestEllipse_fill.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestEllipse_fill.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestEllipse_fill.bmp", g_visualDebug);
}
TEST(TestEllipse, stroke) {
@ -32,7 +32,7 @@ TEST(TestEllipse, stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestEllipse_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestEllipse_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestEllipse_stroke.bmp", g_visualDebug);
}
TEST(TestEllipse, fill_and_stroke) {
@ -43,6 +43,6 @@ TEST(TestEllipse, fill_and_stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestEllipse_fill_and_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestEllipse_fill_and_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestEllipse_fill_and_stroke.bmp", g_visualDebug);
}

View File

@ -21,7 +21,7 @@ TEST(TestLine, stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestLine_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestLine_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestLine_stroke.bmp", g_visualDebug);
}

View File

@ -22,7 +22,7 @@ TEST(TestPath, fill) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestPath_fill.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestPath_fill.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestPath_fill.bmp", g_visualDebug);
}
TEST(TestPath, stroke) {
@ -34,7 +34,7 @@ TEST(TestPath, stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestPath_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestPath_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestPath_stroke.bmp", g_visualDebug);
}
TEST(TestPath, fill_and_stroke) {
@ -46,5 +46,5 @@ TEST(TestPath, fill_and_stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestPath_fill_and_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestPath_fill_and_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestPath_fill_and_stroke.bmp", g_visualDebug);
}

View File

@ -21,7 +21,7 @@ TEST(TestPolygon, fill) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestPolygon_fill.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestPolygon_fill.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestPolygon_fill.bmp", g_visualDebug);
}
TEST(TestPolygon, stroke) {
@ -32,7 +32,7 @@ TEST(TestPolygon, stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestPolygon_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestPolygon_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestPolygon_stroke.bmp", g_visualDebug);
}
TEST(TestPolygon, fill_and_stroke) {
@ -43,5 +43,5 @@ TEST(TestPolygon, fill_and_stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestPolygon_fill_and_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestPolygon_fill_and_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestPolygon_fill_and_stroke.bmp", g_visualDebug);
}

View File

@ -21,7 +21,7 @@ TEST(TestRectangle, fill) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestRectangle_fill.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_fill.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_fill.bmp", g_visualDebug);
}
TEST(TestRectangle, stroke) {
@ -32,7 +32,7 @@ TEST(TestRectangle, stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestRectangle_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_stroke.bmp", g_visualDebug);
}
TEST(TestRectangle, fill_and_stroke) {
@ -43,7 +43,7 @@ TEST(TestRectangle, fill_and_stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestRectangle_fill_and_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_fill_and_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_fill_and_stroke.bmp", g_visualDebug);
}
TEST(TestRectangle, fill_and_stroke_blend) {
@ -54,7 +54,7 @@ TEST(TestRectangle, fill_and_stroke_blend) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestRectangle_fill_and_stroke_blend.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_fill_and_stroke_blend.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_fill_and_stroke_blend.bmp", g_visualDebug);
}
TEST(TestRectangle, fill_and_stroke_opacity) {
@ -65,7 +65,7 @@ TEST(TestRectangle, fill_and_stroke_opacity) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestRectangle_fill_and_stroke_blend.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_fill_and_stroke_blend.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_fill_and_stroke_blend.bmp", g_visualDebug);
}
TEST(TestRectangle, corned_fill) {
@ -76,7 +76,7 @@ TEST(TestRectangle, corned_fill) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestRectangle_corned_fill.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_corned_fill.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_corned_fill.bmp", g_visualDebug);
}
TEST(TestRectangle, corned_stroke) {
@ -87,7 +87,7 @@ TEST(TestRectangle, corned_stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestRectangle_corned_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_corned_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_corned_stroke.bmp", g_visualDebug);
}
TEST(TestRectangle, corned_fill_and_stroke) {
@ -98,5 +98,5 @@ TEST(TestRectangle, corned_fill_and_stroke) {
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestRectangle_corned_fill_and_stroke.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_corned_fill_and_stroke.ppm", g_visualDebug);
doc.generateAnImage(ivec2(100, 100), "TestRectangle_corned_fill_and_stroke.bmp", g_visualDebug);
}