From 83c5385551c7ecff243356e555699cf0fdca7815 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 11 Nov 2014 14:28:26 -0500 Subject: [PATCH 1/2] Use floating-point rendering buffer --- src/_backend_agg.cpp | 72 +---------------------- src/_backend_agg.h | 109 +++++++++++++++++++++++++---------- src/_backend_agg_wrapper.cpp | 61 +++++++++++++++----- 3 files changed, 129 insertions(+), 113 deletions(-) diff --git a/src/_backend_agg.cpp b/src/_backend_agg.cpp index 36ff8a268da7..2e801c76da8e 100644 --- a/src/_backend_agg.cpp +++ b/src/_backend_agg.cpp @@ -6,31 +6,11 @@ #include "mplutils.h" #include "MPL_isnan.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), + NUMBYTES(width * height * sizeof(pixfmt::color_type)), pixBuffer(NULL), renderingBuffer(), alphaBuffer(NULL), @@ -50,7 +30,7 @@ RendererAgg::RendererAgg(unsigned int width, unsigned int height, double dpi) lastclippath(NULL), _fill_color(agg::rgba(1, 1, 1, 0)) { - unsigned stride(width * 4); + unsigned stride(width * sizeof(pixfmt::color_type)); pixBuffer = new agg::int8u[NUMBYTES]; renderingBuffer.attach(pixBuffer, width, height, stride); @@ -78,54 +58,6 @@ void RendererAgg::create_alpha_buffers() } } -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 "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 x, int y, int xx1, int yy1, int xx2, int yy2) -{ - if (region.get_data() == NULL) { - throw "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) { diff --git a/src/_backend_agg.h b/src/_backend_agg.h index 3672c93774f2..7ecdf5d5d7e7 100644 --- a/src/_backend_agg.h +++ b/src/_backend_agg.h @@ -43,6 +43,7 @@ // a helper class to pass agg::buffer objects around. agg::buffer is // a class in the swig wrapper +template class BufferRegion { public: @@ -50,7 +51,7 @@ class BufferRegion { width = r.x2 - r.x1; height = r.y2 - r.y1; - stride = width * 4; + stride = width * sizeof(typename PixFmt::color_type); data = new agg::int8u[stride * height]; } @@ -84,7 +85,13 @@ class BufferRegion return stride; } - void to_string_argb(uint8_t *buf); + void to_string_argb(uint8_t *buf) + { + agg::rendering_buffer src(data, width, height, width * sizeof(typename PixFmt::color_type)); + agg::rendering_buffer dst(buf, width, height, width * sizeof(agg::rgba8)); + + agg::convert(&dst, &src); + } private: agg::int8u *data; @@ -105,9 +112,8 @@ class BufferRegion class RendererAgg { public: - - typedef fixed_blender_rgba_plain fixed_blender_rgba32_plain; - typedef agg::pixfmt_alpha_blend_rgba pixfmt; + typedef agg::pixfmt_rgba128_plain pixfmt; + typedef typename pixfmt::color_type color_type; typedef agg::renderer_base renderer_base; typedef agg::renderer_scanline_aa_solid renderer_aa; typedef agg::renderer_scanline_bin_solid renderer_bin; @@ -211,9 +217,54 @@ class RendererAgg agg::rect_i get_content_extents(); void clear(); - BufferRegion *copy_from_bbox(agg::rect_d in_rect); - void restore_region(BufferRegion ®); - void restore_region(BufferRegion ®ion, int x, int y, int xx1, int yy1, int xx2, int yy2); + BufferRegion *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 restore_region(BufferRegion ®ion) + { + if (region.get_data() == NULL) { + throw "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); + } + + void + restore_region(BufferRegion ®ion, int x, int y, int xx1, int yy1, int xx2, int yy2) + { + if (region.get_data() == NULL) { + throw "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); + } unsigned int width, height; double dpi; @@ -392,7 +443,7 @@ RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, agg::wrap_mode_repeat_auto_pow2, agg::wrap_mode_repeat_auto_pow2> img_source_type; typedef agg::span_pattern_rgba span_gen_type; - agg::span_allocator sa; + agg::span_allocator sa; img_source_type img_src(hatch_img_pixf); span_gen_type sg(img_src, 0, 0); theRasterizer.add_path(path); @@ -551,10 +602,8 @@ inline void RendererAgg::draw_markers(GCAgg &gc, fillCache = new agg::int8u[fillSize]; } scanlines.serialize(fillCache); - marker_size = agg::rect_i(scanlines.min_x(), - scanlines.min_y(), - scanlines.max_x(), - scanlines.max_y()); + marker_size = agg::rect_i( + scanlines.min_x(), scanlines.min_y(), scanlines.max_x(), scanlines.max_y()); } stroke_t stroke(marker_path_curve); @@ -676,26 +725,25 @@ inline void RendererAgg::draw_markers(GCAgg &gc, * This is a custom span generator that converts spans in the * 8-bit inverted greyscale font buffer to rgba that agg can use. */ -template +template class font_to_rgba { public: typedef ChildGenerator child_type; - typedef agg::rgba8 color_type; typedef typename child_type::color_type child_color_type; typedef agg::span_allocator span_alloc_type; private: child_type *_gen; - color_type _color; + ColorType _color; span_alloc_type _allocator; public: - font_to_rgba(child_type *gen, color_type color) : _gen(gen), _color(color) + font_to_rgba(child_type *gen, ColorType color) : _gen(gen), _color(color) { } - inline void generate(color_type *output_span, int x, int y, unsigned len) + inline void generate(ColorType *output_span, int x, int y, unsigned len) { _allocator.allocate(len); child_color_type *input_span = _allocator.span(); @@ -703,7 +751,8 @@ class font_to_rgba do { *output_span = _color; - output_span->a = ((unsigned int)_color.a * (unsigned int)input_span->v) >> 8; + output_span->opacity(_color.opacity() * + input_span->to_double(input_span->v)); ++output_span; ++input_span; } while (--len); @@ -718,11 +767,11 @@ class font_to_rgba template inline void RendererAgg::draw_text_image(GCAgg &gc, ImageArray &image, int x, int y, double angle) { - typedef agg::span_allocator color_span_alloc_type; + typedef agg::span_allocator color_span_alloc_type; typedef agg::span_interpolator_linear<> interpolator_type; typedef agg::image_accessor_clip image_accessor_type; typedef agg::span_image_filter_gray image_span_gen_type; - typedef font_to_rgba span_gen_type; + typedef font_to_rgba span_gen_type; typedef agg::renderer_scanline_aa renderer_type; @@ -763,11 +812,10 @@ inline void RendererAgg::draw_text_image(GCAgg &gc, ImageArray &image, int x, in agg::render_scanlines(theRasterizer, slineP8, ri); } +template class span_conv_alpha { public: - typedef agg::rgba8 color_type; - double m_alpha; span_conv_alpha(double alpha) : m_alpha(alpha) @@ -835,18 +883,19 @@ inline void RendererAgg::draw_image(GCAgg &gc, agg::trans_affine inv_mtx(mtx); inv_mtx.invert(); - typedef agg::span_allocator color_span_alloc_type; + typedef agg::span_allocator color_span_alloc_type; typedef agg::image_accessor_clip image_accessor_type; typedef agg::span_interpolator_linear<> interpolator_type; typedef agg::span_image_filter_rgba_nn image_span_gen_type; - typedef agg::span_converter span_conv; + typedef agg::span_converter > + span_conv; color_span_alloc_type sa; - image_accessor_type ia(pixf, agg::rgba8(0, 0, 0, 0)); + image_accessor_type ia(pixf, pixfmt::color_type(0, 0, 0, 0)); interpolator_type interpolator(inv_mtx); image_span_gen_type image_span_generator(ia, interpolator); - span_conv_alpha conv_alpha(alpha); + span_conv_alpha conv_alpha(alpha); span_conv spans(image_span_generator, conv_alpha); if (has_clippath) { @@ -1200,7 +1249,7 @@ inline void RendererAgg::_draw_gouraud_triangle(PointArray &points, agg::trans_affine trans, bool has_clippath) { - typedef agg::rgba8 color_t; + typedef pixfmt::color_type color_t; typedef agg::span_gouraud_rgba span_gen_t; typedef agg::span_allocator span_alloc_t; @@ -1266,7 +1315,7 @@ inline void RendererAgg::draw_gouraud_triangle(GCAgg &gc, throw "colors must be a 3x4 array"; } - _draw_gouraud_triangle(points, colors, trans, has_clippath); + // _draw_gouraud_triangle(points, colors, trans, has_clippath); } template @@ -1296,7 +1345,7 @@ inline void RendererAgg::draw_gouraud_triangles(GCAgg &gc, typename PointArray::sub_t point = points[i]; typename ColorArray::sub_t color = colors[i]; - _draw_gouraud_triangle(point, color, trans, has_clippath); + // _draw_gouraud_triangle(point, color, trans, has_clippath); } } diff --git a/src/_backend_agg_wrapper.cpp b/src/_backend_agg_wrapper.cpp index 753afe3837d2..a0d05883dbf5 100644 --- a/src/_backend_agg_wrapper.cpp +++ b/src/_backend_agg_wrapper.cpp @@ -6,6 +6,7 @@ typedef struct { PyObject_HEAD; RendererAgg *x; + uint8_t *tmp_buffer; Py_ssize_t shape[3]; Py_ssize_t strides[3]; Py_ssize_t suboffsets[3]; @@ -14,13 +15,12 @@ typedef struct typedef struct { PyObject_HEAD; - BufferRegion *x; + BufferRegion *x; Py_ssize_t shape[3]; Py_ssize_t strides[3]; Py_ssize_t suboffsets[3]; } PyBufferRegion; - /********************************************************************** * BufferRegion * */ @@ -158,6 +158,7 @@ static PyObject *PyRendererAgg_new(PyTypeObject *type, PyObject *args, PyObject PyRendererAgg *self; self = (PyRendererAgg *)type->tp_alloc(type, 0); self->x = NULL; + self->tmp_buffer = NULL; return (PyObject *)self; } @@ -185,6 +186,7 @@ static int PyRendererAgg_init(PyRendererAgg *self, PyObject *args, PyObject *kwd static void PyRendererAgg_dealloc(PyRendererAgg *self) { delete self->x; + delete self->tmp_buffer; Py_TYPE(self)->tp_free((PyObject *)self); } @@ -551,21 +553,54 @@ PyRendererAgg_get_content_extents(PyRendererAgg *self, PyObject *args, PyObject static PyObject *PyRendererAgg_buffer_rgba(PyRendererAgg *self, PyObject *args, PyObject *kwds) { -#if PY3K - return PyBytes_FromStringAndSize((const char *)self->x->pixBuffer, - self->x->get_width() * self->x->get_height() * 4); -#else - return PyBuffer_FromReadWriteMemory(self->x->pixBuffer, - self->x->get_width() * self->x->get_height() * 4); -#endif + PyObject *result; + unsigned char *output_buffer; + + result = PyBytes_FromStringAndSize(NULL, self->x->get_width() * self->x->get_height() * 4); + if (result == NULL) { + return NULL; + } + + output_buffer = (unsigned char *)PyBytes_AsString(result); + + agg::rendering_buffer dst(output_buffer, + self->x->get_width(), + self->x->get_height(), + self->x->get_width() * sizeof(agg::rgba8)); + + agg::convert(&dst, &self->x->renderingBuffer); + + return result; + + // Below is the fast path if we are already in rgba8 + + // #if PY3K + // return PyBytes_FromStringAndSize((const char *)self->x->pixBuffer, + // self->x->get_width() * self->x->get_height() * 4); + // #else + // return PyBuffer_FromReadWriteMemory(self->x->pixBuffer, + // self->x->get_width() * self->x->get_height() * 4); + // #endif } int PyRendererAgg_get_buffer(PyRendererAgg *self, Py_buffer *buf, int flags) { Py_INCREF(self); + if (self->tmp_buffer == NULL) { + self->tmp_buffer = + new uint8_t[self->x->get_width() * self->x->get_height() * sizeof(agg::rgba8)]; + } + + agg::rendering_buffer dst(self->tmp_buffer, + self->x->get_width(), + self->x->get_height(), + self->x->get_width() * sizeof(agg::rgba8)); + + agg::convert(&dst, &self->x->renderingBuffer); + buf->obj = (PyObject *)self; - buf->buf = self->x->pixBuffer; - buf->len = self->x->get_width() * self->x->get_height() * 4; + buf->buf = self->tmp_buffer; + buf->len = self->x->get_width() * self->x->get_height() * sizeof(agg::rgba8); buf->readonly = 0; buf->format = (char *)"B"; buf->ndim = 3; @@ -573,7 +608,7 @@ int PyRendererAgg_get_buffer(PyRendererAgg *self, Py_buffer *buf, int flags) self->shape[1] = self->x->get_width(); self->shape[2] = 4; buf->shape = self->shape; - self->strides[0] = self->x->get_width() * 4; + self->strides[0] = self->x->get_width() * sizeof(agg::rgba8); self->strides[1] = 4; self->strides[2] = 1; buf->strides = self->strides; @@ -594,7 +629,7 @@ static PyObject *PyRendererAgg_clear(PyRendererAgg *self, PyObject *args, PyObje static PyObject *PyRendererAgg_copy_from_bbox(PyRendererAgg *self, PyObject *args, PyObject *kwds) { agg::rect_d bbox; - BufferRegion *reg; + BufferRegion *reg; PyObject *regobj; if (!PyArg_ParseTuple(args, "O&:copy_from_bbox", &convert_rect, &bbox)) { From 15d1e439648808e36f13ce68c891949386df5539 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 11 Nov 2014 14:50:05 -0500 Subject: [PATCH 2/2] More floating-point buffer progress --- src/_backend_agg.cpp | 16 ++++++++++------ src/_backend_agg.h | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/_backend_agg.cpp b/src/_backend_agg.cpp index 2e801c76da8e..b69bb5082143 100644 --- a/src/_backend_agg.cpp +++ b/src/_backend_agg.cpp @@ -39,7 +39,8 @@ RendererAgg::RendererAgg(unsigned int width, unsigned int height, double dpi) rendererBase.clear(_fill_color); rendererAA.attach(rendererBase); rendererBin.attach(rendererBase); - hatchRenderingBuffer.attach(hatchBuffer, HATCH_SIZE, HATCH_SIZE, HATCH_SIZE * 4); + hatchRenderingBuffer.attach( + hatchBuffer, HATCH_SIZE, HATCH_SIZE, HATCH_SIZE * sizeof(color_type)); } RendererAgg::~RendererAgg() @@ -95,34 +96,37 @@ void RendererAgg::tostring_rgb(uint8_t *buf) agg::rendering_buffer renderingBufferTmp; renderingBufferTmp.attach(buf, width, height, row_len); - agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_rgb24()); + agg::convert(&renderingBufferTmp, &renderingBuffer); } void RendererAgg::tostring_argb(uint8_t *buf) { //"Return the rendered buffer as an RGB string"; - int row_len = width * 4; + int row_len = width * sizeof(agg::rgba8); agg::rendering_buffer renderingBufferTmp; renderingBufferTmp.attach(buf, width, height, row_len); - agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_argb32()); + + agg::convert(&renderingBufferTmp, &renderingBuffer); } void RendererAgg::tostring_bgra(uint8_t *buf) { //"Return the rendered buffer as an RGB string"; - int row_len = width * 4; + int row_len = width * sizeof(agg::rgba8); agg::rendering_buffer renderingBufferTmp; renderingBufferTmp.attach(buf, width, height, row_len); - agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_bgra32()); + agg::convert(&renderingBufferTmp, &renderingBuffer); } agg::rect_i RendererAgg::get_content_extents() { + // TODO: This is most definitely broken + agg::rect_i r(width, height, 0, 0); // Looks at the alpha channel to find the minimum extents of the image diff --git a/src/_backend_agg.h b/src/_backend_agg.h index 7ecdf5d5d7e7..064f3f898b50 100644 --- a/src/_backend_agg.h +++ b/src/_backend_agg.h @@ -15,6 +15,7 @@ #include "agg_image_accessors.h" #include "agg_pixfmt_amask_adaptor.h" #include "agg_pixfmt_gray.h" +#include "agg_pixfmt_rgb.h" #include "agg_pixfmt_rgba.h" #include "agg_rasterizer_scanline_aa.h" #include "agg_renderer_base.h" @@ -293,7 +294,7 @@ class RendererAgg agg::trans_affine lastclippath_transform; static const size_t HATCH_SIZE = 72; - agg::int8u hatchBuffer[HATCH_SIZE * HATCH_SIZE * 4]; + agg::int8u hatchBuffer[HATCH_SIZE * HATCH_SIZE * sizeof(color_type)]; agg::rendering_buffer hatchRenderingBuffer; agg::rgba _fill_color;