428 lines
13 KiB
C++
428 lines
13 KiB
C++
/** @file
|
|
* @author Edouard DUPIN
|
|
* @copyright 2011, Edouard DUPIN, all right reserved
|
|
* @license MPL v2.0 (see license file)
|
|
*/
|
|
|
|
#include <esvg/debug.hpp>
|
|
#include <esvg/Renderer.hpp>
|
|
#include <etk/uri/Uri.hpp>
|
|
#include <etk/uri/provider/provider.hpp>
|
|
|
|
esvg::Renderer::Renderer(const ivec2& _size, esvg::Document* _document, bool _visualDebug) :
|
|
#ifdef DEBUG
|
|
m_visualDebug(_visualDebug),
|
|
m_factor(1),
|
|
#endif
|
|
m_interpolationRecurtionMax(10),
|
|
m_interpolationThreshold(0.25f),
|
|
m_nbSubScanLine(8),
|
|
m_document(_document) {
|
|
#ifdef DEBUG
|
|
if (m_visualDebug == true) {
|
|
m_factor = 20;
|
|
}
|
|
#endif
|
|
setSize(_size);
|
|
}
|
|
|
|
esvg::Renderer::~Renderer() {
|
|
m_buffer.clear();
|
|
m_size = ivec2(0,0);
|
|
}
|
|
|
|
etk::Color<float,4> esvg::Renderer::mergeColor(etk::Color<float,4> _base, etk::Color<float,4> _integration) {
|
|
etk::Color<float,4> result;
|
|
/*
|
|
if (_integration.a() < _base.a()) {
|
|
result = _integration;
|
|
_integration = _base;
|
|
_base = result;
|
|
}
|
|
*/
|
|
result.setR(_integration.a() * _integration.r() + _base.a() * (1.0f - _integration.a()) * _base.r());
|
|
result.setG(_integration.a() * _integration.g() + _base.a() * (1.0f - _integration.a()) * _base.g());
|
|
result.setB(_integration.a() * _integration.b() + _base.a() * (1.0f - _integration.a()) * _base.b());
|
|
result.setA(_integration.a() + _base.a() * (1.0f - _integration.a()));
|
|
if (result.a() != 0.0f) {
|
|
float reverse = 1.0f/result.a();
|
|
result.setR(result.r()*reverse);
|
|
result.setG(result.g()*reverse);
|
|
result.setB(result.b()*reverse);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void esvg::Renderer::print(const esvg::render::Weight& _weightFill,
|
|
ememory::SharedPtr<esvg::render::DynamicColor>& _colorFill,
|
|
const esvg::render::Weight& _weightStroke,
|
|
ememory::SharedPtr<esvg::render::DynamicColor>& _colorStroke,
|
|
float _opacity) {
|
|
if (_colorFill != null) {
|
|
//_colorFill->setViewPort(etk::Pair<vec2, vec2>(vec2(0,0), vec2(sizeX, sizeY)));
|
|
_colorFill->generate(m_document);
|
|
}
|
|
if (_colorStroke != null) {
|
|
//_colorStroke->setViewPort(etk::Pair<vec2, vec2>(vec2(0,0), vec2(sizeX, sizeY)));
|
|
_colorStroke->generate(m_document);
|
|
}
|
|
// all together
|
|
for (int32_t yyy=0; yyy<m_size.y(); ++yyy) {
|
|
for (int32_t xxx=0; xxx<m_size.x(); ++xxx) {
|
|
ivec2 pos(xxx, yyy);
|
|
float valueFill = _weightFill.get(pos);
|
|
float valueStroke = _weightStroke.get(pos);
|
|
// calculate merge of stroke and fill value:
|
|
etk::Color<float,4> intermediateColorFill(etk::color::none);
|
|
etk::Color<float,4> intermediateColorStroke(etk::color::none);
|
|
if ( _colorFill != null
|
|
&& valueFill != 0.0f) {
|
|
intermediateColorFill = _colorFill->getColor(pos);
|
|
intermediateColorFill.setA(intermediateColorFill.a()*valueFill);
|
|
}
|
|
if ( _colorStroke != null
|
|
&& valueStroke != 0.0f) {
|
|
intermediateColorStroke = _colorStroke->getColor(pos);
|
|
intermediateColorStroke.setA(intermediateColorStroke.a()*valueStroke);
|
|
}
|
|
etk::Color<float,4> intermediateColor = mergeColor(intermediateColorFill, intermediateColorStroke);
|
|
intermediateColor.setA(intermediateColor.a() * _opacity);
|
|
#if DEBUG
|
|
for (int32_t deltaY=0; deltaY<m_factor; ++deltaY) {
|
|
for (int32_t deltaX=0; deltaX<m_factor; ++deltaX) {
|
|
int32_t id = m_size.x()*m_factor*(yyy*m_factor+deltaY) + (xxx*m_factor+deltaX);
|
|
m_buffer[id] = mergeColor(m_buffer[id], intermediateColor);
|
|
}
|
|
}
|
|
#else
|
|
m_buffer[m_size.x()*yyy + xxx] = mergeColor(m_buffer[m_size.x()*yyy + xxx], intermediateColor);
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
// display the gradient position:
|
|
ememory::SharedPtr<esvg::render::DynamicColorSpecial> tmpColor = ememory::dynamicPointerCast<esvg::render::DynamicColorSpecial>(_colorFill);
|
|
if (tmpColor != null) {
|
|
esvg::render::SegmentList listSegment;
|
|
// Display bounding box
|
|
listSegment.addSegment(esvg::render::Point(tmpColor->m_viewPort.first),
|
|
esvg::render::Point(vec2(tmpColor->m_viewPort.first.x(), tmpColor->m_viewPort.second.y()) ),
|
|
false);
|
|
listSegment.addSegment(esvg::render::Point(vec2(tmpColor->m_viewPort.first.x(), tmpColor->m_viewPort.second.y()) ),
|
|
esvg::render::Point(tmpColor->m_viewPort.second),
|
|
false);
|
|
listSegment.addSegment(esvg::render::Point(tmpColor->m_viewPort.second),
|
|
esvg::render::Point(vec2(tmpColor->m_viewPort.second.x(), tmpColor->m_viewPort.first.y()) ),
|
|
false);
|
|
listSegment.addSegment(esvg::render::Point(vec2(tmpColor->m_viewPort.second.x(), tmpColor->m_viewPort.first.y()) ),
|
|
esvg::render::Point(tmpColor->m_viewPort.first),
|
|
false);
|
|
listSegment.applyMatrix(tmpColor->m_matrix);
|
|
// display the gradient axis
|
|
listSegment.addSegment(esvg::render::Point(tmpColor->m_pos1),
|
|
esvg::render::Point(tmpColor->m_pos2),
|
|
false);
|
|
/*
|
|
mat2x3 m_matrix;
|
|
etk::Pair<vec2, vec2> m_viewPort;
|
|
vec2 m_pos1;
|
|
vec2 m_pos2;
|
|
*/
|
|
addDebugSegment(listSegment);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void esvg::Renderer::addDebugSegment(const esvg::render::SegmentList& _listSegment) {
|
|
if (m_visualDebug == false) {
|
|
return;
|
|
}
|
|
ivec2 dynamicSize = m_size * m_factor;
|
|
// for each lines:
|
|
for (int32_t yyy=0; yyy<dynamicSize.y(); ++yyy) {
|
|
// Reduce the number of lines in the subsampling parsing:
|
|
etk::Vector<esvg::render::Segment> availlableSegmentPixel;
|
|
for (auto &it : _listSegment.m_data) {
|
|
if ( it.p0.y() * m_factor <= float(yyy+1)
|
|
&& it.p1.y() * m_factor >= float(yyy)) {
|
|
availlableSegmentPixel.pushBack(it);
|
|
}
|
|
}
|
|
//find all the segment that cross the middle of the line of the center of the pixel line:
|
|
float subSamplingCenterPos = yyy + 0.5f;
|
|
etk::Vector<esvg::render::Segment> availlableSegment;
|
|
// find in the subList ...
|
|
for (auto &it : availlableSegmentPixel) {
|
|
if ( it.p0.y() * m_factor <= subSamplingCenterPos
|
|
&& it.p1.y() * m_factor >= subSamplingCenterPos ) {
|
|
availlableSegment.pushBack(it);
|
|
}
|
|
}
|
|
// x position, angle
|
|
etk::Vector<etk::Pair<float, float>> listPosition;
|
|
for (auto &it : availlableSegment) {
|
|
vec2 delta = it.p0 * m_factor - it.p1 * m_factor;
|
|
// x = coefficent*y+bbb;
|
|
float coefficient = delta.x()/delta.y();
|
|
float bbb = it.p0.x() * m_factor - coefficient*it.p0.y() * m_factor;
|
|
float xpos = coefficient * subSamplingCenterPos + bbb;
|
|
if ( xpos >= 0
|
|
&& xpos < dynamicSize.x()
|
|
&& yyy >= 0
|
|
&& yyy < dynamicSize.y() ) {
|
|
if (it.direction == 1.0f) {
|
|
m_buffer[(dynamicSize.x()*yyy + int32_t(xpos))] = etk::color::blue;
|
|
} else {
|
|
m_buffer[(dynamicSize.x()*yyy + int32_t(xpos))] = etk::color::darkRed;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// for each colomn:
|
|
for (int32_t xxx=0; xxx<dynamicSize.x(); ++xxx) {
|
|
// Reduce the number of lines in the subsampling parsing:
|
|
etk::Vector<esvg::render::Segment> availlableSegmentPixel;
|
|
for (auto &it : _listSegment.m_data) {
|
|
if ( ( it.p0.x() * m_factor <= float(xxx+1)
|
|
&& it.p1.x() * m_factor >= float(xxx) )
|
|
|| ( it.p0.x() * m_factor >= float(xxx+1)
|
|
&& it.p1.x() * m_factor <= float(xxx) ) ) {
|
|
availlableSegmentPixel.pushBack(it);
|
|
}
|
|
}
|
|
//find all the segment that cross the middle of the line of the center of the pixel line:
|
|
float subSamplingCenterPos = xxx + 0.5f;
|
|
etk::Vector<esvg::render::Segment> availlableSegment;
|
|
// find in the subList ...
|
|
for (auto &it : availlableSegmentPixel) {
|
|
if ( ( it.p0.x() * m_factor <= subSamplingCenterPos
|
|
&& it.p1.x() * m_factor >= subSamplingCenterPos)
|
|
|| ( it.p0.x() * m_factor >= subSamplingCenterPos
|
|
&& it.p1.x() * m_factor <= subSamplingCenterPos) ) {
|
|
availlableSegment.pushBack(it);
|
|
}
|
|
}
|
|
// x position, angle
|
|
etk::Vector<etk::Pair<float, float>> listPosition;
|
|
for (auto &it : availlableSegment) {
|
|
vec2 delta = it.p0 * m_factor - it.p1 * m_factor;
|
|
// x = coefficent*y+bbb;
|
|
if (delta.x() == 0) {
|
|
continue;
|
|
}
|
|
float coefficient = delta.y()/delta.x();
|
|
float bbb = it.p0.y() * m_factor - coefficient*it.p0.x() * m_factor;
|
|
float ypos = coefficient * subSamplingCenterPos + bbb;
|
|
if ( ypos >= 0
|
|
&& ypos < dynamicSize.y()
|
|
&& xxx >= 0
|
|
&& xxx < dynamicSize.y() ) {
|
|
if (it.direction == 1.0f) {
|
|
m_buffer[(dynamicSize.x()*int32_t(ypos) + xxx)] = etk::color::blue;
|
|
} else {
|
|
m_buffer[(dynamicSize.x()*int32_t(ypos) + xxx)] = etk::color::darkRed;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
void esvg::Renderer::writePPM(const etk::Uri& _uri) {
|
|
if (m_buffer.size() == 0) {
|
|
return;
|
|
}
|
|
auto fileIo = etk::uri::get(_uri);
|
|
if (fileIo == null) {
|
|
Log.error("Can not create the uri: " << _uri);
|
|
return;
|
|
}
|
|
if (fileIo->open(etk::io::OpenMode::Write) == false) {
|
|
Log.error("Can not open (r) the file : " << _uri);
|
|
return;
|
|
}
|
|
int32_t sizeX = m_size.x();
|
|
int32_t sizeY = m_size.y();
|
|
#if DEBUG
|
|
sizeX *= m_factor;
|
|
sizeY *= m_factor;
|
|
#endif
|
|
Log.debug("Generate ppm : " << m_size << " debug size=" << ivec2(sizeX,sizeY));
|
|
char tmpValue[1024];
|
|
sprintf(tmpValue, "P6 %d %d 255 ", sizeX, sizeY);
|
|
fileIo->write(tmpValue,1,sizeof(tmpValue));
|
|
for (int32_t iii=0 ; iii<sizeX*sizeY; iii++) {
|
|
etk::Color<uint8_t,3> tmp = m_buffer[iii];
|
|
fileIo->write(&tmp, 1, 3);
|
|
}
|
|
fileIo->close();
|
|
}
|
|
#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 etk::Uri& _uri) {
|
|
if (m_buffer.size() == 0) {
|
|
return;
|
|
}
|
|
auto fileIo = etk::uri::get(_uri);
|
|
if (fileIo == null) {
|
|
Log.error("Can not create the uri: " << _uri);
|
|
return;
|
|
}
|
|
if (fileIo->open(etk::io::OpenMode::Write) == false) {
|
|
Log.error("Can not open (r) the file : " << _uri);
|
|
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; // "Win "
|
|
for (int32_t jjj=0; jjj<16; ++jjj) {
|
|
infoHeader.biUnused[jjj] = 0;
|
|
}
|
|
infoHeader.biUnused[12] = 0x00000002;
|
|
#endif
|
|
// get the data :
|
|
fileIo->write(&fileHeader, sizeof(struct bitmapFileHeader), 1);
|
|
fileIo->write(&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
|
|
fileIo->write(data,1,4);
|
|
}
|
|
}
|
|
fileIo->close();
|
|
}
|
|
|
|
|
|
void esvg::Renderer::setSize(const ivec2& _size) {
|
|
m_size = _size;
|
|
m_buffer.resize(m_size.x() * m_size.y()
|
|
#if DEBUG
|
|
* m_factor * m_factor
|
|
#endif
|
|
, etk::color::none);
|
|
}
|
|
|
|
const ivec2& esvg::Renderer::getSize() const {
|
|
return m_size;
|
|
}
|
|
|
|
etk::Vector<etk::Color<float,4>> esvg::Renderer::getData() {
|
|
return m_buffer;
|
|
}
|
|
|
|
|
|
|
|
|
|
void esvg::Renderer::setInterpolationRecurtionMax(int32_t _value) {
|
|
m_interpolationRecurtionMax = etk::avg(1, _value, 200);
|
|
}
|
|
|
|
int32_t esvg::Renderer::getInterpolationRecurtionMax() const {
|
|
return m_interpolationRecurtionMax;
|
|
}
|
|
|
|
void esvg::Renderer::setInterpolationThreshold(float _value) {
|
|
m_interpolationThreshold = etk::avg(0.0f, _value, 20000.0f);
|
|
}
|
|
|
|
float esvg::Renderer::getInterpolationThreshold() const {
|
|
return m_interpolationThreshold;
|
|
}
|
|
|
|
void esvg::Renderer::setNumberSubScanLine(int32_t _value) {
|
|
m_nbSubScanLine = etk::avg(1, _value, 200);
|
|
}
|
|
|
|
int32_t esvg::Renderer::getNumberSubScanLine() const {
|
|
return m_nbSubScanLine;
|
|
}
|
|
|
|
|