/* -*- mode: c++; c-basic-offset: 4 -*- */
#define NO_IMPORT_ARRAY
#include "_backend_agg.h"
#include "mplutils.h"
void BufferRegion::to_string_argb(uint8_t *buf)
{
unsigned char *pix;
unsigned char tmp;
size_t i, j;
memcpy(buf, data, height * stride);
for (i = 0; i < (size_t)height; ++i) {
pix = buf + i * stride;
for (j = 0; j < (size_t)width; ++j) {
// Convert rgba to argb
tmp = pix[2];
pix[2] = pix[0];
pix[0] = tmp;
pix += 4;
}
}
}
RendererAgg::RendererAgg(unsigned int width, unsigned int height, double dpi)
: width(width),
height(height),
dpi(dpi),
NUMBYTES(width * height * 4),
pixBuffer(NULL),
renderingBuffer(),
alphaBuffer(NULL),
alphaMaskRenderingBuffer(),
alphaMask(alphaMaskRenderingBuffer),
pixfmtAlphaMask(alphaMaskRenderingBuffer),
rendererBaseAlphaMask(),
rendererAlphaMask(),
scanlineAlphaMask(),
slineP8(),
slineBin(),
pixFmt(),
rendererBase(),
rendererAA(),
rendererBin(),
theRasterizer(8192),
lastclippath(NULL),
_fill_color(agg::rgba(1, 1, 1, 0))
{
unsigned stride(width * 4);
pixBuffer = new agg::int8u[NUMBYTES];
renderingBuffer.attach(pixBuffer, width, height, stride);
pixFmt.attach(renderingBuffer);
rendererBase.attach(pixFmt);
rendererBase.clear(_fill_color);
rendererAA.attach(rendererBase);
rendererBin.attach(rendererBase);
hatch_size = int(dpi);
hatchBuffer = new agg::int8u[hatch_size * hatch_size * 4];
hatchRenderingBuffer.attach(hatchBuffer, hatch_size, hatch_size, hatch_size * 4);
}
RendererAgg::~RendererAgg()
{
delete[] hatchBuffer;
delete[] alphaBuffer;
delete[] pixBuffer;
}
void RendererAgg::create_alpha_buffers()
{
if (!alphaBuffer) {
alphaBuffer = new agg::int8u[width * height];
alphaMaskRenderingBuffer.attach(alphaBuffer, width, height, width);
rendererBaseAlphaMask.attach(pixfmtAlphaMask);
rendererAlphaMask.attach(rendererBaseAlphaMask);
}
}
BufferRegion *RendererAgg::copy_from_bbox(agg::rect_d in_rect)
{
agg::rect_i rect(
(int)in_rect.x1, height - (int)in_rect.y2, (int)in_rect.x2, height - (int)in_rect.y1);
BufferRegion *reg = NULL;
reg = new BufferRegion(rect);
agg::rendering_buffer rbuf;
rbuf.attach(reg->get_data(), reg->get_width(), reg->get_height(), reg->get_stride());
pixfmt pf(rbuf);
renderer_base rb(pf);
rb.copy_from(renderingBuffer, &rect, -rect.x1, -rect.y1);
return reg;
}
void RendererAgg::restore_region(BufferRegion ®ion)
{
if (region.get_data() == NULL) {
throw std::runtime_error("Cannot restore_region from NULL data");
}
agg::rendering_buffer rbuf;
rbuf.attach(region.get_data(), region.get_width(), region.get_height(), region.get_stride());
rendererBase.copy_from(rbuf, 0, region.get_rect().x1, region.get_rect().y1);
}
// Restore the part of the saved region with offsets
void
RendererAgg::restore_region(BufferRegion ®ion, int xx1, int yy1, int xx2, int yy2, int x, int y )
{
if (region.get_data() == NULL) {
throw std::runtime_error("Cannot restore_region from NULL data");
}
agg::rect_i &rrect = region.get_rect();
agg::rect_i rect(xx1 - rrect.x1, (yy1 - rrect.y1), xx2 - rrect.x1, (yy2 - rrect.y1));
agg::rendering_buffer rbuf;
rbuf.attach(region.get_data(), region.get_width(), region.get_height(), region.get_stride());
rendererBase.copy_from(rbuf, &rect, x, y);
}
bool RendererAgg::render_clippath(py::PathIterator &clippath,
const agg::trans_affine &clippath_trans)
{
typedef agg::conv_transform<py::PathIterator> transformed_path_t;
typedef agg::conv_curve<transformed_path_t> curve_t;
bool has_clippath = (clippath.total_vertices() != 0);
if (has_clippath &&
(clippath.get_id() != lastclippath || clippath_trans != lastclippath_transform)) {
create_alpha_buffers();
agg::trans_affine trans(clippath_trans);
trans *= agg::trans_affine_scaling(1.0, -1.0);
trans *= agg::trans_affine_translation(0.0, (double)height);
rendererBaseAlphaMask.clear(agg::gray8(0, 0));
transformed_path_t transformed_clippath(clippath, trans);
curve_t curved_clippath(transformed_clippath);
theRasterizer.add_path(curved_clippath);
rendererAlphaMask.color(agg::gray8(255, 255));
agg::render_scanlines(theRasterizer, scanlineAlphaMask, rendererAlphaMask);
lastclippath = clippath.get_id();
lastclippath_transform = clippath_trans;
}
return has_clippath;
}
void RendererAgg::tostring_rgb(uint8_t *buf)
{
// "Return the rendered buffer as an RGB string"
int row_len = width * 3;
agg::rendering_buffer renderingBufferTmp;
renderingBufferTmp.attach(buf, width, height, row_len);
agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_rgb24());
}
void RendererAgg::tostring_argb(uint8_t *buf)
{
//"Return the rendered buffer as an RGB string";
int row_len = width * 4;
agg::rendering_buffer renderingBufferTmp;
renderingBufferTmp.attach(buf, width, height, row_len);
agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_argb32());
}
void RendererAgg::tostring_bgra(uint8_t *buf)
{
//"Return the rendered buffer as an RGB string";
int row_len = width * 4;
agg::rendering_buffer renderingBufferTmp;
renderingBufferTmp.attach(buf, width, height, row_len);
agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_bgra32());
}
agg::rect_i RendererAgg::get_content_extents()
{
agg::rect_i r(width, height, 0, 0);
// Looks at the alpha channel to find the minimum extents of the image
unsigned char *pixel = pixBuffer + 3;
for (int y = 0; y < (int)height; ++y) {
for (int x = 0; x < (int)width; ++x) {
if (*pixel) {
if (x < r.x1)
r.x1 = x;
if (y < r.y1)
r.y1 = y;
if (x > r.x2)
r.x2 = x;
if (y > r.y2)
r.y2 = y;
}
pixel += 4;
}
}
if (r.x1 == width && r.x2 == 0) {
// The buffer is completely empty.
r.x1 = r.y1 = r.x2 = r.y2 = 0;
} else {
r.x1 = std::max(0, r.x1);
r.y1 = std::max(0, r.y1);
r.x2 = std::min(r.x2 + 1, (int)width);
r.y2 = std::min(r.y2 + 1, (int)height);
}
return r;
}
void RendererAgg::clear()
{
//"clear the rendered buffer";
rendererBase.clear(_fill_color);
}