diff --git a/.travis.yml b/.travis.yml
index e60e8abb6d0c..e7445680b4e6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -24,8 +24,8 @@ matrix:
install:
- pip install -q --use-mirrors nose python-dateutil $NUMPY pep8 pyparsing pillow
- - sudo apt-get update && sudo apt-get -qq install inkscape libav-tools
- # We use --no-install-recommends to avoid pulling in additional large latex docs that we don't need
+ - sudo apt-get update && sudo apt-get -qq install inkscape libav-tools gdb
+ # We use --no-install-recommends to avoid pulling in additional large latex docs that we don't need
- if [[ $BUILD_DOCS == true ]]; then sudo apt-get install -qq --no-install-recommends dvipng texlive-latex-base texlive-latex-extra texlive-fonts-recommended graphviz; fi
- if [[ $BUILD_DOCS == true ]]; then pip install sphinx numpydoc linkchecker; fi
- python setup.py install
@@ -37,11 +37,12 @@ script:
- echo Testing using 8 processes
# Generate the font caches in a single process before starting the
# multiple processes
+ - gcc --version
- python -c "from matplotlib import font_manager"
- if [[ $BUILD_DOCS == false ]]; then export MPL_REPO_DIR=$PWD; fi # pep8-conformance test of the examples
- if [[ $BUILD_DOCS == false ]]; then mkdir ../tmp_test_dir; fi
- if [[ $BUILD_DOCS == false ]]; then cd ../tmp_test_dir; fi
- - if [[ $BUILD_DOCS == false ]]; then python ../matplotlib/tests.py -sv --processes=8 --process-timeout=300 $TEST_ARGS; fi
+ - if [[ $BUILD_DOCS == false ]]; then gdb -return-child-result -batch -ex r -ex bt --args python ../matplotlib/tests.py -sv --processes=8 --process-timeout=300 $TEST_ARGS; fi
- if [[ $BUILD_DOCS == true ]]; then cd doc; python make.py html --small; fi
# We don't build the LaTeX docs here, so linkchecker will complain
- if [[ $BUILD_DOCS == true ]]; then touch build/html/Matplotlib.pdf; fi
diff --git a/examples/pylab_examples/agg_buffer_to_array.py b/examples/pylab_examples/agg_buffer_to_array.py
index a52434b282ff..acbe2333a319 100644
--- a/examples/pylab_examples/agg_buffer_to_array.py
+++ b/examples/pylab_examples/agg_buffer_to_array.py
@@ -8,12 +8,7 @@
fig.canvas.draw()
# grab the pixel buffer and dump it into a numpy array
-buf = fig.canvas.buffer_rgba()
-l, b, w, h = fig.bbox.bounds
-# The array needs to be copied, because the underlying buffer
-# may be reallocated when the window is resized.
-X = np.frombuffer(buf, np.uint8).copy()
-X.shape = h,w,4
+X = np.array(fig.canvas.renderer._renderer)
# now display the array X as an Axes in a new figure
fig2 = plt.figure()
diff --git a/examples/pylab_examples/mathtext_demo.py b/examples/pylab_examples/mathtext_demo.py
old mode 100644
new mode 100755
index 2947672e255c..98a9c73c2f70
--- a/examples/pylab_examples/mathtext_demo.py
+++ b/examples/pylab_examples/mathtext_demo.py
@@ -24,5 +24,4 @@
ax.set_title(r'$\Delta_i^j \hspace{0.4} \mathrm{versus} \hspace{0.4} \Delta_{i+1}^j$', fontsize=20)
-
show()
diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py
index 66e2eb5e83c5..5978886fa127 100644
--- a/lib/matplotlib/__init__.py
+++ b/lib/matplotlib/__init__.py
@@ -1405,6 +1405,13 @@ def tk_window_focus():
def test(verbosity=1):
"""run the matplotlib test suite"""
+ try:
+ import faulthandler
+ except ImportError:
+ pass
+ else:
+ faulthandler.enable()
+
old_backend = rcParams['backend']
try:
use('agg')
diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py
index 9d93e22e0641..91dfc0478ae4 100644
--- a/lib/matplotlib/axes/_base.py
+++ b/lib/matplotlib/axes/_base.py
@@ -2063,8 +2063,8 @@ def draw(self, renderer=None, inframe=False):
for z, im in zorder_images]
l, b, r, t = self.bbox.extents
- width = mag * ((round(r) + 0.5) - (round(l) - 0.5))
- height = mag * ((round(t) + 0.5) - (round(b) - 0.5))
+ width = int(mag * ((round(r) + 0.5) - (round(l) - 0.5)))
+ height = int(mag * ((round(t) + 0.5) - (round(b) - 0.5)))
im = mimage.from_images(height,
width,
ims)
diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py
index 4cb14a6fe130..c7a59be98ce8 100644
--- a/lib/matplotlib/backends/backend_agg.py
+++ b/lib/matplotlib/backends/backend_agg.py
@@ -127,15 +127,19 @@ def draw_path_collection(self, *kl, **kw):
return self._renderer.draw_path_collection(*kl, **kw)
def _update_methods(self):
- #self.draw_path = self._renderer.draw_path # see below
- #self.draw_markers = self._renderer.draw_markers
- #self.draw_path_collection = self._renderer.draw_path_collection
self.draw_quad_mesh = self._renderer.draw_quad_mesh
self.draw_gouraud_triangle = self._renderer.draw_gouraud_triangle
self.draw_gouraud_triangles = self._renderer.draw_gouraud_triangles
self.draw_image = self._renderer.draw_image
self.copy_from_bbox = self._renderer.copy_from_bbox
- self.tostring_rgba_minimized = self._renderer.tostring_rgba_minimized
+ self.get_content_extents = self._renderer.get_content_extents
+
+ def tostring_rgba_minimized(self):
+ extents = self.get_content_extents()
+ bbox = [[extents[0], self.height - (extents[1] + extents[3])],
+ [extents[0] + extents[2], self.height - extents[1]]]
+ region = self.copy_from_bbox(bbox)
+ return np.array(region), extents
def draw_path(self, gc, path, transform, rgbFace=None):
"""
@@ -203,7 +207,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
#print x, y, int(x), int(y), s
self._renderer.draw_text_image(
- font.get_image(), np.round(x - xd), np.round(y + yd) + 1, angle, gc)
+ font, np.round(x - xd), np.round(y + yd) + 1, angle, gc)
def get_text_width_height_descent(self, s, prop, ismath):
"""
@@ -354,7 +358,7 @@ def restore_region(self, region, bbox=None, xy=None):
else:
ox, oy = xy
- self._renderer.restore_region2(region, x1, y1, x2, y2, ox, oy)
+ self._renderer.restore_region(region, x1, y1, x2, y2, ox, oy)
else:
self._renderer.restore_region(region)
@@ -394,7 +398,7 @@ def post_processing(image, dpi):
width, height = int(self.width), int(self.height)
- buffer, bounds = self._renderer.tostring_rgba_minimized()
+ buffer, bounds = self.tostring_rgba_minimized()
l, b, w, h = bounds
@@ -407,7 +411,6 @@ def post_processing(image, dpi):
img, ox, oy = post_processing(img.reshape((h, w, 4)) / 255.,
self.dpi)
image = fromarray(img, 1)
- image.flipud_out()
gc = self.new_gc()
self._renderer.draw_image(gc,
@@ -505,12 +508,13 @@ def print_raw(self, filename_or_obj, *args, **kwargs):
original_dpi = renderer.dpi
renderer.dpi = self.figure.dpi
if is_string_like(filename_or_obj):
- filename_or_obj = open(filename_or_obj, 'wb')
+ fileobj = open(filename_or_obj, 'wb')
close = True
else:
+ fileobj = filename_or_obj
close = False
try:
- renderer._renderer.write_rgba(filename_or_obj)
+ fileobj.write(renderer._renderer.buffer_rgba())
finally:
if close:
filename_or_obj.close()
@@ -528,9 +532,7 @@ def print_png(self, filename_or_obj, *args, **kwargs):
else:
close = False
try:
- _png.write_png(renderer._renderer.buffer_rgba(),
- renderer.width, renderer.height,
- filename_or_obj, self.figure.dpi)
+ _png.write_png(renderer._renderer, filename_or_obj, self.figure.dpi)
finally:
if close:
filename_or_obj.close()
diff --git a/lib/matplotlib/backends/backend_cairo.py b/lib/matplotlib/backends/backend_cairo.py
index 952b879d2c7e..4af8562a0940 100644
--- a/lib/matplotlib/backends/backend_cairo.py
+++ b/lib/matplotlib/backends/backend_cairo.py
@@ -167,8 +167,6 @@ def draw_image(self, gc, x, y, im):
# bbox - not currently used
if _debug: print('%s.%s()' % (self.__class__.__name__, _fn_name()))
- im.flipud_out()
-
rows, cols, buf = im.color_conv (BYTE_FORMAT)
surface = cairo.ImageSurface.create_for_data (
buf, cairo.FORMAT_ARGB32, cols, rows, cols*4)
@@ -183,8 +181,6 @@ def draw_image(self, gc, x, y, im):
ctx.paint()
ctx.restore()
- im.flipud_out()
-
def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
# Note: x,y are device/display coords, not user-coords, unlike other
# draw_* methods
diff --git a/lib/matplotlib/backends/backend_gdk.py b/lib/matplotlib/backends/backend_gdk.py
index fc705febeb59..d3e8e5bb1202 100644
--- a/lib/matplotlib/backends/backend_gdk.py
+++ b/lib/matplotlib/backends/backend_gdk.py
@@ -109,7 +109,6 @@ def draw_image(self, gc, x, y, im):
# int(w), int(h))
# set clip rect?
- im.flipud_out()
rows, cols, image_str = im.as_rgba_str()
image_array = np.fromstring(image_str, np.uint8)
@@ -120,7 +119,7 @@ def draw_image(self, gc, x, y, im):
width=cols, height=rows)
array = pixbuf_get_pixels_array(pixbuf)
- array[:,:,:] = image_array
+ array[:,:,:] = image_array[::-1]
gc = self.new_gc()
@@ -138,9 +137,6 @@ def draw_image(self, gc, x, y, im):
int(x), int(y), cols, rows,
gdk.RGB_DITHER_NONE, 0, 0)
- # unflip
- im.flipud_out()
-
def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
x, y = int(x), int(y)
diff --git a/lib/matplotlib/backends/backend_macosx.py b/lib/matplotlib/backends/backend_macosx.py
index 371f687b1190..43434e374e1d 100755
--- a/lib/matplotlib/backends/backend_macosx.py
+++ b/lib/matplotlib/backends/backend_macosx.py
@@ -110,10 +110,8 @@ def get_image_magnification(self):
return self.gc.get_image_magnification()
def draw_image(self, gc, x, y, im):
- im.flipud_out()
nrows, ncols, data = im.as_rgba_str()
gc.draw_image(x, y, nrows, ncols, data)
- im.flipud_out()
def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None):
# todo, handle props, angle, origins
diff --git a/lib/matplotlib/backends/backend_mixed.py b/lib/matplotlib/backends/backend_mixed.py
index 85b3c42c5368..ee48f9274bbe 100644
--- a/lib/matplotlib/backends/backend_mixed.py
+++ b/lib/matplotlib/backends/backend_mixed.py
@@ -121,7 +121,6 @@ def stop_rasterizing(self):
if w > 0 and h > 0:
image = frombuffer(buffer, w, h, True)
image.is_grayscale = False
- image.flipud_out()
gc = self._renderer.new_gc()
# TODO: If the mixedmode resolution differs from the figure's
# dpi, the image must be scaled (dpi->_figdpi). Not all
diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py
index 109f1ef92124..933e84b22988 100644
--- a/lib/matplotlib/backends/backend_pdf.py
+++ b/lib/matplotlib/backends/backend_pdf.py
@@ -1241,6 +1241,7 @@ def _rgb(self, im):
rgba = np.fromstring(s, np.uint8)
rgba.shape = (h, w, 4)
+ rgba = rgba[::-1]
rgb = rgba[:, :, :3]
a = rgba[:, :, 3:]
return h, w, rgb.tostring(), a.tostring()
@@ -1249,6 +1250,7 @@ def _gray(self, im, rc=0.3, gc=0.59, bc=0.11):
rgbat = im.as_rgba_str()
rgba = np.fromstring(rgbat[2], np.uint8)
rgba.shape = (rgbat[0], rgbat[1], 4)
+ rgba = rgba[::-1]
rgba_f = rgba.astype(np.float32)
r = rgba_f[:, :, 0]
g = rgba_f[:, :, 1]
@@ -1258,7 +1260,6 @@ def _gray(self, im, rc=0.3, gc=0.59, bc=0.11):
def writeImages(self):
for img, pair in six.iteritems(self.images):
- img.flipud_out()
if img.is_grayscale:
height, width, data = self._gray(img)
self.beginStream(
@@ -1294,8 +1295,6 @@ def writeImages(self):
self.currentstream.write(data)
self.endStream()
- img.flipud_out()
-
def markerObject(self, path, trans, fillp, strokep, lw, joinstyle,
capstyle):
"""Return name of a marker XObject representing the given path."""
diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py
index 6ae2867b00a9..e432a340d803 100644
--- a/lib/matplotlib/backends/backend_pgf.py
+++ b/lib/matplotlib/backends/backend_pgf.py
@@ -14,6 +14,8 @@
import weakref
import warnings
+import numpy as np
+
import matplotlib as mpl
from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\
FigureManagerBase, FigureCanvasBase
@@ -619,9 +621,7 @@ def draw_image(self, gc, x, y, im):
fname = os.path.splitext(os.path.basename(self.fh.name))[0]
fname_img = "%s-img%d.png" % (fname, self.image_counter)
self.image_counter += 1
- im.flipud_out()
- rows, cols, buf = im.as_rgba_str()
- _png.write_png(buf, cols, rows, os.path.join(path, fname_img))
+ _png.write_png(np.array(im)[::-1], os.path.join(path, fname_img))
# reference the image in the pgf picture
writeln(self.fh, r"\begin{pgfscope}")
diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py
index 0d14962ad85e..48c5bdd6cae9 100644
--- a/lib/matplotlib/backends/backend_ps.py
+++ b/lib/matplotlib/backends/backend_ps.py
@@ -414,13 +414,14 @@ def _rgb(self, im):
rgba = np.fromstring(s, np.uint8)
rgba.shape = (h, w, 4)
- rgb = rgba[:,:,:3]
+ rgb = rgba[::-1,:,:3]
return h, w, rgb.tostring()
def _gray(self, im, rc=0.3, gc=0.59, bc=0.11):
rgbat = im.as_rgba_str()
rgba = np.fromstring(rgbat[2], np.uint8)
rgba.shape = (rgbat[0], rgbat[1], 4)
+ rgba = rgba[::-1]
rgba_f = rgba.astype(np.float32)
r = rgba_f[:,:,0]
g = rgba_f[:,:,1]
@@ -472,8 +473,6 @@ def draw_image(self, gc, x, y, im, dx=None, dy=None, transform=None):
interpreted as the coordinate of the transform.
"""
- im.flipud_out()
-
h, w, bits, imagecmd = self._get_image_h_w_bits_command(im)
hexlines = b'\n'.join(self._hex_lines(bits)).decode('ascii')
@@ -524,9 +523,6 @@ def draw_image(self, gc, x, y, im, dx=None, dy=None, transform=None):
""" % locals()
self._pswriter.write(ps)
- # unflip
- im.flipud_out()
-
def _convert_path(self, path, transform, clip=False, simplify=None):
ps = []
last_points = None
diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py
index 89e4d5986fb8..5b4710142571 100644
--- a/lib/matplotlib/backends/backend_svg.py
+++ b/lib/matplotlib/backends/backend_svg.py
@@ -800,10 +800,7 @@ def draw_image(self, gc, x, y, im, dx=None, dy=None, transform=None):
self.writer.start('a', attrib={'xlink:href': url})
if rcParams['svg.image_inline']:
bytesio = io.BytesIO()
- im.flipud_out()
- rows, cols, buffer = im.as_rgba_str()
- _png.write_png(buffer, cols, rows, bytesio)
- im.flipud_out()
+ _png.write_png(np.array(im)[::-1], bytesio)
oid = oid or self._make_id('image', bytesio)
attrib['xlink:href'] = (
"data:image/png;base64,\n" +
@@ -812,10 +809,7 @@ def draw_image(self, gc, x, y, im, dx=None, dy=None, transform=None):
self._imaged[self.basename] = self._imaged.get(self.basename,0) + 1
filename = '%s.image%d.png'%(self.basename, self._imaged[self.basename])
verbose.report( 'Writing image file for inclusion: %s' % filename)
- im.flipud_out()
- rows, cols, buffer = im.as_rgba_str()
- _png.write_png(buffer, cols, rows, filename)
- im.flipud_out()
+ _png.write_png(np.array(im)[::-1], filename)
oid = oid or 'Im_' + self._make_id('image', filename)
attrib['xlink:href'] = filename
diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py
index 6cf0a592fc8c..4971b336671c 100644
--- a/lib/matplotlib/backends/backend_webagg_core.py
+++ b/lib/matplotlib/backends/backend_webagg_core.py
@@ -143,8 +143,7 @@ def get_diff_image(self):
# TODO: We should write a new version of write_png that
# handles the differencing inline
_png.write_png(
- output.tostring(),
- output.shape[1], output.shape[0],
+ output,
self._png_buffer)
# Swap the renderer frames
diff --git a/lib/matplotlib/delaunay/_delaunay.cpp b/lib/matplotlib/delaunay/_delaunay.cpp
index 452e415d149d..12e60c837166 100644
--- a/lib/matplotlib/delaunay/_delaunay.cpp
+++ b/lib/matplotlib/delaunay/_delaunay.cpp
@@ -6,7 +6,7 @@
#include "VoronoiDiagramGenerator.h"
#include "delaunay_utils.h"
#include "natneighbors.h"
-#include "numpy/noprefix.h"
+#include "numpy/ndarrayobject.h"
// support numpy 1.6 - this macro got renamed and deprecated at once in 1.7
#ifndef NPY_ARRAY_IN_ARRAY
@@ -123,7 +123,7 @@ static PyObject* getMesh(int npoints, double *x, double *y)
int tri0, tri1, reg0, reg1;
double tri0x, tri0y, tri1x, tri1y;
int length, numtri, i, j;
- intp dim[MAX_DIMS];
+ npy_intp dim[NPY_MAXDIMS];
int *edge_db_ptr, *tri_edges_ptr, *tri_nbrs_ptr;
double *vertices_ptr;
VoronoiDiagramGenerator vdg;
@@ -221,7 +221,7 @@ static PyObject* getMesh(int npoints, double *x, double *y)
static PyObject *linear_planes(int ntriangles, double *x, double *y, double *z,
int *nodes)
{
- intp dims[2];
+ npy_intp dims[2];
PyObject *planes;
int i;
double *planes_ptr;
@@ -286,7 +286,7 @@ static PyObject *linear_interpolate_grid(double x0, double x1, int xsteps,
int rowtri, coltri, tri;
PyObject *z;
double *z_ptr;
- intp dims[2];
+ npy_intp dims[2];
dims[0] = ysteps;
dims[1] = xsteps;
@@ -596,7 +596,7 @@ static PyObject *nn_interpolate_method(PyObject *self, PyObject *args)
double x0, x1, y0, y1, defvalue;
int xsteps, ysteps;
int npoints, ntriangles;
- intp dims[2];
+ npy_intp dims[2];
if (!PyArg_ParseTuple(args, "ddiddidOOOOOO", &x0, &x1, &xsteps,
&y0, &y1, &ysteps, &defvalue, &pyx, &pyy, &pyz, &pycenters, &pynodes,
diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py
index b334ca4edf88..5d75bb8f5bb0 100644
--- a/lib/matplotlib/figure.py
+++ b/lib/matplotlib/figure.py
@@ -1045,8 +1045,8 @@ def draw(self, renderer):
ims = [(im.make_image(mag), im.ox, im.oy, im.get_alpha())
for im in self.images]
- im = _image.from_images(self.bbox.height * mag,
- self.bbox.width * mag,
+ im = _image.from_images(int(self.bbox.height * mag),
+ int(self.bbox.width * mag),
ims)
im.is_grayscale = False
diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py
index be7e94d5d36c..9dd9599a67f7 100644
--- a/lib/matplotlib/image.py
+++ b/lib/matplotlib/image.py
@@ -204,15 +204,19 @@ def _get_unsampled_image(self, A, image_extents, viewlim):
self._oldyslice = yslice
if self._imcache is None:
- if self._A.dtype == np.uint8 and self._A.ndim == 3:
- im = _image.frombyte(self._A[yslice, xslice, :], 0)
+ A = self._A
+ if self.origin == 'upper':
+ A = A[::-1]
+
+ if A.dtype == np.uint8 and A.ndim == 3:
+ im = _image.frombyte(A[yslice, xslice, :], 0)
im.is_grayscale = False
else:
if self._rgbacache is None:
- x = self.to_rgba(self._A, bytes=False)
+ x = self.to_rgba(A, bytes=False)
# Avoid side effects: to_rgba can return its argument
# unchanged.
- if np.may_share_memory(x, self._A):
+ if np.may_share_memory(x, A):
x = x.copy()
# premultiply the colors
x[..., 0:3] *= x[..., 3:4]
@@ -226,9 +230,6 @@ def _get_unsampled_image(self, A, image_extents, viewlim):
else:
im.is_grayscale = False
self._imcache = im
-
- if self.origin == 'upper':
- im.flipud_in()
else:
im = self._imcache
@@ -413,9 +414,7 @@ def write_png(self, fname, noscale=False):
im.reset_matrix()
im.set_interpolation(0)
im.resize(numcols, numrows)
- im.flipud_out()
- rows, cols, buffer = im.as_rgba_str()
- _png.write_png(buffer, cols, rows, fname)
+ _png.write_png(im, fname)
def set_data(self, A):
"""
@@ -732,7 +731,7 @@ def make_image(self, magnification=1.0):
width *= magnification
height *= magnification
im = _image.pcolor(self._Ax, self._Ay, A,
- height, width,
+ int(height), int(width),
(x0, x0+v_width, y0, y0+v_height),
self._interpd[self._interpolation])
@@ -1005,7 +1004,11 @@ def make_image(self, magnification=1.0):
if self._A is None:
raise RuntimeError('You must first set the image array')
- x = self.to_rgba(self._A, bytes=True)
+ A = self._A
+ if self.origin == 'upper':
+ A = A[::-1]
+
+ x = self.to_rgba(A, bytes=True)
self.magnification = magnification
# if magnification is not one, we need to resize
ismag = magnification != 1
@@ -1018,7 +1021,7 @@ def make_image(self, magnification=1.0):
fc = self.figure.get_facecolor()
im.set_bg(*mcolors.colorConverter.to_rgba(fc, 0))
im.is_grayscale = (self.cmap.name == "gray" and
- len(self._A.shape) == 2)
+ len(A.shape) == 2)
if ismag:
numrows, numcols = self.get_size()
@@ -1026,8 +1029,6 @@ def make_image(self, magnification=1.0):
numcols *= magnification
im.set_interpolation(_image.NEAREST)
im.resize(numcols, numrows)
- if self.origin == 'upper':
- im.flipud_out()
return im
@@ -1047,8 +1048,7 @@ def draw(self, renderer, *args, **kwargs):
def write_png(self, fname):
"""Write the image to png file with fname"""
im = self.make_image()
- rows, cols, buffer = im.as_rgba_str()
- _png.write_png(buffer, cols, rows, fname)
+ _png.write_png(im, fname)
class BboxImage(_AxesImageBase):
@@ -1131,24 +1131,24 @@ def make_image(self, renderer, magnification=1.0):
'array or the image attribute')
if self._imcache is None:
- if self._A.dtype == np.uint8 and len(self._A.shape) == 3:
- im = _image.frombyte(self._A, 0)
+ A = self._A
+ if self.origin == 'upper':
+ A = A[::-1]
+ if A.dtype == np.uint8 and len(A.shape) == 3:
+ im = _image.frombyte(A, 0)
im.is_grayscale = False
else:
if self._rgbacache is None:
- x = self.to_rgba(self._A, bytes=True)
+ x = self.to_rgba(A, bytes=True)
self._rgbacache = x
else:
x = self._rgbacache
im = _image.frombyte(x, 0)
- if len(self._A.shape) == 2:
+ if len(A.shape) == 2:
im.is_grayscale = self.cmap.is_gray()
else:
im.is_grayscale = False
self._imcache = im
-
- if self.origin == 'upper':
- im.flipud_in()
else:
im = self._imcache
diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py
index 388e209ec32f..ce0807376ec1 100644
--- a/lib/matplotlib/mathtext.py
+++ b/lib/matplotlib/mathtext.py
@@ -3092,8 +3092,7 @@ def to_png(self, filename, texstr, color='black', dpi=120, fontsize=14):
image in pixels.
"""
rgba, depth = self.to_rgba(texstr, color=color, dpi=dpi, fontsize=fontsize)
- numrows, numcols, tmp = rgba.shape
- _png.write_png(rgba.tostring(), numcols, numrows, filename)
+ _png.write_png(rgba, filename)
return depth
def get_depth(self, texstr, dpi=120, fontsize=14):
diff --git a/lib/matplotlib/testing/compare.py b/lib/matplotlib/testing/compare.py
index ecce3e637324..e02f3c2f3c34 100644
--- a/lib/matplotlib/testing/compare.py
+++ b/lib/matplotlib/testing/compare.py
@@ -375,4 +375,4 @@ def save_diff_image(expected, actual, output):
# Hard-code the alpha channel to fully solid
save_image_np[:, :, 3] = 255
- _png.write_png(save_image_np.tostring(), width, height, output)
+ _png.write_png(save_image_np, output)
diff --git a/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.pdf b/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.pdf
index dcc3dc53d8cf..583715ea662f 100644
Binary files a/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.pdf and b/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.pdf differ
diff --git a/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.svg b/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.svg
index efa15965ac90..b6629abd2c76 100644
--- a/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.svg
+++ b/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.svg
@@ -29,7 +29,7 @@ z
" style="fill:#ffffff;"/>
-
@@ -45,8 +45,8 @@ L86.0824 7.2
z
" style="fill:#ffffff;"/>
-
+
diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py
index aa9f9a7d703f..cc457d49a38a 100644
--- a/lib/matplotlib/tests/test_axes.py
+++ b/lib/matplotlib/tests/test_axes.py
@@ -4,6 +4,8 @@
import six
from six.moves import xrange
+import io
+
from nose.tools import assert_equal, assert_raises
import datetime
@@ -470,7 +472,7 @@ def __init__(self, x, y):
data = np.arange(200.)/200.
data.shape = 2, 100
x, y = data
- hb = ax.hexbin(x, y, extent=[.1, .3, .6, .7], picker=1)
+ hb = ax.hexbin(x, y, extent=[.1, .3, .6, .7], picker=-1)
assert hb.contains(FauxMouseEvent(400, 300))[0]
@@ -3392,11 +3394,13 @@ def test_margins():
@cleanup
def test_pathological_hexbin():
# issue #2863
+ out = io.BytesIO()
+
with warnings.catch_warnings(record=True) as w:
mylist = [10] * 100
fig, ax = plt.subplots(1, 1)
ax.hexbin(mylist, mylist)
- plt.show()
+ fig.savefig(out)
assert_equal(len(w), 0)
diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py
index 2589fa0e2ac8..00e89629e268 100644
--- a/lib/matplotlib/transforms.py
+++ b/lib/matplotlib/transforms.py
@@ -662,7 +662,7 @@ def count_overlaps(self, bboxes):
bboxes is a sequence of :class:`BboxBase` objects
"""
- return count_bboxes_overlapping_bbox(self, bboxes)
+ return count_bboxes_overlapping_bbox(self, [np.array(x) for x in bboxes])
def expanded(self, sw, sh):
"""
@@ -1668,7 +1668,7 @@ def transform_affine(self, points):
def transform_point(self, point):
mtx = self.get_matrix()
- return affine_transform(point, mtx)
+ return affine_transform([point], mtx)[0]
transform_point.__doc__ = AffineBase.transform_point.__doc__
if DEBUG:
diff --git a/lib/matplotlib/tri/_tri.cpp b/lib/matplotlib/tri/_tri.cpp
index 39bd98ba3cbf..94dc2eb3e975 100644
--- a/lib/matplotlib/tri/_tri.cpp
+++ b/lib/matplotlib/tri/_tri.cpp
@@ -234,13 +234,13 @@ Triangulation::Triangulation(PyArrayObject* x,
_edges(edges),
_neighbors(neighbors)
{
- _VERBOSE("Triangulation::Triangulation");
+
correct_triangles();
}
Triangulation::~Triangulation()
{
- _VERBOSE("Triangulation::~Triangulation");
+
Py_XDECREF(_x);
Py_XDECREF(_y);
Py_XDECREF(_triangles);
@@ -251,7 +251,7 @@ Triangulation::~Triangulation()
void Triangulation::calculate_boundaries()
{
- _VERBOSE("Triangulation::calculate_boundaries");
+
get_neighbors(); // Ensure _neighbors has been created.
@@ -309,7 +309,7 @@ void Triangulation::calculate_boundaries()
void Triangulation::calculate_edges()
{
- _VERBOSE("Triangulation::calculate_edges");
+
Py_XDECREF(_edges);
// Create set of all edges, storing them with start point index less than
@@ -338,7 +338,7 @@ void Triangulation::calculate_edges()
void Triangulation::calculate_neighbors()
{
- _VERBOSE("Triangulation::calculate_neighbors");
+
Py_XDECREF(_neighbors);
// Create _neighbors array with shape (ntri,3) and initialise all to -1.
@@ -382,7 +382,7 @@ void Triangulation::calculate_neighbors()
Py::Object Triangulation::calculate_plane_coefficients(const Py::Tuple &args)
{
- _VERBOSE("Triangulation::calculate_plane_coefficients");
+
args.verify_length(1);
PyArrayObject* z = (PyArrayObject*)PyArray_ContiguousFromObject(
@@ -485,7 +485,7 @@ void Triangulation::correct_triangles()
const Triangulation::Boundaries& Triangulation::get_boundaries() const
{
- _VERBOSE("Triangulation::get_boundaries");
+
if (_boundaries.empty())
const_cast(this)->calculate_boundaries();
return _boundaries;
@@ -517,7 +517,7 @@ int Triangulation::get_edge_in_triangle(int tri, int point) const
Py::Object Triangulation::get_edges()
{
- _VERBOSE("Triangulation::get_edges");
+
if (_edges == 0)
calculate_edges();
return Py::asObject(Py::new_reference_to((PyObject*)_edges));
@@ -544,7 +544,7 @@ TriEdge Triangulation::get_neighbor_edge(int tri, int edge) const
Py::Object Triangulation::get_neighbors()
{
- _VERBOSE("Triangulation::get_neighbors");
+
if (_neighbors == 0) calculate_neighbors();
return Py::asObject(Py::new_reference_to((PyObject*)_neighbors));
}
@@ -592,7 +592,7 @@ const int* Triangulation::get_triangles_ptr() const
void Triangulation::init_type()
{
- _VERBOSE("Triangulation::init_type");
+
behaviors().name("Triangulation");
behaviors().doc("Triangulation");
@@ -616,7 +616,7 @@ bool Triangulation::is_masked(int tri) const
Py::Object Triangulation::set_mask(const Py::Tuple &args)
{
- _VERBOSE("Triangulation::set_mask");
+
args.verify_length(1);
Py_XDECREF(_mask);
@@ -667,12 +667,12 @@ TriContourGenerator::TriContourGenerator(Py::Object triangulation,
_boundaries_visited(0),
_boundaries_used(0)
{
- _VERBOSE("TriContourGenerator::TriContourGenerator");
+
}
TriContourGenerator::~TriContourGenerator()
{
- _VERBOSE("TriContourGenerator::~TriContourGenerator");
+
Py_XDECREF(_z);
}
@@ -761,7 +761,7 @@ Py::Object TriContourGenerator::contour_to_segs_and_kinds(const Contour& contour
Py::Object TriContourGenerator::create_contour(const Py::Tuple &args)
{
- _VERBOSE("TriContourGenerator::create_contour");
+
args.verify_length(1);
double level = (Py::Float)args[0];
@@ -777,7 +777,7 @@ Py::Object TriContourGenerator::create_contour(const Py::Tuple &args)
Py::Object TriContourGenerator::create_filled_contour(const Py::Tuple &args)
{
- _VERBOSE("TriContourGenerator::create_filled_contour");
+
args.verify_length(2);
double lower_level = (Py::Float)args[0];
@@ -1089,7 +1089,7 @@ const double& TriContourGenerator::get_z(int point) const
void TriContourGenerator::init_type()
{
- _VERBOSE("TriContourGenerator::init_type");
+
behaviors().name("TriContourGenerator");
behaviors().doc("TriContourGenerator");
@@ -1124,12 +1124,12 @@ TrapezoidMapTriFinder::TrapezoidMapTriFinder(Py::Object triangulation)
_points(0),
_tree(0)
{
- _VERBOSE("TrapezoidMapTriFinder::TrapezoidMapTriFinder");
+
}
TrapezoidMapTriFinder::~TrapezoidMapTriFinder()
{
- _VERBOSE("TrapezoidMapTriFinder::~TrapezoidMapTriFinder");
+
clear();
}
@@ -1452,7 +1452,7 @@ TrapezoidMapTriFinder::find_trapezoids_intersecting_edge(
Py::Object
TrapezoidMapTriFinder::get_tree_stats()
{
- _VERBOSE("TrapezoidMapTriFinder::get_tree_stats");
+
NodeStats stats;
_tree->get_stats(0, stats);
@@ -1477,7 +1477,7 @@ TrapezoidMapTriFinder::get_triangulation() const
void
TrapezoidMapTriFinder::init_type()
{
- _VERBOSE("TrapezoidMapTriFinder::init_type");
+
behaviors().name("TrapezoidMapTriFinder");
behaviors().doc("TrapezoidMapTriFinder");
@@ -1499,7 +1499,7 @@ TrapezoidMapTriFinder::init_type()
Py::Object
TrapezoidMapTriFinder::initialize()
{
- _VERBOSE("TrapezoidMapTriFinder::initialize");
+
clear();
const Triangulation& triang = get_triangulation();
@@ -1594,7 +1594,7 @@ TrapezoidMapTriFinder::initialize()
Py::Object
TrapezoidMapTriFinder::print_tree()
{
- _VERBOSE("TrapezoidMapTriFinder::print_tree");
+
assert(_tree != 0 && "Null Node tree");
_tree->print();
@@ -2229,7 +2229,7 @@ TriModule::TriModule()
Py::Object TriModule::new_triangulation(const Py::Tuple &args)
{
- _VERBOSE("TriModule::new_triangulation");
+
args.verify_length(6);
// x and y.
@@ -2310,7 +2310,7 @@ Py::Object TriModule::new_triangulation(const Py::Tuple &args)
Py::Object TriModule::new_tricontourgenerator(const Py::Tuple &args)
{
- _VERBOSE("TriModule::new_tricontourgenerator");
+
args.verify_length(2);
Py::Object tri = args[0];
@@ -2332,7 +2332,7 @@ Py::Object TriModule::new_tricontourgenerator(const Py::Tuple &args)
Py::Object
TriModule::new_TrapezoidMapTriFinder(const Py::Tuple &args)
{
- _VERBOSE("TriModule::new_TrapezoidMapTriFinder");
+
args.verify_length(1);
Py::Object triangulation = args[0];
diff --git a/lib/mpl_toolkits/mplot3d/axis3d.py b/lib/mpl_toolkits/mplot3d/axis3d.py
index c9bac1ae95bd..12516fc715ff 100755
--- a/lib/mpl_toolkits/mplot3d/axis3d.py
+++ b/lib/mpl_toolkits/mplot3d/axis3d.py
@@ -260,8 +260,8 @@ def draw(self, renderer):
# edge points of the plane to display coordinates and calculate
# an angle from that.
# TODO: Maybe Text objects should handle this themselves?
- dx, dy = (self.axes.transAxes.transform(peparray[0:2, 1]) -
- self.axes.transAxes.transform(peparray[0:2, 0]))
+ dx, dy = (self.axes.transAxes.transform([peparray[0:2, 1]]) -
+ self.axes.transAxes.transform([peparray[0:2, 0]]))[0]
lxyz = 0.5*(edgep1 + edgep2)
diff --git a/setupext.py b/setupext.py
index ba78c6726dce..27a8b8bf5ec3 100755
--- a/setupext.py
+++ b/setupext.py
@@ -1005,12 +1005,12 @@ class FT2Font(SetupPackage):
def get_extension(self):
sources = [
'src/ft2font.cpp',
+ 'src/ft2font_wrapper.cpp',
'src/mplutils.cpp'
]
ext = make_extension('matplotlib.ft2font', sources)
FreeType().add_flags(ext)
Numpy().add_flags(ext)
- CXX().add_flags(ext)
return ext
@@ -1039,14 +1039,14 @@ def check(self):
def get_extension(self):
sources = [
- 'src/_png.cpp', 'src/mplutils.cpp'
+ 'src/_png.cpp',
+ 'src/mplutils.cpp'
]
ext = make_extension('matplotlib._png', sources)
pkg_config.setup_extension(
ext, 'libpng', default_libraries=['png', 'z'],
alt_exec='libpng-config --ldflags')
Numpy().add_flags(ext)
- CXX().add_flags(ext)
return ext
@@ -1092,7 +1092,6 @@ def get_extension(self):
]
ext = make_extension('matplotlib.ttconv', sources)
Numpy().add_flags(ext)
- CXX().add_flags(ext)
ext.include_dirs.append('extern')
return ext
@@ -1102,15 +1101,13 @@ class Path(SetupPackage):
def get_extension(self):
sources = [
- 'src/_path.cpp',
- 'src/path_cleanup.cpp',
- 'src/agg_py_transforms.cpp'
+ 'src/py_converters.cpp',
+ 'src/_path_wrapper.cpp'
]
ext = make_extension('matplotlib._path', sources)
Numpy().add_flags(ext)
LibAgg().add_flags(ext)
- CXX().add_flags(ext)
return ext
@@ -1119,12 +1116,13 @@ class Image(SetupPackage):
def get_extension(self):
sources = [
- 'src/_image.cpp', 'src/mplutils.cpp'
+ 'src/_image.cpp',
+ 'src/mplutils.cpp',
+ 'src/_image_wrapper.cpp'
]
ext = make_extension('matplotlib._image', sources)
Numpy().add_flags(ext)
LibAgg().add_flags(ext)
- CXX().add_flags(ext)
return ext
@@ -1317,14 +1315,14 @@ class BackendAgg(OptionalBackendPackage):
def get_extension(self):
sources = [
"src/mplutils.cpp",
- "src/agg_py_transforms.cpp",
- "src/_backend_agg.cpp"
+ "src/py_converters.cpp",
+ "src/_backend_agg.cpp",
+ "src/_backend_agg_wrapper.cpp"
]
ext = make_extension('matplotlib.backends._backend_agg', sources)
Numpy().add_flags(ext)
LibAgg().add_flags(ext)
FreeType().add_flags(ext)
- CXX().add_flags(ext)
return ext
@@ -1363,7 +1361,7 @@ def check_requirements(self):
def get_extension(self):
sources = [
- 'src/agg_py_transforms.cpp',
+ 'src/py_converters.cpp',
'src/_tkagg.cpp'
]
@@ -1371,7 +1369,6 @@ def get_extension(self):
self.add_flags(ext)
Numpy().add_flags(ext)
LibAgg().add_flags(ext)
- CXX().add_flags(ext)
return ext
def query_tcltk(self):
@@ -1740,14 +1737,13 @@ def get_package_data(self):
def get_extension(self):
sources = [
- 'src/agg_py_transforms.cpp',
+ 'src/py_converters.cpp',
'src/_gtkagg.cpp',
'src/mplutils.cpp'
]
ext = make_extension('matplotlib.backends._gtkagg', sources)
self.add_flags(ext)
LibAgg().add_flags(ext)
- CXX().add_flags(ext)
Numpy().add_flags(ext)
return ext
@@ -1919,14 +1915,13 @@ def check_requirements(self):
def get_extension(self):
sources = [
'src/_macosx.m',
- 'src/agg_py_transforms.cpp',
+ 'src/py_converters.cpp',
'src/path_cleanup.cpp'
]
ext = make_extension('matplotlib.backends._macosx', sources)
Numpy().add_flags(ext)
LibAgg().add_flags(ext)
- CXX().add_flags(ext)
ext.extra_link_args.extend(['-framework', 'Cocoa'])
return ext
diff --git a/src/.clang-format b/src/.clang-format
new file mode 100644
index 000000000000..d54ffcedf924
--- /dev/null
+++ b/src/.clang-format
@@ -0,0 +1,46 @@
+---
+# BasedOnStyle: LLVM
+AccessModifierOffset: -2
+ConstructorInitializerIndentWidth: 4
+AlignEscapedNewlinesLeft: false
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakTemplateDeclarations: true
+AlwaysBreakBeforeMultilineStrings: true
+BreakBeforeBinaryOperators: false
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BinPackParameters: false
+ColumnLimit: 100
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+DerivePointerBinding: false
+ExperimentalAutoDetectBinPacking: false
+IndentCaseLabels: false
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 60
+PenaltyBreakString: 1000
+PenaltyBreakFirstLessLess: 120
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerBindsToType: false
+SpacesBeforeTrailingComments: 1
+Cpp11BracedListStyle: false
+Standard: Cpp03
+IndentWidth: 4
+TabWidth: 8
+UseTab: Never
+BreakBeforeBraces: Linux
+IndentFunctionDeclarationAfterType: false
+SpacesInParentheses: false
+SpacesInAngles: false
+SpaceInEmptyParentheses: false
+SpacesInCStyleCastParentheses: false
+SpaceAfterControlStatementKeyword: true
+SpaceBeforeAssignmentOperators: true
+ContinuationIndentWidth: 4
+...
diff --git a/src/_backend_agg.cpp b/src/_backend_agg.cpp
index 9d12c860f28b..9c1e10941c9a 100644
--- a/src/_backend_agg.cpp
+++ b/src/_backend_agg.cpp
@@ -1,176 +1,22 @@
/* -*- mode: c++; c-basic-offset: 4 -*- */
-/* A rewrite of _backend_agg using PyCXX to handle ref counting, etc..
- */
+#define NO_IMPORT_ARRAY
-/* Python API mandates Python.h is included *first* */
-#include "Python.h"
-
-/* TODO: Remove this dependency */
-#include "ft2font.h"
-#include "_image.h"
#include "_backend_agg.h"
#include "mplutils.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "agg_conv_curve.h"
-#include "agg_conv_transform.h"
-#include "agg_image_accessors.h"
-#include "agg_renderer_primitives.h"
-#include "agg_scanline_storage_aa.h"
-#include "agg_scanline_storage_bin.h"
-#include "agg_span_allocator.h"
-#include "agg_span_converter.h"
-#include "agg_span_image_filter_gray.h"
-#include "agg_span_image_filter_rgba.h"
-#include "agg_span_interpolator_linear.h"
-#include "agg_span_pattern_rgba.h"
-#include "agg_span_gouraud_rgba.h"
-#include "agg_conv_shorten_path.h"
-#include "util/agg_color_conv_rgb8.h"
-
#include "MPL_isnan.h"
-#include "numpy/arrayobject.h"
-#include "agg_py_transforms.h"
-
-#include "file_compat.h"
-
-#ifndef M_PI
-#define M_PI 3.14159265358979323846
-#endif
-#ifndef M_PI_4
-#define M_PI_4 0.785398163397448309616
-#endif
-#ifndef M_PI_2
-#define M_PI_2 1.57079632679489661923
-#endif
-
-
-/*
- Convert dashes from the Python representation as nested sequences to
- the C++ representation as a std::vector >
- (GCAgg::dash_t) */
-void
-convert_dashes(const Py::Tuple& dashes, double dpi,
- GCAgg::dash_t& dashes_out, double& dashOffset_out)
-{
- if (dashes.length() != 2)
- {
- throw Py::ValueError(
- Printf("Dash descriptor must be a length 2 tuple; found %d",
- dashes.length()).str()
- );
- }
-
- dashes_out.clear();
- dashOffset_out = 0.0;
- if (dashes[0].ptr() == Py_None)
- {
- return;
- }
-
- dashOffset_out = double(Py::Float(dashes[0])) * dpi / 72.0;
-
- Py::SeqBase dashSeq = dashes[1];
-
- size_t Ndash = dashSeq.length();
- if (Ndash % 2 != 0)
- {
- throw Py::ValueError(
- Printf("Dash sequence must be an even length sequence; found %d", Ndash).str()
- );
- }
-
- dashes_out.clear();
- dashes_out.reserve(Ndash / 2);
-
- double val0, val1;
- for (size_t i = 0; i < Ndash; i += 2)
- {
- val0 = double(Py::Float(dashSeq[i])) * dpi / 72.0;
- val1 = double(Py::Float(dashSeq[i+1])) * dpi / 72.0;
- dashes_out.push_back(std::make_pair(val0, val1));
- }
-}
-
-
-Py::Object
-BufferRegion::to_string(const Py::Tuple &args)
-{
- // owned=true to prevent memory leak
- #if PY3K
- return Py::Bytes
- #else
- return Py::String
- #endif
- (PyBytes_FromStringAndSize((const char*)data, height*stride), true);
-}
-
-
-Py::Object
-BufferRegion::set_x(const Py::Tuple &args)
-{
- args.verify_length(1);
- size_t x = (long) Py::Int(args[0]);
- rect.x1 = x;
- return Py::Object();
-}
-
-
-Py::Object
-BufferRegion::set_y(const Py::Tuple &args)
-{
- args.verify_length(1);
- size_t y = (long)Py::Int(args[0]);
- rect.y1 = y;
- return Py::Object();
-}
-
-
-Py::Object
-BufferRegion::get_extents(const Py::Tuple &args)
-{
- args.verify_length(0);
-
- Py::Tuple extents(4);
- extents[0] = Py::Int(rect.x1);
- extents[1] = Py::Int(rect.y1);
- extents[2] = Py::Int(rect.x2);
- extents[3] = Py::Int(rect.y2);
-
- return extents;
-}
-
-
-Py::Object
-BufferRegion::to_string_argb(const Py::Tuple &args)
+void BufferRegion::to_string_argb(uint8_t *buf)
{
- // owned=true to prevent memory leak
- Py_ssize_t length;
- unsigned char* pix;
- unsigned char* begin;
+ unsigned char *pix;
unsigned char tmp;
size_t i, j;
- PyObject* str = PyBytes_FromStringAndSize((const char*)data, height * stride);
- if (PyBytes_AsStringAndSize(str, (char**)&begin, &length))
- {
- throw Py::TypeError("Could not create memory for blit");
- }
+ memcpy(buf, data, height * stride);
- for (i = 0; i < (size_t)height; ++i)
- {
- pix = begin + i * stride;
- for (j = 0; j < (size_t)width; ++j)
- {
+ 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];
@@ -178,253 +24,33 @@ BufferRegion::to_string_argb(const Py::Tuple &args)
pix += 4;
}
}
-
- #if PY3K
- return Py::Bytes
- #else
- return Py::String
- #endif
- (str, true);
-}
-
-
-GCAgg::GCAgg(const Py::Object &gc, double dpi) :
- dpi(dpi), isaa(true), dashOffset(0.0)
-{
- _VERBOSE("GCAgg::GCAgg");
- linewidth = points_to_pixels(gc.getAttr("_linewidth")) ;
- alpha = Py::Float(gc.getAttr("_alpha"));
- forced_alpha = Py::Boolean(gc.getAttr("_forced_alpha"));
- color = get_color(gc);
- _set_antialiased(gc);
- _set_linecap(gc);
- _set_joinstyle(gc);
- _set_dashes(gc);
- _set_clip_rectangle(gc);
- _set_clip_path(gc);
- _set_snap(gc);
- _set_hatch_path(gc);
- _set_sketch_params(gc);
-}
-
-
-void
-GCAgg::_set_antialiased(const Py::Object& gc)
-{
- _VERBOSE("GCAgg::antialiased");
- isaa = Py::Boolean(gc.getAttr("_antialiased"));
-}
-
-
-agg::rgba
-GCAgg::get_color(const Py::Object& gc)
-{
- _VERBOSE("GCAgg::get_color");
- Py::Tuple rgb = Py::Tuple(gc.getAttr("_rgb"));
-
- double r = Py::Float(rgb[0]);
- double g = Py::Float(rgb[1]);
- double b = Py::Float(rgb[2]);
- double a = Py::Float(rgb[3]);
- return agg::rgba(r, g, b, a);
-}
-
-
-double
-GCAgg::points_to_pixels(const Py::Object& points)
-{
- _VERBOSE("GCAgg::points_to_pixels");
- double p = Py::Float(points) ;
- return p * dpi / 72.0;
-}
-
-
-void
-GCAgg::_set_linecap(const Py::Object& gc)
-{
- _VERBOSE("GCAgg::_set_linecap");
-
- std::string capstyle = Py::String(gc.getAttr("_capstyle")).encode("utf-8");
-
- if (capstyle == "butt")
- {
- cap = agg::butt_cap;
- }
- else if (capstyle == "round")
- {
- cap = agg::round_cap;
- }
- else if (capstyle == "projecting")
- {
- cap = agg::square_cap;
- }
- else
- {
- throw Py::ValueError(Printf("GC _capstyle attribute must be one of butt, round, projecting; found %s", capstyle.c_str()).str());
- }
-}
-
-
-void
-GCAgg::_set_joinstyle(const Py::Object& gc)
-{
- _VERBOSE("GCAgg::_set_joinstyle");
-
- std::string joinstyle = Py::String(gc.getAttr("_joinstyle")).encode("utf-8");
-
- if (joinstyle == "miter")
- {
- join = agg::miter_join_revert;
- }
- else if (joinstyle == "round")
- {
- join = agg::round_join;
- }
- else if (joinstyle == "bevel")
- {
- join = agg::bevel_join;
- }
- else
- {
- throw Py::ValueError(Printf("GC _joinstyle attribute must be one of butt, round, projecting; found %s", joinstyle.c_str()).str());
- }
-}
-
-
-void
-GCAgg::_set_dashes(const Py::Object& gc)
-{
- //return the dashOffset, dashes sequence tuple.
- _VERBOSE("GCAgg::_set_dashes");
-
- Py::Object dash_obj(gc.getAttr("_dashes"));
- if (dash_obj.ptr() == Py_None)
- {
- dashes.clear();
- return;
- }
-
- convert_dashes(dash_obj, dpi, dashes, dashOffset);
-}
-
-
-void
-GCAgg::_set_clip_rectangle(const Py::Object& gc)
-{
- //set the clip rectangle from the gc
-
- _VERBOSE("GCAgg::_set_clip_rectangle");
-
- Py::Object o(gc.getAttr("_cliprect"));
- cliprect = o;
-}
-
-
-void
-GCAgg::_set_clip_path(const Py::Object& gc)
-{
- //set the clip path from the gc
-
- _VERBOSE("GCAgg::_set_clip_path");
-
- Py::Object method_obj = gc.getAttr("get_clip_path");
- Py::Callable method(method_obj);
- Py::Tuple path_and_transform = method.apply(Py::Tuple());
- if (path_and_transform[0].ptr() != Py_None)
- {
- clippath = path_and_transform[0];
- clippath_trans = py_to_agg_transformation_matrix(path_and_transform[1].ptr());
- }
-}
-
-
-void
-GCAgg::_set_snap(const Py::Object& gc)
-{
- //set the snap setting
-
- _VERBOSE("GCAgg::_set_snap");
-
- Py::Object method_obj = gc.getAttr("get_snap");
- Py::Callable method(method_obj);
- Py::Object py_snap = method.apply(Py::Tuple());
- if (py_snap.isNone())
- {
- snap_mode = SNAP_AUTO;
- }
- else if (py_snap.isTrue())
- {
- snap_mode = SNAP_TRUE;
- }
- else
- {
- snap_mode = SNAP_FALSE;
- }
-}
-
-
-void
-GCAgg::_set_hatch_path(const Py::Object& gc)
-{
- _VERBOSE("GCAgg::_set_hatch_path");
-
- Py::Object method_obj = gc.getAttr("get_hatch_path");
- Py::Callable method(method_obj);
- hatchpath = method.apply(Py::Tuple());
- if (hatchpath.ptr() == NULL)
- throw Py::Exception();
-}
-
-void
-GCAgg::_set_sketch_params(const Py::Object& gc)
-{
- _VERBOSE("GCAgg::_get_sketch_params");
-
- Py::Object method_obj = gc.getAttr("get_sketch_params");
- Py::Callable method(method_obj);
- Py::Object result = method.apply(Py::Tuple());
- if (result.ptr() == Py_None) {
- sketch_scale = 0.0;
- } else {
- Py::Tuple sketch_params(result);
- sketch_scale = Py::Float(sketch_params[0]);
- sketch_length = Py::Float(sketch_params[1]);
- sketch_randomness = Py::Float(sketch_params[2]);
- }
}
-
-const size_t
-RendererAgg::PIXELS_PER_INCH(96);
-
-
-RendererAgg::RendererAgg(unsigned int width, unsigned int height, double dpi,
- int debug) :
- 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(),
- debug(debug),
- _fill_color(agg::rgba(1, 1, 1, 0))
-{
- _VERBOSE("RendererAgg::RendererAgg");
- unsigned stride(width*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(),
+ 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);
@@ -433,16 +59,18 @@ 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 * 4);
}
+RendererAgg::~RendererAgg()
+{
+ delete[] alphaBuffer;
+ delete[] pixBuffer;
+}
-void
-RendererAgg::create_alpha_buffers()
+void RendererAgg::create_alpha_buffers()
{
- if (!alphaBuffer)
- {
+ if (!alphaBuffer) {
alphaBuffer = new agg::int8u[width * height];
alphaMaskRenderingBuffer.attach(alphaBuffer, width, height, width);
rendererBaseAlphaMask.attach(pixfmtAlphaMask);
@@ -450,2184 +78,150 @@ RendererAgg::create_alpha_buffers()
}
}
-
-template
-void
-RendererAgg::set_clipbox(const Py::Object& cliprect, R& rasterizer)
-{
- //set the clip rectangle from the gc
-
- _VERBOSE("RendererAgg::set_clipbox");
-
- double l, b, r, t;
- if (py_convert_bbox(cliprect.ptr(), l, b, r, t))
- {
- rasterizer.clip_box(std::max(int(floor(l + 0.5)), 0),
- std::max(int(floor(height - b + 0.5)), 0),
- std::min(int(floor(r + 0.5)), int(width)),
- std::min(int(floor(height - t + 0.5)), int(height)));
- }
- else
- {
- rasterizer.clip_box(0, 0, width, height);
- }
-
- _VERBOSE("RendererAgg::set_clipbox done");
-}
-
-
-std::pair
-RendererAgg::_get_rgba_face(const Py::Object& rgbFace, double alpha, bool forced_alpha)
-{
- _VERBOSE("RendererAgg::_get_rgba_face");
- std::pair face;
-
- if (rgbFace.ptr() == Py_None)
- {
- face.first = false;
- }
- else
- {
- face.first = true;
- Py::Tuple rgb = Py::Tuple(rgbFace);
- if (forced_alpha || rgb.length() < 4)
- {
- face.second = rgb_to_color(rgb, alpha);
- }
- else
- {
- face.second = rgb_to_color(rgb, Py::Float(rgb[3]));
- }
- }
- return face;
-}
-
-
-Py::Object
-RendererAgg::copy_from_bbox(const Py::Tuple& args)
+BufferRegion *RendererAgg::copy_from_bbox(agg::rect_d in_rect)
{
- //copy region in bbox to buffer and return swig/agg buffer object
- args.verify_length(1);
+ agg::rect_i rect(
+ (int)in_rect.x1, height - (int)in_rect.y2, (int)in_rect.x2, height - (int)in_rect.y1);
- Py::Object box_obj = args[0];
- double l, b, r, t;
- if (!py_convert_bbox(box_obj.ptr(), l, b, r, t))
- {
- throw Py::TypeError("Invalid bbox provided to copy_from_bbox");
- }
-
- agg::rect_i rect((int)l, height - (int)t, (int)r, height - (int)b);
-
- BufferRegion* reg = NULL;
- try
- {
- reg = new BufferRegion(rect, true);
- }
- catch (...)
- {
- throw Py::MemoryError(
- "RendererAgg::copy_from_bbox could not allocate memory for buffer");
- }
+ BufferRegion *reg = NULL;
+ reg = new BufferRegion(rect);
- if (!reg)
- {
- throw Py::MemoryError(
- "RendererAgg::copy_from_bbox could not allocate memory for buffer");
- }
+ agg::rendering_buffer rbuf;
+ rbuf.attach(reg->get_data(), reg->get_width(), reg->get_height(), reg->get_stride());
- try
- {
- agg::rendering_buffer rbuf;
- rbuf.attach(reg->data, reg->width, reg->height, reg->stride);
+ pixfmt pf(rbuf);
+ renderer_base rb(pf);
+ rb.copy_from(renderingBuffer, &rect, -rect.x1, -rect.y1);
- pixfmt pf(rbuf);
- renderer_base rb(pf);
- rb.copy_from(renderingBuffer, &rect, -rect.x1, -rect.y1);
- }
- catch (...)
- {
- delete reg;
- throw Py::RuntimeError("An unknown error occurred in copy_from_bbox");
- }
- return Py::asObject(reg);
+ return reg;
}
-
-Py::Object
-RendererAgg::restore_region(const Py::Tuple& args)
+void RendererAgg::restore_region(BufferRegion ®ion)
{
- //copy BufferRegion to buffer
- args.verify_length(1);
- BufferRegion* region = static_cast(args[0].ptr());
-
- if (region->data == NULL)
- {
- throw Py::ValueError("Cannot restore_region from NULL data");
+ if (region.get_data() == NULL) {
+ throw "Cannot restore_region from NULL data";
}
agg::rendering_buffer rbuf;
- rbuf.attach(region->data,
- region->width,
- region->height,
- region->stride);
-
- rendererBase.copy_from(rbuf, 0, region->rect.x1, region->rect.y1);
+ rbuf.attach(region.get_data(), region.get_width(), region.get_height(), region.get_stride());
- return Py::Object();
+ rendererBase.copy_from(rbuf, 0, region.get_rect().x1, region.get_rect().y1);
}
-
// Restore the part of the saved region with offsets
-Py::Object
-RendererAgg::restore_region2(const Py::Tuple& args)
+void
+RendererAgg::restore_region(BufferRegion ®ion, int x, int y, int xx1, int yy1, int xx2, int yy2)
{
- //copy BufferRegion to buffer
- args.verify_length(7);
-
- int x(0), y(0), xx1(0), yy1(0), xx2(0), yy2(0);
- try
- {
- xx1 = Py::Int(args[1]);
- yy1 = Py::Int(args[2]);
- xx2 = Py::Int(args[3]);
- yy2 = Py::Int(args[4]);
- x = Py::Int(args[5]);
- y = Py::Int(args[6]);
- }
- catch (Py::TypeError)
- {
- throw Py::TypeError("Invalid input arguments to restore_region2");
+ if (region.get_data() == NULL) {
+ throw "Cannot restore_region from NULL data";
}
+ agg::rect_i &rrect = region.get_rect();
- BufferRegion* region = static_cast(args[0].ptr());
-
- if (region->data == NULL)
- {
- throw Py::ValueError("Cannot restore_region from NULL data");
- }
-
- agg::rect_i rect(xx1 - region->rect.x1, (yy1 - region->rect.y1),
- xx2 - region->rect.x1, (yy2 - region->rect.y1));
+ agg::rect_i rect(xx1 - rrect.x1, (yy1 - rrect.y1), xx2 - rrect.x1, (yy2 - rrect.y1));
agg::rendering_buffer rbuf;
- rbuf.attach(region->data,
- region->width,
- region->height,
- region->stride);
+ rbuf.attach(region.get_data(), region.get_width(), region.get_height(), region.get_stride());
rendererBase.copy_from(rbuf, &rect, x, y);
-
- return Py::Object();
}
-
-bool
-RendererAgg::render_clippath(const Py::Object& clippath,
- const agg::trans_affine& clippath_trans)
+bool RendererAgg::render_clippath(py::PathIterator &clippath,
+ const agg::trans_affine &clippath_trans)
{
- typedef agg::conv_transform transformed_path_t;
+ typedef agg::conv_transform transformed_path_t;
typedef agg::conv_curve curve_t;
- bool has_clippath = (clippath.ptr() != Py_None);
+ bool has_clippath = (clippath.total_vertices() != 0);
if (has_clippath &&
- (clippath.ptr() != lastclippath.ptr() ||
- clippath_trans != lastclippath_transform))
- {
+ (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);
- PathIterator clippath_iter(clippath);
rendererBaseAlphaMask.clear(agg::gray8(0, 0));
- transformed_path_t transformed_clippath(clippath_iter, trans);
+ transformed_path_t transformed_clippath(clippath, trans);
curve_t curved_clippath(transformed_clippath);
- try {
- theRasterizer.add_path(curved_clippath);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
+ theRasterizer.add_path(curved_clippath);
rendererAlphaMask.color(agg::gray8(255, 255));
agg::render_scanlines(theRasterizer, scanlineAlphaMask, rendererAlphaMask);
- lastclippath = clippath;
+ lastclippath = clippath.get_id();
lastclippath_transform = clippath_trans;
}
return has_clippath;
}
-#define MARKER_CACHE_SIZE 512
-
-
-Py::Object
-RendererAgg::draw_markers(const Py::Tuple& args)
+void RendererAgg::tostring_rgb(uint8_t *buf)
{
- typedef agg::conv_transform transformed_path_t;
- typedef PathSnapper snap_t;
- typedef agg::conv_curve curve_t;
- typedef agg::conv_stroke stroke_t;
- typedef agg::pixfmt_amask_adaptor pixfmt_amask_type;
- typedef agg::renderer_base amask_ren_type;
- typedef agg::renderer_scanline_aa_solid amask_aa_renderer_type;
- args.verify_length(5, 6);
-
- Py::Object gc_obj = args[0];
- Py::Object marker_path_obj = args[1];
- agg::trans_affine marker_trans = py_to_agg_transformation_matrix(args[2].ptr());
- Py::Object path_obj = args[3];
- agg::trans_affine trans = py_to_agg_transformation_matrix(args[4].ptr());
- Py::Object face_obj;
- if (args.size() == 6)
- {
- face_obj = args[5];
- }
-
- GCAgg gc(gc_obj, dpi);
-
- // Deal with the difference in y-axis direction
- marker_trans *= agg::trans_affine_scaling(1.0, -1.0);
- trans *= agg::trans_affine_scaling(1.0, -1.0);
- trans *= agg::trans_affine_translation(0.5, (double)height + 0.5);
-
- PathIterator marker_path(marker_path_obj);
- transformed_path_t marker_path_transformed(marker_path, marker_trans);
- snap_t marker_path_snapped(marker_path_transformed,
- gc.snap_mode,
- marker_path.total_vertices(),
- gc.linewidth);
- curve_t marker_path_curve(marker_path_snapped);
-
- PathIterator path(path_obj);
- transformed_path_t path_transformed(path, trans);
- snap_t path_snapped(path_transformed,
- SNAP_FALSE,
- path.total_vertices(),
- 0.0);
- curve_t path_curve(path_snapped);
- path_curve.rewind(0);
-
- facepair_t face = _get_rgba_face(face_obj, gc.alpha, gc.forced_alpha);
-
- //maxim's suggestions for cached scanlines
- agg::scanline_storage_aa8 scanlines;
- theRasterizer.reset();
- theRasterizer.reset_clipping();
- rendererBase.reset_clipping(true);
-
- agg::int8u staticFillCache[MARKER_CACHE_SIZE];
- agg::int8u staticStrokeCache[MARKER_CACHE_SIZE];
- agg::int8u* fillCache = staticFillCache;
- agg::int8u* strokeCache = staticStrokeCache;
-
- try
- {
- unsigned fillSize = 0;
- if (face.first)
- {
- try {
- theRasterizer.add_path(marker_path_curve);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
- agg::render_scanlines(theRasterizer, slineP8, scanlines);
- fillSize = scanlines.byte_size();
- if (fillSize >= MARKER_CACHE_SIZE)
- {
- fillCache = new agg::int8u[fillSize];
- }
- scanlines.serialize(fillCache);
- }
-
- stroke_t stroke(marker_path_curve);
- stroke.width(gc.linewidth);
- stroke.line_cap(gc.cap);
- stroke.line_join(gc.join);
- theRasterizer.reset();
- try {
- theRasterizer.add_path(stroke);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
- agg::render_scanlines(theRasterizer, slineP8, scanlines);
- unsigned strokeSize = scanlines.byte_size();
- if (strokeSize >= MARKER_CACHE_SIZE)
- {
- strokeCache = new agg::int8u[strokeSize];
- }
- scanlines.serialize(strokeCache);
-
- theRasterizer.reset_clipping();
- rendererBase.reset_clipping(true);
- set_clipbox(gc.cliprect, rendererBase);
- bool has_clippath = render_clippath(gc.clippath, gc.clippath_trans);
-
- double x, y;
-
- agg::serialized_scanlines_adaptor_aa8 sa;
- agg::serialized_scanlines_adaptor_aa8::embedded_scanline sl;
-
- agg::rect_d clipping_rect(
- -1.0 - scanlines.max_x(),
- -1.0 - scanlines.max_y(),
- 1.0 + width - scanlines.min_x(),
- 1.0 + height - scanlines.min_y());
-
- if (has_clippath)
- {
- while (path_curve.vertex(&x, &y) != agg::path_cmd_stop)
- {
- if (MPL_notisfinite64(x) || MPL_notisfinite64(y))
- {
- continue;
- }
-
- /* These values are correctly snapped above -- so we don't want
- to round here, we really only want to truncate */
- x = floor(x);
- y = floor(y);
-
- // Cull points outside the boundary of the image.
- // Values that are too large may overflow and create
- // segfaults.
- // http://sourceforge.net/tracker/?func=detail&aid=2865490&group_id=80706&atid=560720
- if (!clipping_rect.hit_test(x, y))
- {
- continue;
- }
-
- pixfmt_amask_type pfa(pixFmt, alphaMask);
- amask_ren_type r(pfa);
- amask_aa_renderer_type ren(r);
-
- if (face.first)
- {
- ren.color(face.second);
- sa.init(fillCache, fillSize, x, y);
- agg::render_scanlines(sa, sl, ren);
- }
- ren.color(gc.color);
- sa.init(strokeCache, strokeSize, x, y);
- agg::render_scanlines(sa, sl, ren);
- }
- }
- else
- {
- while (path_curve.vertex(&x, &y) != agg::path_cmd_stop)
- {
- if (MPL_notisfinite64(x) || MPL_notisfinite64(y))
- {
- continue;
- }
-
- /* These values are correctly snapped above -- so we don't want
- to round here, we really only want to truncate */
- x = floor(x);
- y = floor(y);
+ // "Return the rendered buffer as an RGB string"
- // Cull points outside the boundary of the image.
- // Values that are too large may overflow and create
- // segfaults.
- // http://sourceforge.net/tracker/?func=detail&aid=2865490&group_id=80706&atid=560720
- if (!clipping_rect.hit_test(x, y))
- {
- continue;
- }
-
- if (face.first)
- {
- rendererAA.color(face.second);
- sa.init(fillCache, fillSize, x, y);
- agg::render_scanlines(sa, sl, rendererAA);
- }
-
- rendererAA.color(gc.color);
- sa.init(strokeCache, strokeSize, x, y);
- agg::render_scanlines(sa, sl, rendererAA);
- }
- }
- }
- catch (...)
- {
- if (fillCache != staticFillCache)
- delete[] fillCache;
- if (strokeCache != staticStrokeCache)
- delete[] strokeCache;
- theRasterizer.reset_clipping();
- rendererBase.reset_clipping(true);
- throw;
- }
-
- if (fillCache != staticFillCache)
- delete[] fillCache;
- if (strokeCache != staticStrokeCache)
- delete[] strokeCache;
+ int row_len = width * 3;
- theRasterizer.reset_clipping();
- rendererBase.reset_clipping(true);
+ agg::rendering_buffer renderingBufferTmp;
+ renderingBufferTmp.attach(buf, width, height, row_len);
- return Py::Object();
+ agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_rgb24());
}
-
-/**
- * This is a custom span generator that converts spans in the
- * 8-bit inverted greyscale font buffer to rgba that agg can use.
- */
-template
-class font_to_rgba
+void RendererAgg::tostring_argb(uint8_t *buf)
{
-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;
- span_alloc_type _allocator;
+ //"Return the rendered buffer as an RGB string";
-public:
- font_to_rgba(child_type* gen, color_type color) :
- _gen(gen),
- _color(color)
- {
+ 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());
+}
- inline void
- generate(color_type* output_span, int x, int y, unsigned len)
- {
- _allocator.allocate(len);
- child_color_type* input_span = _allocator.span();
- _gen->generate(input_span, x, y, len);
+void RendererAgg::tostring_bgra(uint8_t *buf)
+{
+ //"Return the rendered buffer as an RGB string";
- do
- {
- *output_span = _color;
- output_span->a = ((unsigned int)_color.a *
- (unsigned int)input_span->v) >> 8;
- ++output_span;
- ++input_span;
- }
- while (--len);
- }
+ int row_len = width * 4;
- void
- prepare()
- {
- _gen->prepare();
- }
-};
+ agg::rendering_buffer renderingBufferTmp;
+ renderingBufferTmp.attach(buf, width, height, row_len);
+ agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_bgra32());
+}
-// MGDTODO: Support clip paths
-Py::Object
-RendererAgg::draw_text_image(const Py::Tuple& args)
+agg::rect_i RendererAgg::get_content_extents()
{
- _VERBOSE("RendererAgg::draw_text");
-
- 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 agg::renderer_scanline_aa renderer_type;
-
- args.verify_length(5);
-
- const unsigned char* buffer = NULL;
- int width, height;
- Py::Object image_obj = args[0];
+ agg::rect_i r(width, height, 0, 0);
- if (PyArray_Check(image_obj.ptr()))
- {
- PyObject* image_array = PyArray_FromObject(
- image_obj.ptr(), NPY_UBYTE, 2, 2);
- if (!image_array)
- {
- throw Py::ValueError(
- "First argument to draw_text_image must be a FT2Font.Image object or a Nx2 uint8 numpy array.");
- }
- image_obj = Py::Object(image_array, true);
- buffer = (unsigned char *)PyArray_DATA((PyArrayObject*)image_array);
- width = PyArray_DIM((PyArrayObject*)image_array, 1);
- height = PyArray_DIM((PyArrayObject*)image_array, 0);
- }
- else
- {
- FT2Image* image = static_cast(
- Py::getPythonExtensionBase(image_obj.ptr()));
- if (!image->get_buffer())
- {
- throw Py::ValueError(
- "First argument to draw_text_image must be a FT2Font.Image object or a Nx2 uint8 numpy array.");
+ // 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;
}
- buffer = image->get_buffer();
- width = image->get_width();
- height = image->get_height();
- }
-
- int x(0), y(0);
- try
- {
- x = Py::Int(args[1]);
- y = Py::Int(args[2]);
- }
- catch (Py::TypeError)
- {
- throw Py::TypeError("Invalid input arguments to draw_text_image");
}
- double angle = Py::Float(args[3]);
+ r.x1 = std::max(0, r.x1 - 1);
+ r.y1 = std::max(0, r.y1 - 1);
+ r.x2 = std::max(r.x2 + 1, (int)width);
+ r.y2 = std::max(r.y2 + 1, (int)height);
- GCAgg gc(args[4], dpi);
-
- theRasterizer.reset_clipping();
- rendererBase.reset_clipping(true);
- set_clipbox(gc.cliprect, theRasterizer);
-
- agg::rendering_buffer srcbuf((agg::int8u*)buffer, width, height, width);
- agg::pixfmt_gray8 pixf_img(srcbuf);
-
- agg::trans_affine mtx;
- mtx *= agg::trans_affine_translation(0, -height);
- mtx *= agg::trans_affine_rotation(-angle * agg::pi / 180.0);
- mtx *= agg::trans_affine_translation(x, y);
-
- agg::path_storage rect;
- rect.move_to(0, 0);
- rect.line_to(width, 0);
- rect.line_to(width, height);
- rect.line_to(0, height);
- rect.line_to(0, 0);
- agg::conv_transform rect2(rect, mtx);
-
- agg::trans_affine inv_mtx(mtx);
- inv_mtx.invert();
-
- agg::image_filter_lut filter;
- filter.calculate(agg::image_filter_spline36());
- interpolator_type interpolator(inv_mtx);
- color_span_alloc_type sa;
- image_accessor_type ia(pixf_img, 0);
- image_span_gen_type image_span_generator(ia, interpolator, filter);
- span_gen_type output_span_generator(&image_span_generator, gc.color);
- renderer_type ri(rendererBase, sa, output_span_generator);
-
- try {
- theRasterizer.add_path(rect2);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
- agg::render_scanlines(theRasterizer, slineP8, ri);
-
- return Py::Object();
+ return r;
}
-class span_conv_alpha
+void RendererAgg::clear()
{
-public:
- typedef agg::rgba8 color_type;
-
- double m_alpha;
-
- span_conv_alpha(double alpha) :
- m_alpha(alpha)
- {
- }
-
- void prepare() {}
- void generate(color_type* span, int x, int y, unsigned len) const
- {
- do
- {
- span->a = (agg::int8u)((double)span->a * m_alpha);
- ++span;
- }
- while(--len);
- }
-};
+ //"clear the rendered buffer";
-
-Py::Object
-RendererAgg::draw_image(const Py::Tuple& args)
-{
- _VERBOSE("RendererAgg::draw_image");
-
- args.verify_length(4, 7); // 7 if affine matrix if given
-
- GCAgg gc(args[0], dpi);
- Image *image = static_cast(args[3].ptr());
- bool has_clippath = false;
- agg::trans_affine affine_trans;
- bool has_affine = false;
- double x, y, w, h;
- double alpha;
-
- if (args.size() == 7)
- {
- has_affine = true;
- x = Py::Float(args[1]);
- y = Py::Float(args[2]);
- w = Py::Float(args[4]);
- h = Py::Float(args[5]);
- affine_trans = py_to_agg_transformation_matrix(args[6].ptr());
- }
- else
- {
- x = mpl_round(Py::Float(args[1]));
- y = mpl_round(Py::Float(args[2]));
- w = h = 0; /* w and h not used in this case, but assign to prevent
- warnings from the compiler */
- }
-
- alpha = gc.alpha;
-
- theRasterizer.reset_clipping();
- rendererBase.reset_clipping(true);
- set_clipbox(gc.cliprect, theRasterizer);
- has_clippath = render_clippath(gc.clippath, gc.clippath_trans);
-
- Py::Tuple empty;
- image->flipud_out(empty);
- pixfmt pixf(*(image->rbufOut));
-
- if (has_affine | has_clippath)
- {
- agg::trans_affine mtx;
- agg::path_storage rect;
-
- if (has_affine)
- {
- mtx *= agg::trans_affine_scaling(1, -1);
- mtx *= agg::trans_affine_translation(0, image->rowsOut);
- mtx *= agg::trans_affine_scaling(w / (image->colsOut),
- h / (image->rowsOut));
- mtx *= agg::trans_affine_translation(x, y);
- mtx *= affine_trans;
- mtx *= agg::trans_affine_scaling(1.0, -1.0);
- mtx *= agg::trans_affine_translation(0.0, (double) height);
- }
- else
- {
- mtx *= agg::trans_affine_translation(
- (int)x,
- (int)(height - (y + image->rowsOut)));
- }
-
- rect.move_to(0, 0);
- rect.line_to(image->colsOut, 0);
- rect.line_to(image->colsOut, image->rowsOut);
- rect.line_to(0, image->rowsOut);
- rect.line_to(0, 0);
-
- agg::conv_transform rect2(rect, mtx);
-
- agg::trans_affine inv_mtx(mtx);
- inv_mtx.invert();
-
- 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;
-
- color_span_alloc_type sa;
- image_accessor_type ia(pixf, agg::rgba8(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 spans(image_span_generator, conv_alpha);
-
- if (has_clippath)
- {
- typedef agg::pixfmt_amask_adaptor
- pixfmt_amask_type;
- typedef agg::renderer_base amask_ren_type;
- typedef agg::renderer_scanline_aa
- renderer_type_alpha;
-
- pixfmt_amask_type pfa(pixFmt, alphaMask);
- amask_ren_type r(pfa);
- renderer_type_alpha ri(r, sa, spans);
-
- try {
- theRasterizer.add_path(rect2);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
- agg::render_scanlines(theRasterizer, scanlineAlphaMask, ri);
- }
- else
- {
- typedef agg::renderer_base ren_type;
- typedef agg::renderer_scanline_aa
- renderer_type;
-
- ren_type r(pixFmt);
- renderer_type ri(r, sa, spans);
-
- try {
- theRasterizer.add_path(rect2);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
- agg::render_scanlines(theRasterizer, slineP8, ri);
- }
-
- }
- else
- {
- set_clipbox(gc.cliprect, rendererBase);
- rendererBase.blend_from(
- pixf, 0, (int)x, (int)(height - (y + image->rowsOut)),
- (agg::int8u)(alpha * 255));
- }
-
- rendererBase.reset_clipping(true);
- image->flipud_out(empty);
-
- return Py::Object();
-}
-
-
-template
-void RendererAgg::_draw_path(path_t& path, bool has_clippath,
- const facepair_t& face, const GCAgg& gc)
-{
- typedef agg::conv_stroke stroke_t;
- typedef agg::conv_dash dash_t;
- typedef agg::conv_stroke stroke_dash_t;
- typedef agg::pixfmt_amask_adaptor pixfmt_amask_type;
- typedef agg::renderer_base amask_ren_type;
- typedef agg::renderer_scanline_aa_solid amask_aa_renderer_type;
- typedef agg::renderer_scanline_bin_solid amask_bin_renderer_type;
-
- // Render face
- if (face.first)
- {
- try {
- theRasterizer.add_path(path);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
-
- if (gc.isaa)
- {
- if (has_clippath)
- {
- pixfmt_amask_type pfa(pixFmt, alphaMask);
- amask_ren_type r(pfa);
- amask_aa_renderer_type ren(r);
- ren.color(face.second);
- agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren);
- }
- else
- {
- rendererAA.color(face.second);
- agg::render_scanlines(theRasterizer, slineP8, rendererAA);
- }
- }
- else
- {
- if (has_clippath)
- {
- pixfmt_amask_type pfa(pixFmt, alphaMask);
- amask_ren_type r(pfa);
- amask_bin_renderer_type ren(r);
- ren.color(face.second);
- agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren);
- }
- else
- {
- rendererBin.color(face.second);
- agg::render_scanlines(theRasterizer, slineP8, rendererBin);
- }
- }
- }
-
- // Render hatch
- if (!gc.hatchpath.isNone())
- {
- // Reset any clipping that may be in effect, since we'll be
- // drawing the hatch in a scratch buffer at origin (0, 0)
- theRasterizer.reset_clipping();
- rendererBase.reset_clipping(true);
-
- // Create and transform the path
- typedef agg::conv_transform hatch_path_trans_t;
- typedef agg::conv_curve hatch_path_curve_t;
- typedef agg::conv_stroke hatch_path_stroke_t;
-
- PathIterator hatch_path(gc.hatchpath);
- agg::trans_affine hatch_trans;
- hatch_trans *= agg::trans_affine_scaling(1.0, -1.0);
- hatch_trans *= agg::trans_affine_translation(0.0, 1.0);
- hatch_trans *= agg::trans_affine_scaling(HATCH_SIZE, HATCH_SIZE);
- hatch_path_trans_t hatch_path_trans(hatch_path, hatch_trans);
- hatch_path_curve_t hatch_path_curve(hatch_path_trans);
- hatch_path_stroke_t hatch_path_stroke(hatch_path_curve);
- hatch_path_stroke.width(1.0);
- hatch_path_stroke.line_cap(agg::square_cap);
-
- // Render the path into the hatch buffer
- pixfmt hatch_img_pixf(hatchRenderingBuffer);
- renderer_base rb(hatch_img_pixf);
- renderer_aa rs(rb);
- rb.clear(_fill_color);
- rs.color(gc.color);
-
- try {
- theRasterizer.add_path(hatch_path_curve);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
- agg::render_scanlines(theRasterizer, slineP8, rs);
- try {
- theRasterizer.add_path(hatch_path_stroke);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
- agg::render_scanlines(theRasterizer, slineP8, rs);
-
- // Put clipping back on, if originally set on entry to this
- // function
- set_clipbox(gc.cliprect, theRasterizer);
- if (has_clippath)
- render_clippath(gc.clippath, gc.clippath_trans);
-
- // Transfer the hatch to the main image buffer
- typedef agg::image_accessor_wrap < pixfmt,
- 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;
- img_source_type img_src(hatch_img_pixf);
- span_gen_type sg(img_src, 0, 0);
- try {
- theRasterizer.add_path(path);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
-
- if (has_clippath)
- {
- pixfmt_amask_type pfa(pixFmt, alphaMask);
- amask_ren_type ren(pfa);
- agg::render_scanlines_aa(theRasterizer, slineP8, ren, sa, sg);
- }
- else
- {
- agg::render_scanlines_aa(theRasterizer, slineP8, rendererBase, sa, sg);
- }
- }
-
- // Render stroke
- if (gc.linewidth != 0.0)
- {
- double linewidth = gc.linewidth;
- if (!gc.isaa)
- {
- linewidth = (linewidth < 0.5) ? 0.5 : mpl_round(linewidth);
- }
- if (gc.dashes.size() == 0)
- {
- stroke_t stroke(path);
- stroke.width(linewidth);
- stroke.line_cap(gc.cap);
- stroke.line_join(gc.join);
- try {
- theRasterizer.add_path(stroke);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
- }
- else
- {
- dash_t dash(path);
- for (GCAgg::dash_t::const_iterator i = gc.dashes.begin();
- i != gc.dashes.end(); ++i)
- {
- double val0 = i->first;
- double val1 = i->second;
- if (!gc.isaa)
- {
- val0 = (int)val0 + 0.5;
- val1 = (int)val1 + 0.5;
- }
- dash.add_dash(val0, val1);
- }
- stroke_dash_t stroke(dash);
- stroke.line_cap(gc.cap);
- stroke.line_join(gc.join);
- stroke.width(linewidth);
- try {
- theRasterizer.add_path(stroke);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what());
- }
- }
-
- if (gc.isaa)
- {
- if (has_clippath)
- {
- pixfmt_amask_type pfa(pixFmt, alphaMask);
- amask_ren_type r(pfa);
- amask_aa_renderer_type ren(r);
- ren.color(gc.color);
- agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren);
- }
- else
- {
- rendererAA.color(gc.color);
- agg::render_scanlines(theRasterizer, slineP8, rendererAA);
- }
- }
- else
- {
- if (has_clippath)
- {
- pixfmt_amask_type pfa(pixFmt, alphaMask);
- amask_ren_type r(pfa);
- amask_bin_renderer_type ren(r);
- ren.color(gc.color);
- agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren);
- }
- else
- {
- rendererBin.color(gc.color);
- agg::render_scanlines(theRasterizer, slineBin, rendererBin);
- }
- }
- }
-}
-
-
-Py::Object
-RendererAgg::draw_path(const Py::Tuple& args)
-{
- typedef agg::conv_transform transformed_path_t;
- typedef PathNanRemover nan_removed_t;
- typedef PathClipper clipped_t;
- typedef PathSnapper snapped_t;
- typedef PathSimplifier simplify_t;
- typedef agg::conv_curve curve_t;
- typedef Sketch sketch_t;
-
- _VERBOSE("RendererAgg::draw_path");
- args.verify_length(3, 4);
-
- GCAgg gc(args[0], dpi);
- PathIterator path(args[1]);
- agg::trans_affine trans = py_to_agg_transformation_matrix(args[2].ptr());
- Py::Object face_obj;
- if (args.size() == 4)
- face_obj = args[3];
-
- facepair_t face = _get_rgba_face(face_obj, gc.alpha, gc.forced_alpha);
-
- theRasterizer.reset_clipping();
- rendererBase.reset_clipping(true);
- set_clipbox(gc.cliprect, theRasterizer);
- bool has_clippath = render_clippath(gc.clippath, gc.clippath_trans);
-
- trans *= agg::trans_affine_scaling(1.0, -1.0);
- trans *= agg::trans_affine_translation(0.0, (double)height);
- bool clip = !face.first && gc.hatchpath.isNone() && !path.has_curves();
- bool simplify = path.should_simplify() && clip;
- double snapping_linewidth = gc.linewidth;
- if (gc.color.a == 0.0) {
- snapping_linewidth = 0.0;
- }
-
- transformed_path_t tpath(path, trans);
- nan_removed_t nan_removed(tpath, true, path.has_curves());
- clipped_t clipped(nan_removed, clip, width, height);
- snapped_t snapped(clipped, gc.snap_mode, path.total_vertices(), snapping_linewidth);
- simplify_t simplified(snapped, simplify, path.simplify_threshold());
- curve_t curve(simplified);
- sketch_t sketch(curve, gc.sketch_scale, gc.sketch_length, gc.sketch_randomness);
-
- try
- {
- _draw_path(sketch, has_clippath, face, gc);
- }
- catch (const char* e)
- {
- throw Py::RuntimeError(e);
- }
-
- return Py::Object();
-}
-
-
-template
-Py::Object
-RendererAgg::_draw_path_collection_generic
-(GCAgg& gc,
- agg::trans_affine master_transform,
- const Py::Object& cliprect,
- const Py::Object& clippath,
- const agg::trans_affine& clippath_trans,
- const PathGenerator& path_generator,
- const Py::Object& transforms_obj,
- const Py::Object& offsets_obj,
- const agg::trans_affine& offset_trans,
- const Py::Object& facecolors_obj,
- const Py::Object& edgecolors_obj,
- const Py::SeqBase& linewidths,
- const Py::SeqBase& linestyles_obj,
- const Py::SeqBase& antialiaseds,
- const bool data_offsets)
-{
- typedef agg::conv_transform transformed_path_t;
- typedef PathNanRemover nan_removed_t;
- typedef PathClipper clipped_t;
- typedef PathSnapper snapped_t;
- typedef agg::conv_curve snapped_curve_t;
- typedef agg::conv_curve curve_t;
-
- PyArrayObject* offsets = (PyArrayObject*)PyArray_FromObject
- (offsets_obj.ptr(), NPY_DOUBLE, 0, 2);
- if (!offsets ||
- (PyArray_NDIM(offsets) == 2 && PyArray_DIM(offsets, 1) != 2) ||
- (PyArray_NDIM(offsets) == 1 && PyArray_DIM(offsets, 0) != 0))
- {
- Py_XDECREF(offsets);
- throw Py::ValueError("Offsets array must be Nx2");
- }
- Py::Object offsets_arr_obj((PyObject*)offsets, true);
-
- PyArrayObject* facecolors = (PyArrayObject*)PyArray_FromObject
- (facecolors_obj.ptr(), NPY_DOUBLE, 1, 2);
- if (!facecolors ||
- (PyArray_NDIM(facecolors) == 1 && PyArray_DIM(facecolors, 0) != 0) ||
- (PyArray_NDIM(facecolors) == 2 && PyArray_DIM(facecolors, 1) != 4))
- {
- Py_XDECREF(facecolors);
- throw Py::ValueError("Facecolors must be a Nx4 numpy array or empty");
- }
- Py::Object facecolors_arr_obj((PyObject*)facecolors, true);
-
- PyArrayObject* edgecolors = (PyArrayObject*)PyArray_FromObject
- (edgecolors_obj.ptr(), NPY_DOUBLE, 1, 2);
- if (!edgecolors ||
- (PyArray_NDIM(edgecolors) == 1 && PyArray_DIM(edgecolors, 0) != 0) ||
- (PyArray_NDIM(edgecolors) == 2 && PyArray_DIM(edgecolors, 1) != 4))
- {
- Py_XDECREF(edgecolors);
- throw Py::ValueError("Edgecolors must be a Nx4 numpy array");
- }
- Py::Object edgecolors_arr_obj((PyObject*)edgecolors, true);
-
- PyArrayObject* transforms_arr = (PyArrayObject*)PyArray_FromObject
- (transforms_obj.ptr(), NPY_DOUBLE, 1, 3);
- if (!transforms_arr ||
- (PyArray_NDIM(transforms_arr) == 1 && PyArray_DIM(transforms_arr, 0) != 0) ||
- (PyArray_NDIM(transforms_arr) == 2) ||
- (PyArray_NDIM(transforms_arr) == 3 &&
- ((PyArray_DIM(transforms_arr, 1) != 3) ||
- (PyArray_DIM(transforms_arr, 2) != 3))))
- {
- Py_XDECREF(transforms_arr);
- throw Py::ValueError("Transforms must be a Nx3x3 numpy array");
- }
-
- size_t Npaths = path_generator.num_paths();
- size_t Noffsets = PyArray_DIM(offsets, 0);
- size_t N = std::max(Npaths, Noffsets);
- size_t Ntransforms = PyArray_DIM(transforms_arr, 0);
- size_t Nfacecolors = PyArray_DIM(facecolors, 0);
- size_t Nedgecolors = PyArray_DIM(edgecolors, 0);
- size_t Nlinewidths = linewidths.length();
- size_t Nlinestyles = std::min(linestyles_obj.length(), N);
- size_t Naa = antialiaseds.length();
-
- if ((Nfacecolors == 0 && Nedgecolors == 0) || Npaths == 0)
- {
- Py_XDECREF(transforms_arr);
- return Py::Object();
- }
-
- size_t i = 0;
-
- // Convert all of the transforms up front
- typedef std::vector transforms_t;
- transforms_t transforms;
- transforms.reserve(Ntransforms);
- for (i = 0; i < Ntransforms; ++i)
- {
- /* TODO: Use a Numpy iterator */
- agg::trans_affine trans(
- *(double *)PyArray_GETPTR3(transforms_arr, i, 0, 0),
- *(double *)PyArray_GETPTR3(transforms_arr, i, 1, 0),
- *(double *)PyArray_GETPTR3(transforms_arr, i, 0, 1),
- *(double *)PyArray_GETPTR3(transforms_arr, i, 1, 1),
- *(double *)PyArray_GETPTR3(transforms_arr, i, 0, 2),
- *(double *)PyArray_GETPTR3(transforms_arr, i, 1, 2));
- trans *= master_transform;
-
- transforms.push_back(trans);
- }
-
- // Convert all the dashes up front
- typedef std::vector > dashes_t;
- dashes_t dashes;
- dashes.resize(Nlinestyles);
- i = 0;
- for (dashes_t::iterator d = dashes.begin();
- d != dashes.end(); ++d, ++i)
- {
- convert_dashes(Py::Tuple(linestyles_obj[i]), dpi, d->second,
- d->first);
- }
-
- // Handle any clipping globally
- theRasterizer.reset_clipping();
- rendererBase.reset_clipping(true);
- set_clipbox(cliprect, theRasterizer);
- bool has_clippath = render_clippath(clippath, clippath_trans);
-
- // Set some defaults, assuming no face or edge
- gc.linewidth = 0.0;
- facepair_t face;
- face.first = Nfacecolors != 0;
- agg::trans_affine trans;
-
- for (i = 0; i < N; ++i)
- {
- typename PathGenerator::path_iterator path = path_generator(i);
-
- if (Ntransforms)
- {
- trans = transforms[i % Ntransforms];
- }
- else
- {
- trans = master_transform;
- }
-
- if (Noffsets)
- {
- double xo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0);
- double yo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1);
- offset_trans.transform(&xo, &yo);
- if (data_offsets) {
- trans = agg::trans_affine_translation(xo, yo) * trans;
- } else {
- trans *= agg::trans_affine_translation(xo, yo);
- }
- }
-
- // These transformations must be done post-offsets
- trans *= agg::trans_affine_scaling(1.0, -1.0);
- trans *= agg::trans_affine_translation(0.0, (double)height);
-
- if (Nfacecolors)
- {
- size_t fi = i % Nfacecolors;
- face.second = agg::rgba(
- *(double*)PyArray_GETPTR2(facecolors, fi, 0),
- *(double*)PyArray_GETPTR2(facecolors, fi, 1),
- *(double*)PyArray_GETPTR2(facecolors, fi, 2),
- *(double*)PyArray_GETPTR2(facecolors, fi, 3));
- }
-
- if (Nedgecolors)
- {
- size_t ei = i % Nedgecolors;
- gc.color = agg::rgba(
- *(double*)PyArray_GETPTR2(edgecolors, ei, 0),
- *(double*)PyArray_GETPTR2(edgecolors, ei, 1),
- *(double*)PyArray_GETPTR2(edgecolors, ei, 2),
- *(double*)PyArray_GETPTR2(edgecolors, ei, 3));
-
- if (Nlinewidths)
- {
- gc.linewidth = double(Py::Float(linewidths[i % Nlinewidths])) * dpi / 72.0;
- }
- else
- {
- gc.linewidth = 1.0;
- }
- if (Nlinestyles)
- {
- gc.dashes = dashes[i % Nlinestyles].second;
- gc.dashOffset = dashes[i % Nlinestyles].first;
- }
- }
-
- bool do_clip = !face.first && gc.hatchpath.isNone() && !has_curves;
-
- if (check_snap)
- {
- gc.isaa = Py::Boolean(antialiaseds[i % Naa]);
-
- transformed_path_t tpath(path, trans);
- nan_removed_t nan_removed(tpath, true, has_curves);
- clipped_t clipped(nan_removed, do_clip, width, height);
- snapped_t snapped(clipped, gc.snap_mode,
- path.total_vertices(), gc.linewidth);
- if (has_curves)
- {
- snapped_curve_t curve(snapped);
- _draw_path(curve, has_clippath, face, gc);
- }
- else
- {
- _draw_path(snapped, has_clippath, face, gc);
- }
- }
- else
- {
- gc.isaa = Py::Boolean(antialiaseds[i % Naa]);
-
- transformed_path_t tpath(path, trans);
- nan_removed_t nan_removed(tpath, true, has_curves);
- clipped_t clipped(nan_removed, do_clip, width, height);
- if (has_curves)
- {
- curve_t curve(clipped);
- _draw_path(curve, has_clippath, face, gc);
- }
- else
- {
- _draw_path(clipped, has_clippath, face, gc);
- }
- }
- }
-
- Py_XDECREF(transforms_arr);
-
- return Py::Object();
-}
-
-
-class PathListGenerator
-{
- const Py::SeqBase& m_paths;
- size_t m_npaths;
-
-public:
- typedef PathIterator path_iterator;
-
- inline
- PathListGenerator(const Py::SeqBase& paths) :
- m_paths(paths), m_npaths(paths.size())
- {
-
- }
-
- inline size_t
- num_paths() const
- {
- return m_npaths;
- }
-
- inline path_iterator
- operator()(size_t i) const
- {
- return PathIterator(m_paths[i % m_npaths]);
- }
-};
-
-
-Py::Object
-RendererAgg::draw_path_collection(const Py::Tuple& args)
-{
- _VERBOSE("RendererAgg::draw_path_collection");
- args.verify_length(13);
-
- Py::Object gc_obj = args[0];
- GCAgg gc(gc_obj, dpi);
- agg::trans_affine master_transform = py_to_agg_transformation_matrix(args[1].ptr());
- Py::SeqBase path = args[2];
- PathListGenerator path_generator(path);
- Py::Object transforms_obj = args[3];
- Py::Object offsets_obj = args[4];
- agg::trans_affine offset_trans = py_to_agg_transformation_matrix(args[5].ptr());
- Py::Object facecolors_obj = args[6];
- Py::Object edgecolors_obj = args[7];
- Py::SeqBase linewidths = args[8];
- Py::SeqBase linestyles_obj = args[9];
- Py::SeqBase antialiaseds = args[10];
- // We don't actually care about urls for Agg, so just ignore it.
- // Py::SeqBase urls = args[11];
- std::string offset_position = Py::String(args[12]).encode("utf-8");
-
- bool data_offsets = (offset_position == "data");
-
- try
- {
- _draw_path_collection_generic
- (gc,
- master_transform,
- gc.cliprect,
- gc.clippath,
- gc.clippath_trans,
- path_generator,
- transforms_obj,
- offsets_obj,
- offset_trans,
- facecolors_obj,
- edgecolors_obj,
- linewidths,
- linestyles_obj,
- antialiaseds,
- data_offsets);
- }
- catch (const char *e)
- {
- throw Py::RuntimeError(e);
- }
-
- return Py::Object();
-}
-
-
-class QuadMeshGenerator
-{
- size_t m_meshWidth;
- size_t m_meshHeight;
- PyArrayObject* m_coordinates;
-
- class QuadMeshPathIterator
- {
- size_t m_iterator;
- size_t m_m, m_n;
- PyArrayObject* m_coordinates;
-
- public:
- QuadMeshPathIterator(size_t m, size_t n, PyArrayObject* coordinates) :
- m_iterator(0), m_m(m), m_n(n), m_coordinates(coordinates)
- {
-
- }
-
- private:
- inline unsigned
- vertex(unsigned idx, double* x, double* y)
- {
- size_t m = m_m + ((idx & 0x2) >> 1);
- size_t n = m_n + (((idx + 1) & 0x2) >> 1);
- double* pair = (double*)PyArray_GETPTR2(m_coordinates, n, m);
- *x = *pair++;
- *y = *pair;
- return (idx) ? agg::path_cmd_line_to : agg::path_cmd_move_to;
- }
-
- public:
- inline unsigned
- vertex(double* x, double* y)
- {
- if (m_iterator >= total_vertices())
- {
- return agg::path_cmd_stop;
- }
- return vertex(m_iterator++, x, y);
- }
-
- inline void
- rewind(unsigned path_id)
- {
- m_iterator = path_id;
- }
-
- inline unsigned
- total_vertices()
- {
- return 5;
- }
-
- inline bool
- should_simplify()
- {
- return false;
- }
- };
-
-public:
- typedef QuadMeshPathIterator path_iterator;
-
- inline
- QuadMeshGenerator(size_t meshWidth, size_t meshHeight, PyObject* coordinates) :
- m_meshWidth(meshWidth), m_meshHeight(meshHeight), m_coordinates(NULL)
- {
- PyArrayObject* coordinates_array = \
- (PyArrayObject*)PyArray_ContiguousFromObject(
- coordinates, NPY_DOUBLE, 3, 3);
-
- if (!coordinates_array)
- {
- throw Py::ValueError("Invalid coordinates array.");
- }
-
- m_coordinates = coordinates_array;
- }
-
- inline
- ~QuadMeshGenerator()
- {
- Py_XDECREF(m_coordinates);
- }
-
- inline size_t
- num_paths() const
- {
- return m_meshWidth * m_meshHeight;
- }
-
- inline path_iterator
- operator()(size_t i) const
- {
- return QuadMeshPathIterator(i % m_meshWidth, i / m_meshWidth, m_coordinates);
- }
-};
-
-Py::Object
-RendererAgg::draw_quad_mesh(const Py::Tuple& args)
-{
- _VERBOSE("RendererAgg::draw_quad_mesh");
- args.verify_length(10);
-
- //segments, trans, clipbox, colors, linewidths, antialiaseds
- GCAgg gc(args[0], dpi);
- agg::trans_affine master_transform = py_to_agg_transformation_matrix(args[1].ptr());
- size_t mesh_width = (long)Py::Int(args[2]);
- size_t mesh_height = (long)Py::Int(args[3]);
- Py::Object coordinates = args[4];
- Py::Object offsets_obj = args[5];
- agg::trans_affine offset_trans = py_to_agg_transformation_matrix(args[6].ptr());
- Py::Object facecolors_obj = args[7];
- bool antialiased = (bool)Py::Boolean(args[8]);
- Py::Object edgecolors_obj = args[9];
-
- QuadMeshGenerator path_generator(mesh_width, mesh_height, coordinates.ptr());
-
- Py::Object transforms_obj = Py::List(0);
- Py::Tuple linewidths(1);
- linewidths[0] = Py::Float(gc.linewidth);
- Py::SeqBase linestyles_obj;
- Py::Tuple antialiaseds(1);
- antialiaseds[0] = Py::Int(antialiased ? 1 : 0);
-
- if (edgecolors_obj.isNone()) {
- if (antialiased)
- {
- edgecolors_obj = facecolors_obj;
- }
- else
- {
- npy_intp dims[] = { 0, 0 };
- edgecolors_obj = PyArray_SimpleNew(1, dims, NPY_DOUBLE);
- }
- }
-
- try
- {
- _draw_path_collection_generic
- (gc,
- master_transform,
- gc.cliprect,
- gc.clippath,
- gc.clippath_trans,
- path_generator,
- transforms_obj,
- offsets_obj,
- offset_trans,
- facecolors_obj,
- edgecolors_obj,
- linewidths,
- linestyles_obj,
- antialiaseds,
- false);
- }
- catch (const char* e)
- {
- throw Py::RuntimeError(e);
- }
-
- return Py::Object();
-}
-
-void
-RendererAgg::_draw_gouraud_triangle(const double* points,
- const double* colors,
- agg::trans_affine trans,
- bool has_clippath)
-{
- typedef agg::rgba8 color_t;
- typedef agg::span_gouraud_rgba span_gen_t;
- typedef agg::span_allocator span_alloc_t;
-
- trans *= agg::trans_affine_scaling(1.0, -1.0);
- trans *= agg::trans_affine_translation(0.0, (double)height);
-
- double tpoints[6];
-
- for (int i = 0; i < 6; i += 2)
- {
- tpoints[i] = points[i];
- tpoints[i+1] = points[i+1];
- trans.transform(&tpoints[i], &tpoints[i+1]);
- }
-
- span_alloc_t span_alloc;
- span_gen_t span_gen;
-
- span_gen.colors(
- agg::rgba(colors[0], colors[1], colors[2], colors[3]),
- agg::rgba(colors[4], colors[5], colors[6], colors[7]),
- agg::rgba(colors[8], colors[9], colors[10], colors[11]));
- span_gen.triangle(
- tpoints[0], tpoints[1],
- tpoints[2], tpoints[3],
- tpoints[4], tpoints[5],
- 0.5);
-
- try {
- theRasterizer.add_path(span_gen);
- } catch (std::overflow_error &e) {
- throw Py::OverflowError(e.what()
- );
- }
-
- if (has_clippath)
- {
- typedef agg::pixfmt_amask_adaptor pixfmt_amask_type;
- typedef agg::renderer_base amask_ren_type;
- typedef agg::renderer_scanline_aa
- amask_aa_renderer_type;
-
- pixfmt_amask_type pfa(pixFmt, alphaMask);
- amask_ren_type r(pfa);
- amask_aa_renderer_type ren(r, span_alloc, span_gen);
- agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren);
- }
- else
- {
- agg::render_scanlines_aa(theRasterizer, slineP8, rendererBase, span_alloc, span_gen);
- }
-}
-
-
-Py::Object
-RendererAgg::draw_gouraud_triangle(const Py::Tuple& args)
-{
- _VERBOSE("RendererAgg::draw_gouraud_triangle");
- args.verify_length(4);
-
- GCAgg gc(args[0], dpi);
- Py::Object points_obj = args[1];
- Py::Object colors_obj = args[2];
- agg::trans_affine trans = py_to_agg_transformation_matrix(args[3].ptr());
-
- theRasterizer.reset_clipping();
- rendererBase.reset_clipping(true);
- set_clipbox(gc.cliprect, theRasterizer);
- bool has_clippath = render_clippath(gc.clippath, gc.clippath_trans);
-
- PyArrayObject* points = (PyArrayObject*)PyArray_ContiguousFromAny
- (points_obj.ptr(), NPY_DOUBLE, 2, 2);
- if (!points ||
- PyArray_DIM(points, 0) != 3 || PyArray_DIM(points, 1) != 2)
- {
- Py_XDECREF(points);
- throw Py::ValueError("points must be a 3x2 numpy array");
- }
- points_obj = Py::Object((PyObject*)points, true);
-
- PyArrayObject* colors = (PyArrayObject*)PyArray_ContiguousFromAny
- (colors_obj.ptr(), NPY_DOUBLE, 2, 2);
- if (!colors ||
- PyArray_DIM(colors, 0) != 3 || PyArray_DIM(colors, 1) != 4)
- {
- Py_XDECREF(colors);
- throw Py::ValueError("colors must be a 3x4 numpy array");
- }
- colors_obj = Py::Object((PyObject*)colors, true);
-
- _draw_gouraud_triangle(
- (double*)PyArray_DATA(points), (double*)PyArray_DATA(colors),
- trans, has_clippath);
-
- return Py::Object();
-}
-
-
-Py::Object
-RendererAgg::draw_gouraud_triangles(const Py::Tuple& args)
-{
- _VERBOSE("RendererAgg::draw_gouraud_triangles");
- args.verify_length(4);
-
- typedef agg::rgba8 color_t;
- typedef agg::span_gouraud_rgba span_gen_t;
-
- GCAgg gc(args[0], dpi);
- Py::Object points_obj = args[1];
- Py::Object colors_obj = args[2];
- agg::trans_affine trans = py_to_agg_transformation_matrix(args[3].ptr());
- double c_points[6];
- double c_colors[12];
-
- theRasterizer.reset_clipping();
- rendererBase.reset_clipping(true);
- set_clipbox(gc.cliprect, theRasterizer);
- bool has_clippath = render_clippath(gc.clippath, gc.clippath_trans);
-
- PyArrayObject* points = (PyArrayObject*)PyArray_FromObject
- (points_obj.ptr(), NPY_DOUBLE, 3, 3);
- if (!points ||
- PyArray_DIM(points, 1) != 3 || PyArray_DIM(points, 2) != 2)
- {
- Py_XDECREF(points);
- throw Py::ValueError("points must be a Nx3x2 numpy array");
- }
- points_obj = Py::Object((PyObject*)points, true);
-
- PyArrayObject* colors = (PyArrayObject*)PyArray_FromObject
- (colors_obj.ptr(), NPY_DOUBLE, 3, 3);
- if (!colors ||
- PyArray_DIM(colors, 1) != 3 || PyArray_DIM(colors, 2) != 4)
- {
- Py_XDECREF(colors);
- throw Py::ValueError("colors must be a Nx3x4 numpy array");
- }
- colors_obj = Py::Object((PyObject*)colors, true);
-
- if (PyArray_DIM(points, 0) != PyArray_DIM(colors, 0))
- {
- throw Py::ValueError("points and colors arrays must be the same length");
- }
-
- for (int i = 0; i < PyArray_DIM(points, 0); ++i)
- {
- for (int j = 0; j < 3; ++j) {
- for (int k = 0; k < 2; ++k) {
- c_points[j*2+k] = *(double *)PyArray_GETPTR3(points, i, j, k);
- }
- }
-
- for (int j = 0; j < 3; ++j) {
- for (int k = 0; k < 4; ++k) {
- c_colors[j*4+k] = *(double *)PyArray_GETPTR3(colors, i, j, k);
- }
- }
-
- _draw_gouraud_triangle(
- c_points, c_colors, trans, has_clippath);
- }
-
- return Py::Object();
-}
-
-
-Py::Object
-RendererAgg::write_rgba(const Py::Tuple& args)
-{
- _VERBOSE("RendererAgg::write_rgba");
-
- args.verify_length(1);
-
- FILE *fp = NULL;
- mpl_off_t offset;
- Py::Object py_fileobj = Py::Object(args[0]);
- PyObject* py_file = NULL;
- bool close_file = false;
-
- if (py_fileobj.isString())
- {
- if ((py_file = mpl_PyFile_OpenFile(py_fileobj.ptr(), (char *)"wb")) == NULL) {
- throw Py::Exception();
- }
- }
- else
- {
- py_file = py_fileobj.ptr();
- }
-
- if ((fp = mpl_PyFile_Dup(py_file, (char *)"wb", &offset)))
- {
- if (fwrite(pixBuffer, 1, NUMBYTES, fp) != NUMBYTES)
- {
- if (mpl_PyFile_DupClose(py_file, fp, offset)) {
- throw Py::RuntimeError("Error closing dupe file handle");
- }
-
- if (close_file) {
- mpl_PyFile_CloseFile(py_file);
- Py_DECREF(py_file);
- }
-
- throw Py::RuntimeError("Error writing to file");
- }
-
- if (mpl_PyFile_DupClose(py_file, fp, offset)) {
- throw Py::RuntimeError("Error closing dupe file handle");
- }
-
- if (close_file) {
- mpl_PyFile_CloseFile(py_file);
- Py_DECREF(py_file);
- }
- }
- else
- {
- PyErr_Clear();
- PyObject* write_method = PyObject_GetAttrString(py_fileobj.ptr(),
- "write");
- if (!(write_method && PyCallable_Check(write_method)))
- {
- Py_XDECREF(write_method);
- throw Py::TypeError(
- "Object does not appear to be a 8-bit string path or a Python file-like object");
- }
-
- #if PY3K
- PyObject_CallFunction(write_method, (char *)"y#", pixBuffer, NUMBYTES);
- #else
- PyObject_CallFunction(write_method, (char *)"s#", pixBuffer, NUMBYTES);
- #endif
-
- Py_XDECREF(write_method);
- }
-
- return Py::Object();
-}
-
-
-Py::Object
-RendererAgg::tostring_rgb(const Py::Tuple& args)
-{
- //"Return the rendered buffer as an RGB string";
-
- _VERBOSE("RendererAgg::tostring_rgb");
-
- args.verify_length(0);
- int row_len = width * 3;
- unsigned char* buf_tmp = new unsigned char[row_len * height];
- if (buf_tmp == NULL)
- {
- //todo: also handle allocation throw
- throw Py::MemoryError(
- "RendererAgg::tostring_rgb could not allocate memory");
- }
-
- try
- {
- agg::rendering_buffer renderingBufferTmp;
- renderingBufferTmp.attach(buf_tmp,
- width,
- height,
- row_len);
-
- agg::color_conv(&renderingBufferTmp, &renderingBuffer,
- agg::color_conv_rgba32_to_rgb24());
- }
- catch (...)
- {
- delete [] buf_tmp;
- throw Py::RuntimeError("Unknown exception occurred in tostring_rgb");
- }
-
- //todo: how to do this with native CXX
- #if PY3K
- PyObject* o = Py_BuildValue("y#", buf_tmp, row_len * height);
- #else
- PyObject* o = Py_BuildValue("s#", buf_tmp, row_len * height);
- #endif
-
- delete [] buf_tmp;
- return Py::asObject(o);
-}
-
-
-Py::Object
-RendererAgg::tostring_argb(const Py::Tuple& args)
-{
- //"Return the rendered buffer as an RGB string";
-
- _VERBOSE("RendererAgg::tostring_argb");
-
- args.verify_length(0);
- int row_len = width * 4;
- unsigned char* buf_tmp = new unsigned char[row_len * height];
- if (buf_tmp == NULL)
- {
- //todo: also handle allocation throw
- throw Py::MemoryError("RendererAgg::tostring_argb could not allocate memory");
- }
-
- try
- {
- agg::rendering_buffer renderingBufferTmp;
- renderingBufferTmp.attach(buf_tmp, width, height, row_len);
- agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_argb32());
- }
- catch (...)
- {
- delete [] buf_tmp;
- throw Py::RuntimeError("Unknown exception occurred in tostring_argb");
- }
-
- //todo: how to do this with native CXX
-
- #if PY3K
- PyObject* o = Py_BuildValue("y#", buf_tmp, row_len * height);
- #else
- PyObject* o = Py_BuildValue("s#", buf_tmp, row_len * height);
- #endif
- delete [] buf_tmp;
- return Py::asObject(o);
-}
-
-
-Py::Object
-RendererAgg::tostring_bgra(const Py::Tuple& args)
-{
- //"Return the rendered buffer as an RGB string";
-
- _VERBOSE("RendererAgg::tostring_bgra");
-
- args.verify_length(0);
- int row_len = width * 4;
- unsigned char* buf_tmp = new unsigned char[row_len * height];
- if (buf_tmp == NULL)
- {
- //todo: also handle allocation throw
- throw Py::MemoryError("RendererAgg::tostring_bgra could not allocate memory");
- }
-
- try
- {
- agg::rendering_buffer renderingBufferTmp;
- renderingBufferTmp.attach(buf_tmp,
- width,
- height,
- row_len);
-
- agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_bgra32());
- }
- catch (...)
- {
- delete [] buf_tmp;
- throw Py::RuntimeError("Unknown exception occurred in tostring_bgra");
- }
-
- //todo: how to do this with native CXX
- #if PY3K
- PyObject* o = Py_BuildValue("y#", buf_tmp, row_len * height);
- #else
- PyObject* o = Py_BuildValue("s#", buf_tmp, row_len * height);
- #endif
- delete [] buf_tmp;
- return Py::asObject(o);
-}
-
-
-Py::Object
-RendererAgg::buffer_rgba(const Py::Tuple& args)
-{
- //"expose the rendered buffer as Python buffer object, starting from postion x,y";
-
- _VERBOSE("RendererAgg::buffer_rgba");
-
- args.verify_length(0);
-
- #if PY3K
- return Py::asObject(PyMemoryView_FromObject(this));
- #else
- int row_len = width * 4;
- return Py::asObject(PyBuffer_FromReadWriteMemory(
- pixBuffer, row_len*height));
- #endif
-}
-
-
-Py::Object
-RendererAgg::tostring_rgba_minimized(const Py::Tuple& args)
-{
- args.verify_length(0);
-
- int xmin = width;
- int ymin = height;
- int xmax = 0;
- int ymax = 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 < xmin) xmin = x;
- if (y < ymin) ymin = y;
- if (x > xmax) xmax = x;
- if (y > ymax) ymax = y;
- }
- pixel += 4;
- }
- }
-
- int newwidth = 0;
- int newheight = 0;
- PyObject *data;
-
- if (xmin < xmax && ymin < ymax)
- {
- // Expand the bounds by 1 pixel on all sides
- xmin = std::max(0, xmin - 1);
- ymin = std::max(0, ymin - 1);
- xmax = std::min(xmax, (int)width);
- ymax = std::min(ymax, (int)height);
-
- newwidth = xmax - xmin;
- newheight = ymax - ymin;
- int newsize = newwidth * newheight * 4;
-
- // NULL pointer causes Python to allocate uninitialized memory.
- // We then grab Python's pointer to uninitialized memory using
- // the _AsString() API.
- unsigned int* dst;
-
- data = PyBytes_FromStringAndSize(NULL, newsize);
- if (data == NULL)
- {
- throw Py::MemoryError("RendererAgg::tostring_rgba_minimized could not allocate memory");
- }
- dst = (unsigned int *)PyBytes_AsString(data);
-
- unsigned int* src = (unsigned int*)pixBuffer;
- for (int y = ymin; y < ymax; ++y)
- {
- for (int x = xmin; x < xmax; ++x, ++dst)
- {
- *dst = src[y * width + x];
- }
- }
- } else {
- data = PyBytes_FromStringAndSize(NULL, 0);
- if (data == NULL)
- {
- throw Py::MemoryError("RendererAgg::tostring_rgba_minimized could not allocate memory");
- }
- }
-
- Py::Tuple bounds(4);
- bounds[0] = Py::Int(xmin);
- bounds[1] = Py::Int(ymin);
- bounds[2] = Py::Int(newwidth);
- bounds[3] = Py::Int(newheight);
-
- Py::Tuple result(2);
- result[0] = Py::Object(data, true);
- result[1] = bounds;
-
- return result;
-}
-
-
-Py::Object
-RendererAgg::clear(const Py::Tuple& args)
-{
- //"clear the rendered buffer";
-
- _VERBOSE("RendererAgg::clear");
-
- args.verify_length(0);
rendererBase.clear(_fill_color);
-
- return Py::Object();
-}
-
-
-agg::rgba
-RendererAgg::rgb_to_color(const Py::SeqBase& rgb, double alpha)
-{
- _VERBOSE("RendererAgg::rgb_to_color");
-
- double r = Py::Float(rgb[0]);
- double g = Py::Float(rgb[1]);
- double b = Py::Float(rgb[2]);
- return agg::rgba(r, g, b, alpha);
-}
-
-
-double
-RendererAgg::points_to_pixels(const Py::Object& points)
-{
- _VERBOSE("RendererAgg::points_to_pixels");
- double p = Py::Float(points) ;
- return p * dpi / 72.0;
-}
-
-#if PY3K
-int
-RendererAgg::buffer_get( Py_buffer* buf, int flags )
-{
- return PyBuffer_FillInfo(buf, this, pixBuffer, width * height * 4, 1,
- PyBUF_SIMPLE);
-}
-#endif
-
-RendererAgg::~RendererAgg()
-{
-
- _VERBOSE("RendererAgg::~RendererAgg");
-
- delete [] alphaBuffer;
- delete [] pixBuffer;
-}
-
-/* ------------ module methods ------------- */
-Py::Object _backend_agg_module::new_renderer(const Py::Tuple &args,
- const Py::Dict &kws)
-{
-
- if (args.length() != 3)
- {
- throw Py::RuntimeError("Incorrect # of args to RendererAgg(width, height, dpi).");
- }
-
- int debug;
- if (kws.hasKey("debug"))
- {
- debug = Py::Int(kws["debug"]);
- }
- else
- {
- debug = 0;
- }
-
- unsigned int width = (int)Py::Int(args[0]);
- unsigned int height = (int)Py::Int(args[1]);
- double dpi = Py::Float(args[2]);
-
- if (width > 1 << 15 || height > 1 << 15)
- {
- throw Py::ValueError("width and height must each be below 32768");
- }
-
- if (dpi <= 0.0)
- {
- throw Py::ValueError("dpi must be positive");
- }
-
- RendererAgg* renderer = NULL;
- try
- {
- renderer = new RendererAgg(width, height, dpi, debug);
- }
- catch (std::bad_alloc)
- {
- throw Py::RuntimeError("Could not allocate memory for image");
- }
-
- return Py::asObject(renderer);
-}
-
-
-void BufferRegion::init_type()
-{
- behaviors().name("BufferRegion");
- behaviors().doc("A wrapper to pass agg buffer objects to and from the python level");
-
-
- add_varargs_method("set_x", &BufferRegion::set_x,
- "set_x(x)");
-
- add_varargs_method("set_y", &BufferRegion::set_y,
- "set_y(y)");
-
- add_varargs_method("get_extents", &BufferRegion::get_extents,
- "get_extents()");
-
- add_varargs_method("to_string", &BufferRegion::to_string,
- "to_string()");
- add_varargs_method("to_string_argb", &BufferRegion::to_string_argb,
- "to_string_argb()");
-}
-
-
-void RendererAgg::init_type()
-{
- behaviors().name("RendererAgg");
- behaviors().doc("The agg backend extension module");
-
- add_varargs_method("draw_path", &RendererAgg::draw_path,
- "draw_path(gc, path, transform, rgbFace)\n");
- add_varargs_method("draw_path_collection", &RendererAgg::draw_path_collection,
- "draw_path_collection(gc, master_transform, paths, transforms, offsets, offsetTrans, facecolors, edgecolors, linewidths, linestyles, antialiaseds)\n");
- add_varargs_method("draw_quad_mesh", &RendererAgg::draw_quad_mesh,
- "draw_quad_mesh(gc, master_transform, meshWidth, meshHeight, coordinates, offsets, offsetTrans, facecolors, antialiaseds, showedges)\n");
- add_varargs_method("draw_gouraud_triangle", &RendererAgg::draw_gouraud_triangle,
- "draw_gouraud_triangle(gc, points, colors, master_transform)\n");
- add_varargs_method("draw_gouraud_triangles", &RendererAgg::draw_gouraud_triangles,
- "draw_gouraud_triangles(gc, points, colors, master_transform)\n");
- add_varargs_method("draw_markers", &RendererAgg::draw_markers,
- "draw_markers(gc, marker_path, marker_trans, path, rgbFace)\n");
- add_varargs_method("draw_text_image", &RendererAgg::draw_text_image,
- "draw_text_image(font_image, x, y, r, g, b, a)\n");
- add_varargs_method("draw_image", &RendererAgg::draw_image,
- "draw_image(gc, x, y, im)");
- add_varargs_method("write_rgba", &RendererAgg::write_rgba,
- "write_rgba(fname)");
- add_varargs_method("tostring_rgb", &RendererAgg::tostring_rgb,
- "s = tostring_rgb()");
- add_varargs_method("tostring_argb", &RendererAgg::tostring_argb,
- "s = tostring_argb()");
- add_varargs_method("tostring_bgra", &RendererAgg::tostring_bgra,
- "s = tostring_bgra()");
- add_varargs_method("tostring_rgba_minimized", &RendererAgg::tostring_rgba_minimized,
- "s = tostring_rgba_minimized()");
- add_varargs_method("buffer_rgba", &RendererAgg::buffer_rgba,
- "buffer = buffer_rgba()");
- add_varargs_method("clear", &RendererAgg::clear,
- "clear()");
- add_varargs_method("copy_from_bbox", &RendererAgg::copy_from_bbox,
- "copy_from_bbox(bbox)");
- add_varargs_method("restore_region", &RendererAgg::restore_region,
- "restore_region(region)");
- add_varargs_method("restore_region2", &RendererAgg::restore_region2,
- "restore_region(region, x1, y1, x2, y2, x3, y3)");
-
- #if PY3K
- behaviors().supportBufferType();
- #endif
-}
-
-PyMODINIT_FUNC
-#if PY3K
-PyInit__backend_agg(void)
-#else
-init_backend_agg(void)
-#endif
-{
- //static _backend_agg_module* _backend_agg = new _backend_agg_module;
-
- _VERBOSE("init_backend_agg");
-
- import_array();
-
- static _backend_agg_module* _backend_agg = NULL;
- _backend_agg = new _backend_agg_module;
-
- #if PY3K
- return _backend_agg->module().ptr();
- #endif
}
diff --git a/src/_backend_agg.h b/src/_backend_agg.h
index 100ac4819f73..172fbfc5d1c1 100644
--- a/src/_backend_agg.h
+++ b/src/_backend_agg.h
@@ -1,57 +1,42 @@
/* -*- mode: c++; c-basic-offset: 4 -*- */
-/* _backend_agg.h - A rewrite of _backend_agg using PyCXX to handle
- ref counting, etc..
+/* _backend_agg.h
*/
-#ifndef __BACKEND_AGG_H
-#define __BACKEND_AGG_H
-#include
-#include "CXX/Extensions.hxx"
-
-#include "agg_arrowhead.h"
-#include "agg_basics.h"
-#include "agg_bezier_arc.h"
-#include "agg_color_rgba.h"
-#include "agg_conv_concat.h"
-#include "agg_conv_contour.h"
+#ifndef __BACKEND_AGG_H__
+#define __BACKEND_AGG_H__
+
+#include
+
+#include "agg_alpha_mask_u8.h"
#include "agg_conv_curve.h"
#include "agg_conv_dash.h"
-#include "agg_conv_marker.h"
-#include "agg_conv_marker_adaptor.h"
-#include "agg_math_stroke.h"
#include "agg_conv_stroke.h"
-#include "agg_ellipse.h"
-#include "agg_embedded_raster_fonts.h"
-#include "agg_path_storage.h"
-#include "agg_pixfmt_rgb.h"
-#include "agg_pixfmt_rgba.h"
-#include "agg_pixfmt_gray.h"
-#include "agg_alpha_mask_u8.h"
+#include "agg_image_accessors.h"
#include "agg_pixfmt_amask_adaptor.h"
-#include "agg_rasterizer_outline.h"
+#include "agg_pixfmt_gray.h"
+#include "agg_pixfmt_rgba.h"
#include "agg_rasterizer_scanline_aa.h"
-#include "agg_renderer_outline_aa.h"
-#include "agg_renderer_raster_text.h"
+#include "agg_renderer_base.h"
#include "agg_renderer_scanline.h"
#include "agg_rendering_buffer.h"
#include "agg_scanline_bin.h"
-#include "agg_scanline_u.h"
#include "agg_scanline_p.h"
-#include "agg_vcgen_markers_term.h"
+#include "agg_scanline_storage_aa.h"
+#include "agg_scanline_storage_bin.h"
+#include "agg_scanline_u.h"
+#include "agg_span_allocator.h"
+#include "agg_span_converter.h"
+#include "agg_span_gouraud_rgba.h"
+#include "agg_span_image_filter_gray.h"
+#include "agg_span_image_filter_rgba.h"
+#include "agg_span_interpolator_linear.h"
+#include "agg_span_pattern_rgba.h"
+#include "util/agg_color_conv_rgb8.h"
-#include "agg_py_path_iterator.h"
+#include "_backend_agg_basic_types.h"
#include "path_converters.h"
-
-// These are copied directly from path.py, and must be kept in sync
-#define STOP 0
-#define MOVETO 1
-#define LINETO 2
-#define CURVE3 3
-#define CURVE4 4
-#define CLOSEPOLY 5
-
-const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 1 };
+#include "array.h"
typedef agg::pixfmt_rgba32_plain pixfmt;
typedef agg::renderer_base renderer_base;
@@ -69,11 +54,10 @@ typedef agg::renderer_scanline_aa_solid renderer_
// a helper class to pass agg::buffer objects around. agg::buffer is
// a class in the swig wrapper
-class BufferRegion : public Py::PythonExtension
+class BufferRegion
{
-public:
- BufferRegion(const agg::rect_i &r, bool freemem = true) :
- rect(r), freemem(freemem)
+ public:
+ BufferRegion(const agg::rect_i &r) : rect(r)
{
width = r.x2 - r.x1;
height = r.y2 - r.y1;
@@ -81,97 +65,63 @@ class BufferRegion : public Py::PythonExtension
data = new agg::int8u[stride * height];
}
- agg::int8u* data;
- agg::rect_i rect;
- int width;
- int height;
- int stride;
+ virtual ~BufferRegion()
+ {
+ delete[] data;
+ };
- bool freemem;
+ agg::int8u *get_data()
+ {
+ return data;
+ }
- // set the x and y corners of the rectangle
- Py::Object set_x(const Py::Tuple &args);
- Py::Object set_y(const Py::Tuple &args);
+ agg::rect_i &get_rect()
+ {
+ return rect;
+ }
- Py::Object get_extents(const Py::Tuple &args);
+ int get_width()
+ {
+ return width;
+ }
- Py::Object to_string(const Py::Tuple &args);
- Py::Object to_string_argb(const Py::Tuple &args);
- static void init_type(void);
+ int get_height()
+ {
+ return height;
+ }
- virtual ~BufferRegion()
+ int get_stride()
{
- if (freemem)
- {
- delete [] data;
- data = NULL;
- }
- };
+ return stride;
+ }
-private:
- // prevent copying
- BufferRegion(const BufferRegion&);
- BufferRegion& operator=(const BufferRegion&);
-};
+ void to_string_argb(uint8_t *buf);
-class GCAgg
-{
-public:
- GCAgg(const Py::Object& gc, double dpi);
+ private:
+ agg::int8u *data;
+ agg::rect_i rect;
+ int width;
+ int height;
+ int stride;
- double dpi;
- bool isaa;
-
- agg::line_cap_e cap;
- agg::line_join_e join;
-
- double linewidth;
- double alpha;
- bool forced_alpha;
- agg::rgba color;
-
- Py::Object cliprect;
- Py::Object clippath;
- agg::trans_affine clippath_trans;
-
- //dashes
- typedef std::vector > dash_t;
- double dashOffset;
- dash_t dashes;
- e_snap_mode snap_mode;
-
- Py::Object hatchpath;
-
- double sketch_scale;
- double sketch_length;
- double sketch_randomness;
-
-protected:
- agg::rgba get_color(const Py::Object& gc);
- double points_to_pixels(const Py::Object& points);
- void _set_linecap(const Py::Object& gc) ;
- void _set_joinstyle(const Py::Object& gc) ;
- void _set_dashes(const Py::Object& gc) ;
- void _set_clip_rectangle(const Py::Object& gc);
- void _set_clip_path(const Py::Object& gc);
- void _set_antialiased(const Py::Object& gc);
- void _set_snap(const Py::Object& gc);
- void _set_hatch_path(const Py::Object& gc);
- void _set_sketch_params(const Py::Object& gc);
+ private:
+ // prevent copying
+ BufferRegion(const BufferRegion &);
+ BufferRegion &operator=(const BufferRegion &);
};
-
-//struct AMRenderer {
-//
-//}
+#define MARKER_CACHE_SIZE 512
// the renderer
-class RendererAgg: public Py::PythonExtension
+class RendererAgg
{
+ /* TODO: Remove facepair_t */
typedef std::pair facepair_t;
-public:
- RendererAgg(unsigned int width, unsigned int height, double dpi, int debug);
- static void init_type(void);
+
+ public:
+ RendererAgg(unsigned int width, unsigned int height, double dpi);
+
+ virtual ~RendererAgg();
unsigned int get_width()
{
@@ -183,40 +133,86 @@ class RendererAgg: public Py::PythonExtension
return height;
}
- // the drawing methods
- //Py::Object _draw_markers_nocache(const Py::Tuple & args);
- //Py::Object _draw_markers_cache(const Py::Tuple & args);
- Py::Object draw_markers(const Py::Tuple & args);
- Py::Object draw_text_image(const Py::Tuple & args);
- Py::Object draw_image(const Py::Tuple & args);
- Py::Object draw_path(const Py::Tuple & args);
- Py::Object draw_path_collection(const Py::Tuple & args);
- Py::Object draw_quad_mesh(const Py::Tuple& args);
- Py::Object draw_gouraud_triangle(const Py::Tuple& args);
- Py::Object draw_gouraud_triangles(const Py::Tuple& args);
-
- Py::Object write_rgba(const Py::Tuple & args);
- Py::Object tostring_rgb(const Py::Tuple & args);
- Py::Object tostring_argb(const Py::Tuple & args);
- Py::Object tostring_bgra(const Py::Tuple & args);
- Py::Object tostring_rgba_minimized(const Py::Tuple & args);
- Py::Object buffer_rgba(const Py::Tuple & args);
- Py::Object clear(const Py::Tuple & args);
-
- Py::Object copy_from_bbox(const Py::Tuple & args);
- Py::Object restore_region(const Py::Tuple & args);
- Py::Object restore_region2(const Py::Tuple & args);
-
- #if PY3K
- virtual int buffer_get( Py_buffer *, int flags );
- #endif
+ template
+ void draw_path(GCAgg &gc, PathIterator &path, agg::trans_affine &trans, agg::rgba &color);
- virtual ~RendererAgg();
+ template
+ void draw_markers(GCAgg &gc,
+ PathIterator &marker_path,
+ agg::trans_affine &marker_path_trans,
+ PathIterator &path,
+ agg::trans_affine &trans,
+ agg::rgba face);
+
+ template
+ void draw_text_image(GCAgg &gc, ImageArray &image, int x, int y, double angle);
+
+ template
+ void draw_image(GCAgg &gc,
+ double x,
+ double y,
+ ImageArray &image,
+ double w,
+ double h,
+ agg::trans_affine trans,
+ bool resize);
+
+ template
+ void draw_path_collection(GCAgg &gc,
+ agg::trans_affine &master_transform,
+ PathGenerator &path,
+ TransformArray &transforms,
+ OffsetArray &offsets,
+ agg::trans_affine &offset_trans,
+ ColorArray &facecolors,
+ ColorArray &edgecolors,
+ LineWidthArray &linewidths,
+ DashesVector &linestyles,
+ AntialiasedArray &antialiaseds,
+ e_offset_position offset_position);
+
+ template
+ void draw_quad_mesh(GCAgg &gc,
+ agg::trans_affine &master_transform,
+ size_t mesh_width,
+ size_t mesh_height,
+ CoordinateArray &coordinates,
+ OffsetArray &offsets,
+ agg::trans_affine &offset_trans,
+ ColorArray &facecolors,
+ bool antialiased,
+ ColorArray &edgecolors);
+
+ template
+ void draw_gouraud_triangle(GCAgg &gc,
+ PointArray &points,
+ ColorArray &colors,
+ agg::trans_affine &trans);
+
+ template
+ void draw_gouraud_triangles(GCAgg &gc,
+ PointArray &points,
+ ColorArray &colors,
+ agg::trans_affine &trans);
+
+ void tostring_rgb(uint8_t *buf);
+ void tostring_argb(uint8_t *buf);
+ void tostring_bgra(uint8_t *buf);
+ 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);
- static const size_t PIXELS_PER_INCH;
unsigned int width, height;
double dpi;
- size_t NUMBYTES; //the number of bytes in buffer
+ size_t NUMBYTES; // the number of bytes in buffer
agg::int8u *pixBuffer;
agg::rendering_buffer renderingBuffer;
@@ -237,90 +233,1072 @@ class RendererAgg: public Py::PythonExtension
renderer_bin rendererBin;
rasterizer theRasterizer;
- Py::Object lastclippath;
+ void *lastclippath;
agg::trans_affine lastclippath_transform;
static const size_t HATCH_SIZE = 72;
agg::int8u hatchBuffer[HATCH_SIZE * HATCH_SIZE * 4];
agg::rendering_buffer hatchRenderingBuffer;
- const int debug;
-
agg::rgba _fill_color;
+ protected:
+ inline double points_to_pixels(double points)
+ {
+ return points * dpi / 72.0;
+ }
+
+ template
+ void set_clipbox(const agg::rect_d &cliprect, R &rasterizer);
+
+ bool render_clippath(py::PathIterator &clippath, const agg::trans_affine &clippath_trans);
+
+ template
+ void _draw_path(PathIteratorType &path, bool has_clippath, const facepair_t &face, GCAgg &gc);
+
+ template
+ void _draw_path_collection_generic(GCAgg &gc,
+ agg::trans_affine master_transform,
+ const agg::rect_d &cliprect,
+ PathIterator &clippath,
+ const agg::trans_affine &clippath_trans,
+ PathGenerator &path_generator,
+ TransformArray &transforms,
+ OffsetArray &offsets,
+ const agg::trans_affine &offset_trans,
+ ColorArray &facecolors,
+ ColorArray &edgecolors,
+ LineWidthArray &linewidths,
+ DashesVector &linestyles,
+ AntialiasedArray &antialiaseds,
+ e_offset_position offset_position,
+ int check_snap,
+ int has_curves);
-protected:
- double points_to_pixels(const Py::Object& points);
- agg::rgba rgb_to_color(const Py::SeqBase& rgb, double alpha);
- facepair_t _get_rgba_face(const Py::Object& rgbFace, double alpha, bool forced_alpha);
-
- template
- void set_clipbox(const Py::Object& cliprect, R& rasterizer);
-
- bool render_clippath(const Py::Object& clippath, const agg::trans_affine& clippath_trans);
-
- template
- void _draw_path(PathIteratorType& path, bool has_clippath,
- const facepair_t& face, const GCAgg& gc);
-
- template
- Py::Object
- _draw_path_collection_generic
- (GCAgg& gc,
- agg::trans_affine master_transform,
- const Py::Object& cliprect,
- const Py::Object& clippath,
- const agg::trans_affine& clippath_trans,
- const PathGenerator& path_generator,
- const Py::Object& transforms_obj,
- const Py::Object& offsets_obj,
- const agg::trans_affine& offset_trans,
- const Py::Object& facecolors_obj,
- const Py::Object& edgecolors_obj,
- const Py::SeqBase& linewidths,
- const Py::SeqBase& linestyles_obj,
- const Py::SeqBase& antialiaseds,
- const bool data_offsets);
-
- void
- _draw_gouraud_triangle(
- const double* points, const double* colors,
- agg::trans_affine trans, bool has_clippath);
-
-private:
+ template
+ void _draw_gouraud_triangle(PointArray &points,
+ ColorArray &colors,
+ agg::trans_affine trans,
+ bool has_clippath);
+
+ private:
void create_alpha_buffers();
// prevent copying
- RendererAgg(const RendererAgg&);
- RendererAgg& operator=(const RendererAgg&);
+ RendererAgg(const RendererAgg &);
+ RendererAgg &operator=(const RendererAgg &);
};
-// the extension module
-class _backend_agg_module : public Py::ExtensionModule<_backend_agg_module>
+/***************************************************************************
+ * Implementation
+ */
+
+template
+inline void
+RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, GCAgg &gc)
+{
+ typedef agg::conv_stroke stroke_t;
+ typedef agg::conv_dash dash_t;
+ typedef agg::conv_stroke stroke_dash_t;
+ typedef agg::pixfmt_amask_adaptor pixfmt_amask_type;
+ typedef agg::renderer_base amask_ren_type;
+ typedef agg::renderer_scanline_aa_solid amask_aa_renderer_type;
+ typedef agg::renderer_scanline_bin_solid amask_bin_renderer_type;
+
+ // Render face
+ if (face.first) {
+ theRasterizer.add_path(path);
+
+ if (gc.isaa) {
+ if (has_clippath) {
+ pixfmt_amask_type pfa(pixFmt, alphaMask);
+ amask_ren_type r(pfa);
+ amask_aa_renderer_type ren(r);
+ ren.color(face.second);
+ agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren);
+ } else {
+ rendererAA.color(face.second);
+ agg::render_scanlines(theRasterizer, slineP8, rendererAA);
+ }
+ } else {
+ if (has_clippath) {
+ pixfmt_amask_type pfa(pixFmt, alphaMask);
+ amask_ren_type r(pfa);
+ amask_bin_renderer_type ren(r);
+ ren.color(face.second);
+ agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren);
+ } else {
+ rendererBin.color(face.second);
+ agg::render_scanlines(theRasterizer, slineP8, rendererBin);
+ }
+ }
+ }
+
+ // Render hatch
+ if (gc.has_hatchpath()) {
+ // Reset any clipping that may be in effect, since we'll be
+ // drawing the hatch in a scratch buffer at origin (0, 0)
+ theRasterizer.reset_clipping();
+ rendererBase.reset_clipping(true);
+
+ // Create and transform the path
+ typedef agg::conv_transform hatch_path_trans_t;
+ typedef agg::conv_curve hatch_path_curve_t;
+ typedef agg::conv_stroke hatch_path_stroke_t;
+
+ py::PathIterator hatch_path(gc.hatchpath);
+ agg::trans_affine hatch_trans;
+ hatch_trans *= agg::trans_affine_scaling(1.0, -1.0);
+ hatch_trans *= agg::trans_affine_translation(0.0, 1.0);
+ hatch_trans *= agg::trans_affine_scaling(HATCH_SIZE, HATCH_SIZE);
+ hatch_path_trans_t hatch_path_trans(hatch_path, hatch_trans);
+ hatch_path_curve_t hatch_path_curve(hatch_path_trans);
+ hatch_path_stroke_t hatch_path_stroke(hatch_path_curve);
+ hatch_path_stroke.width(1.0);
+ hatch_path_stroke.line_cap(agg::square_cap);
+
+ // Render the path into the hatch buffer
+ pixfmt hatch_img_pixf(hatchRenderingBuffer);
+ renderer_base rb(hatch_img_pixf);
+ renderer_aa rs(rb);
+ rb.clear(_fill_color);
+ rs.color(gc.color);
+
+ theRasterizer.add_path(hatch_path_curve);
+ agg::render_scanlines(theRasterizer, slineP8, rs);
+ theRasterizer.add_path(hatch_path_stroke);
+ agg::render_scanlines(theRasterizer, slineP8, rs);
+
+ // Put clipping back on, if originally set on entry to this
+ // function
+ set_clipbox(gc.cliprect, theRasterizer);
+ if (has_clippath) {
+ render_clippath(gc.clippath.path, gc.clippath.trans);
+ }
+
+ // Transfer the hatch to the main image buffer
+ typedef agg::image_accessor_wrap img_source_type;
+ typedef agg::span_pattern_rgba span_gen_type;
+ agg::span_allocator sa;
+ img_source_type img_src(hatch_img_pixf);
+ span_gen_type sg(img_src, 0, 0);
+ theRasterizer.add_path(path);
+
+ if (has_clippath) {
+ pixfmt_amask_type pfa(pixFmt, alphaMask);
+ amask_ren_type ren(pfa);
+ agg::render_scanlines_aa(theRasterizer, slineP8, ren, sa, sg);
+ } else {
+ agg::render_scanlines_aa(theRasterizer, slineP8, rendererBase, sa, sg);
+ }
+ }
+
+ // Render stroke
+ if (gc.linewidth != 0.0) {
+ double linewidth = points_to_pixels(gc.linewidth);
+ if (!gc.isaa) {
+ linewidth = (linewidth < 0.5) ? 0.5 : mpl_round(linewidth);
+ }
+ if (gc.dashes.size() == 0) {
+ stroke_t stroke(path);
+ stroke.width(points_to_pixels(gc.linewidth));
+ stroke.line_cap(gc.cap);
+ stroke.line_join(gc.join);
+ theRasterizer.add_path(stroke);
+ } else {
+ dash_t dash(path);
+ gc.dashes.dash_to_stroke(dash, dpi, gc.isaa);
+ stroke_dash_t stroke(dash);
+ stroke.line_cap(gc.cap);
+ stroke.line_join(gc.join);
+ stroke.width(linewidth);
+ theRasterizer.add_path(stroke);
+ }
+
+ if (gc.isaa) {
+ if (has_clippath) {
+ pixfmt_amask_type pfa(pixFmt, alphaMask);
+ amask_ren_type r(pfa);
+ amask_aa_renderer_type ren(r);
+ ren.color(gc.color);
+ agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren);
+ } else {
+ rendererAA.color(gc.color);
+ agg::render_scanlines(theRasterizer, slineP8, rendererAA);
+ }
+ } else {
+ if (has_clippath) {
+ pixfmt_amask_type pfa(pixFmt, alphaMask);
+ amask_ren_type r(pfa);
+ amask_bin_renderer_type ren(r);
+ ren.color(gc.color);
+ agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren);
+ } else {
+ rendererBin.color(gc.color);
+ agg::render_scanlines(theRasterizer, slineBin, rendererBin);
+ }
+ }
+ }
+}
+
+template
+inline void
+RendererAgg::draw_path(GCAgg &gc, PathIterator &path, agg::trans_affine &trans, agg::rgba &color)
+{
+ typedef agg::conv_transform transformed_path_t;
+ typedef PathNanRemover nan_removed_t;
+ typedef PathClipper clipped_t;
+ typedef PathSnapper snapped_t;
+ typedef PathSimplifier simplify_t;
+ typedef agg::conv_curve curve_t;
+ typedef Sketch sketch_t;
+
+ facepair_t face(color.a != 0.0, color);
+
+ theRasterizer.reset_clipping();
+ rendererBase.reset_clipping(true);
+ set_clipbox(gc.cliprect, theRasterizer);
+ bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans);
+
+ trans *= agg::trans_affine_scaling(1.0, -1.0);
+ trans *= agg::trans_affine_translation(0.0, (double)height);
+ bool clip = !face.first && gc.has_hatchpath() && !path.has_curves();
+ bool simplify = path.should_simplify() && clip;
+ double snapping_linewidth = points_to_pixels(gc.linewidth);
+ if (gc.color.a == 0.0) {
+ snapping_linewidth = 0.0;
+ }
+
+ transformed_path_t tpath(path, trans);
+ nan_removed_t nan_removed(tpath, true, path.has_curves());
+ clipped_t clipped(nan_removed, clip, width, height);
+ snapped_t snapped(clipped, gc.snap_mode, path.total_vertices(), snapping_linewidth);
+ simplify_t simplified(snapped, simplify, path.simplify_threshold());
+ curve_t curve(simplified);
+ sketch_t sketch(curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness);
+
+ _draw_path(sketch, has_clippath, face, gc);
+}
+
+template
+inline void RendererAgg::draw_markers(GCAgg &gc,
+ PathIterator &marker_path,
+ agg::trans_affine &marker_trans,
+ PathIterator &path,
+ agg::trans_affine &trans,
+ agg::rgba color)
{
-public:
- _backend_agg_module()
- : Py::ExtensionModule<_backend_agg_module>("_backend_agg")
+ typedef agg::conv_transform transformed_path_t;
+ typedef PathSnapper snap_t;
+ typedef agg::conv_curve curve_t;
+ typedef agg::conv_stroke stroke_t;
+ typedef agg::pixfmt_amask_adaptor pixfmt_amask_type;
+ typedef agg::renderer_base amask_ren_type;
+ typedef agg::renderer_scanline_aa_solid amask_aa_renderer_type;
+
+ // Deal with the difference in y-axis direction
+ marker_trans *= agg::trans_affine_scaling(1.0, -1.0);
+ trans *= agg::trans_affine_scaling(1.0, -1.0);
+ trans *= agg::trans_affine_translation(0.5, (double)height + 0.5);
+
+ transformed_path_t marker_path_transformed(marker_path, marker_trans);
+ snap_t marker_path_snapped(marker_path_transformed,
+ gc.snap_mode,
+ marker_path.total_vertices(),
+ points_to_pixels(gc.linewidth));
+ curve_t marker_path_curve(marker_path_snapped);
+
+ transformed_path_t path_transformed(path, trans);
+ snap_t path_snapped(path_transformed, SNAP_FALSE, path.total_vertices(), 0.0);
+ curve_t path_curve(path_snapped);
+ path_curve.rewind(0);
+
+ facepair_t face(color.a != 0.0, color);
+
+ // maxim's suggestions for cached scanlines
+ agg::scanline_storage_aa8 scanlines;
+ theRasterizer.reset();
+ theRasterizer.reset_clipping();
+ rendererBase.reset_clipping(true);
+
+ agg::int8u staticFillCache[MARKER_CACHE_SIZE];
+ agg::int8u staticStrokeCache[MARKER_CACHE_SIZE];
+ agg::int8u *fillCache = staticFillCache;
+ agg::int8u *strokeCache = staticStrokeCache;
+
+ try
{
- RendererAgg::init_type();
- BufferRegion::init_type();
+ unsigned fillSize = 0;
+ if (face.first) {
+ theRasterizer.add_path(marker_path_curve);
+ agg::render_scanlines(theRasterizer, slineP8, scanlines);
+ fillSize = scanlines.byte_size();
+ if (fillSize >= MARKER_CACHE_SIZE) {
+ fillCache = new agg::int8u[fillSize];
+ }
+ scanlines.serialize(fillCache);
+ }
+
+ stroke_t stroke(marker_path_curve);
+ stroke.width(points_to_pixels(gc.linewidth));
+ stroke.line_cap(gc.cap);
+ stroke.line_join(gc.join);
+ theRasterizer.reset();
+ theRasterizer.add_path(stroke);
+ agg::render_scanlines(theRasterizer, slineP8, scanlines);
+ unsigned strokeSize = scanlines.byte_size();
+ if (strokeSize >= MARKER_CACHE_SIZE) {
+ strokeCache = new agg::int8u[strokeSize];
+ }
+ scanlines.serialize(strokeCache);
+
+ theRasterizer.reset_clipping();
+ rendererBase.reset_clipping(true);
+ set_clipbox(gc.cliprect, rendererBase);
+ bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans);
- add_keyword_method("RendererAgg", &_backend_agg_module::new_renderer,
- "RendererAgg(width, height, dpi)");
- initialize("The agg rendering backend");
+ double x, y;
+
+ agg::serialized_scanlines_adaptor_aa8 sa;
+ agg::serialized_scanlines_adaptor_aa8::embedded_scanline sl;
+
+ agg::rect_d clipping_rect(-1.0 - scanlines.max_x(),
+ -1.0 - scanlines.max_y(),
+ 1.0 + width - scanlines.min_x(),
+ 1.0 + height - scanlines.min_y());
+
+ if (has_clippath) {
+ while (path_curve.vertex(&x, &y) != agg::path_cmd_stop) {
+ if (MPL_notisfinite64(x) || MPL_notisfinite64(y)) {
+ continue;
+ }
+
+ /* These values are correctly snapped above -- so we don't want
+ to round here, we really only want to truncate */
+ x = floor(x);
+ y = floor(y);
+
+ // Cull points outside the boundary of the image.
+ // Values that are too large may overflow and create
+ // segfaults.
+ // http://sourceforge.net/tracker/?func=detail&aid=2865490&group_id=80706&atid=560720
+ if (!clipping_rect.hit_test(x, y)) {
+ continue;
+ }
+
+ pixfmt_amask_type pfa(pixFmt, alphaMask);
+ amask_ren_type r(pfa);
+ amask_aa_renderer_type ren(r);
+
+ if (face.first) {
+ ren.color(face.second);
+ sa.init(fillCache, fillSize, x, y);
+ agg::render_scanlines(sa, sl, ren);
+ }
+ ren.color(gc.color);
+ sa.init(strokeCache, strokeSize, x, y);
+ agg::render_scanlines(sa, sl, ren);
+ }
+ } else {
+ while (path_curve.vertex(&x, &y) != agg::path_cmd_stop) {
+ if (MPL_notisfinite64(x) || MPL_notisfinite64(y)) {
+ continue;
+ }
+
+ /* These values are correctly snapped above -- so we don't want
+ to round here, we really only want to truncate */
+ x = floor(x);
+ y = floor(y);
+
+ // Cull points outside the boundary of the image.
+ // Values that are too large may overflow and create
+ // segfaults.
+ // http://sourceforge.net/tracker/?func=detail&aid=2865490&group_id=80706&atid=560720
+ if (!clipping_rect.hit_test(x, y)) {
+ continue;
+ }
+
+ if (face.first) {
+ rendererAA.color(face.second);
+ sa.init(fillCache, fillSize, x, y);
+ agg::render_scanlines(sa, sl, rendererAA);
+ }
+
+ rendererAA.color(gc.color);
+ sa.init(strokeCache, strokeSize, x, y);
+ agg::render_scanlines(sa, sl, rendererAA);
+ }
+ }
+ }
+ catch (...)
+ {
+ if (fillCache != staticFillCache)
+ delete[] fillCache;
+ if (strokeCache != staticStrokeCache)
+ delete[] strokeCache;
+ theRasterizer.reset_clipping();
+ rendererBase.reset_clipping(true);
+ throw;
}
- virtual ~_backend_agg_module() {}
+ if (fillCache != staticFillCache)
+ delete[] fillCache;
+ if (strokeCache != staticStrokeCache)
+ delete[] strokeCache;
-private:
+ theRasterizer.reset_clipping();
+ rendererBase.reset_clipping(true);
+}
- Py::Object new_renderer(const Py::Tuple &args, const Py::Dict &kws);
+/**
+ * This is a custom span generator that converts spans in the
+ * 8-bit inverted greyscale font buffer to rgba that agg can use.
+ */
+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;
- // prevent copying
- _backend_agg_module(const _backend_agg_module&);
- _backend_agg_module& operator=(const _backend_agg_module&);
+ private:
+ child_type *_gen;
+ color_type _color;
+ span_alloc_type _allocator;
+
+ public:
+ font_to_rgba(child_type *gen, color_type color) : _gen(gen), _color(color)
+ {
+ }
+
+ inline void generate(color_type *output_span, int x, int y, unsigned len)
+ {
+ _allocator.allocate(len);
+ child_color_type *input_span = _allocator.span();
+ _gen->generate(input_span, x, y, len);
+
+ do {
+ *output_span = _color;
+ output_span->a = ((unsigned int)_color.a * (unsigned int)input_span->v) >> 8;
+ ++output_span;
+ ++input_span;
+ } while (--len);
+ }
+
+ void prepare()
+ {
+ _gen->prepare();
+ }
+};
+
+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_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 agg::renderer_scanline_aa
+ renderer_type;
+
+ theRasterizer.reset_clipping();
+ rendererBase.reset_clipping(true);
+ set_clipbox(gc.cliprect, theRasterizer);
+
+ agg::rendering_buffer srcbuf(
+ image.data(), (unsigned)image.dim(1), (unsigned)image.dim(0), (unsigned)image.dim(1));
+ agg::pixfmt_gray8 pixf_img(srcbuf);
+
+ agg::trans_affine mtx;
+ mtx *= agg::trans_affine_translation(0, -image.dim(0));
+ mtx *= agg::trans_affine_rotation(-angle * agg::pi / 180.0);
+ mtx *= agg::trans_affine_translation(x, y);
+
+ agg::path_storage rect;
+ rect.move_to(0, 0);
+ rect.line_to(image.dim(1), 0);
+ rect.line_to(image.dim(1), image.dim(0));
+ rect.line_to(0, image.dim(0));
+ rect.line_to(0, 0);
+ agg::conv_transform rect2(rect, mtx);
+
+ agg::trans_affine inv_mtx(mtx);
+ inv_mtx.invert();
+
+ agg::image_filter_lut filter;
+ filter.calculate(agg::image_filter_spline36());
+ interpolator_type interpolator(inv_mtx);
+ color_span_alloc_type sa;
+ image_accessor_type ia(pixf_img, 0);
+ image_span_gen_type image_span_generator(ia, interpolator, filter);
+ span_gen_type output_span_generator(&image_span_generator, gc.color);
+ renderer_type ri(rendererBase, sa, output_span_generator);
+
+ theRasterizer.add_path(rect2);
+ agg::render_scanlines(theRasterizer, slineP8, ri);
+}
+
+class span_conv_alpha
+{
+ public:
+ typedef agg::rgba8 color_type;
+
+ double m_alpha;
+
+ span_conv_alpha(double alpha) : m_alpha(alpha)
+ {
+ }
+
+ void prepare()
+ {
+ }
+ void generate(color_type *span, int x, int y, unsigned len) const
+ {
+ do {
+ span->a = (agg::int8u)((double)span->a * m_alpha);
+ ++span;
+ } while (--len);
+ }
+};
+
+template
+inline void RendererAgg::draw_image(GCAgg &gc,
+ double x,
+ double y,
+ ImageArray &image,
+ double w,
+ double h,
+ agg::trans_affine trans,
+ bool resize)
+{
+ double alpha = gc.alpha;
+
+ theRasterizer.reset_clipping();
+ rendererBase.reset_clipping(true);
+ set_clipbox(gc.cliprect, theRasterizer);
+ bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans);
+
+ agg::rendering_buffer buffer;
+ buffer.attach(
+ image.data(), (unsigned)image.dim(1), (unsigned)image.dim(0), -(int)image.dim(1) * 4);
+ pixfmt pixf(buffer);
+
+ if (resize | has_clippath) {
+ agg::trans_affine mtx;
+ agg::path_storage rect;
+
+ if (resize) {
+ mtx *= agg::trans_affine_scaling(1, -1);
+ mtx *= agg::trans_affine_translation(0, image.dim(0));
+ mtx *= agg::trans_affine_scaling(w / (image.dim(1)), h / (image.dim(0)));
+ mtx *= agg::trans_affine_translation(x, y);
+ mtx *= trans;
+ mtx *= agg::trans_affine_scaling(1.0, -1.0);
+ mtx *= agg::trans_affine_translation(0.0, (double)height);
+ } else {
+ mtx *= agg::trans_affine_translation((int)x, (int)(height - (y + image.dim(0))));
+ }
+
+ rect.move_to(0, 0);
+ rect.line_to(image.dim(1), 0);
+ rect.line_to(image.dim(1), image.dim(0));
+ rect.line_to(0, image.dim(0));
+ rect.line_to(0, 0);
+
+ agg::conv_transform rect2(rect, mtx);
+
+ agg::trans_affine inv_mtx(mtx);
+ inv_mtx.invert();
+
+ 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;
+
+ color_span_alloc_type sa;
+ image_accessor_type ia(pixf, agg::rgba8(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 spans(image_span_generator, conv_alpha);
+
+ if (has_clippath) {
+ typedef agg::pixfmt_amask_adaptor pixfmt_amask_type;
+ typedef agg::renderer_base amask_ren_type;
+ typedef agg::renderer_scanline_aa
+ renderer_type_alpha;
+
+ pixfmt_amask_type pfa(pixFmt, alphaMask);
+ amask_ren_type r(pfa);
+ renderer_type_alpha ri(r, sa, spans);
+
+ theRasterizer.add_path(rect2);
+ agg::render_scanlines(theRasterizer, scanlineAlphaMask, ri);
+ } else {
+ typedef agg::renderer_base ren_type;
+ typedef agg::renderer_scanline_aa
+ renderer_type;
+
+ ren_type r(pixFmt);
+ renderer_type ri(r, sa, spans);
+
+ theRasterizer.add_path(rect2);
+ agg::render_scanlines(theRasterizer, slineP8, ri);
+ }
+
+ } else {
+ set_clipbox(gc.cliprect, rendererBase);
+ rendererBase.blend_from(
+ pixf, 0, (int)x, (int)(height - (y + image.dim(0))), (agg::int8u)(alpha * 255));
+ }
+
+ rendererBase.reset_clipping(true);
+}
+
+template
+inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc,
+ agg::trans_affine master_transform,
+ const agg::rect_d &cliprect,
+ PathIterator &clippath,
+ const agg::trans_affine &clippath_trans,
+ PathGenerator &path_generator,
+ TransformArray &transforms,
+ OffsetArray &offsets,
+ const agg::trans_affine &offset_trans,
+ ColorArray &facecolors,
+ ColorArray &edgecolors,
+ LineWidthArray &linewidths,
+ DashesVector &linestyles,
+ AntialiasedArray &antialiaseds,
+ e_offset_position offset_position,
+ int check_snap,
+ int has_curves)
+{
+ typedef agg::conv_transform transformed_path_t;
+ typedef PathNanRemover nan_removed_t;
+ typedef PathClipper clipped_t;
+ typedef PathSnapper snapped_t;
+ typedef agg::conv_curve snapped_curve_t;
+ typedef agg::conv_curve curve_t;
+
+ if (offsets.dim(0) != 0 && offsets.dim(1) != 2) {
+ throw "Offsets array must be Nx2 or empty";
+ }
+
+ if (facecolors.dim(0) != 0 && facecolors.dim(1) != 4) {
+ throw "Facecolors array must be a Nx4 array or empty";
+ }
+
+ if (edgecolors.dim(0) != 0 && edgecolors.dim(1) != 4) {
+ throw "Edgecolors array must by Nx4 or empty";
+ }
+
+ if (transforms.dim(0) != 0 && (transforms.dim(1) != 3 || transforms.dim(2) != 3)) {
+ throw "Transforms array must by Nx3x3 or empty";
+ }
+
+ size_t Npaths = path_generator.num_paths();
+ size_t Noffsets = offsets.size();
+ size_t N = std::max(Npaths, Noffsets);
+
+ size_t Ntransforms = transforms.size();
+ size_t Nfacecolors = facecolors.size();
+ size_t Nedgecolors = edgecolors.size();
+ size_t Nlinewidths = linewidths.size();
+ size_t Nlinestyles = std::min(linestyles.size(), N);
+ size_t Naa = antialiaseds.size();
+
+ if ((Nfacecolors == 0 && Nedgecolors == 0) || Npaths == 0) {
+ return;
+ }
+
+ // Handle any clipping globally
+ theRasterizer.reset_clipping();
+ rendererBase.reset_clipping(true);
+ set_clipbox(cliprect, theRasterizer);
+ bool has_clippath = render_clippath(clippath, clippath_trans);
+
+ // Set some defaults, assuming no face or edge
+ gc.linewidth = 0.0;
+ facepair_t face;
+ face.first = Nfacecolors != 0;
+ agg::trans_affine trans;
+
+ for (int i = 0; i < (int)N; ++i) {
+ typename PathGenerator::path_iterator path = path_generator(i);
+
+ if (Ntransforms) {
+ typename TransformArray::sub_t subtrans = transforms[i % Ntransforms];
+ trans = agg::trans_affine(subtrans(0, 0),
+ subtrans(1, 0),
+ subtrans(0, 1),
+ subtrans(1, 1),
+ subtrans(0, 2),
+ subtrans(1, 2));
+ } else {
+ trans = master_transform;
+ }
+
+ if (Noffsets) {
+ double xo = offsets(i % Noffsets, 0);
+ double yo = offsets(i % Noffsets, 1);
+ offset_trans.transform(&xo, &yo);
+ if (offset_position == OFFSET_POSITION_DATA) {
+ trans = agg::trans_affine_translation(xo, yo) * trans;
+ } else {
+ trans *= agg::trans_affine_translation(xo, yo);
+ }
+ }
+
+ // These transformations must be done post-offsets
+ trans *= agg::trans_affine_scaling(1.0, -1.0);
+ trans *= agg::trans_affine_translation(0.0, (double)height);
+
+ if (Nfacecolors) {
+ typename ColorArray::sub_t facecolor = facecolors[i % Nfacecolors];
+ face.second = agg::rgba(facecolor(0), facecolor(1), facecolor(2), facecolor(3));
+ }
+
+ if (Nedgecolors) {
+ typename ColorArray::sub_t edgecolor = edgecolors[i % Nedgecolors];
+ gc.color = agg::rgba(edgecolor(0), edgecolor(1), edgecolor(2), edgecolor(3));
+
+ if (Nlinewidths) {
+ gc.linewidth = linewidths(i % Nlinewidths);
+ } else {
+ gc.linewidth = 1.0;
+ }
+ if (Nlinestyles) {
+ gc.dashes = linestyles[i % Nlinestyles];
+ }
+ }
+
+ bool do_clip = !face.first && !gc.has_hatchpath() && !has_curves;
+
+ if (check_snap) {
+ gc.isaa = antialiaseds(i % Naa);
+
+ transformed_path_t tpath(path, trans);
+ nan_removed_t nan_removed(tpath, true, has_curves);
+ clipped_t clipped(nan_removed, do_clip, width, height);
+ snapped_t snapped(
+ clipped, gc.snap_mode, path.total_vertices(), points_to_pixels(gc.linewidth));
+ if (has_curves) {
+ snapped_curve_t curve(snapped);
+ _draw_path(curve, has_clippath, face, gc);
+ } else {
+ _draw_path(snapped, has_clippath, face, gc);
+ }
+ } else {
+ gc.isaa = antialiaseds(i % Naa);
+
+ transformed_path_t tpath(path, trans);
+ nan_removed_t nan_removed(tpath, true, has_curves);
+ clipped_t clipped(nan_removed, do_clip, width, height);
+ if (has_curves) {
+ curve_t curve(clipped);
+ _draw_path(curve, has_clippath, face, gc);
+ } else {
+ _draw_path(clipped, has_clippath, face, gc);
+ }
+ }
+ }
+}
+
+template
+inline void RendererAgg::draw_path_collection(GCAgg &gc,
+ agg::trans_affine &master_transform,
+ PathGenerator &path,
+ TransformArray &transforms,
+ OffsetArray &offsets,
+ agg::trans_affine &offset_trans,
+ ColorArray &facecolors,
+ ColorArray &edgecolors,
+ LineWidthArray &linewidths,
+ DashesVector &linestyles,
+ AntialiasedArray &antialiaseds,
+ e_offset_position offset_position)
+{
+ _draw_path_collection_generic(gc,
+ master_transform,
+ gc.cliprect,
+ gc.clippath.path,
+ gc.clippath.trans,
+ path,
+ transforms,
+ offsets,
+ offset_trans,
+ facecolors,
+ edgecolors,
+ linewidths,
+ linestyles,
+ antialiaseds,
+ offset_position,
+ 1,
+ 1);
+}
+
+template
+class QuadMeshGenerator
+{
+ unsigned m_meshWidth;
+ unsigned m_meshHeight;
+ CoordinateArray m_coordinates;
+
+ class QuadMeshPathIterator
+ {
+ unsigned m_iterator;
+ unsigned m_m, m_n;
+ const CoordinateArray *m_coordinates;
+
+ public:
+ QuadMeshPathIterator(unsigned m, unsigned n, const CoordinateArray *coordinates)
+ : m_iterator(0), m_m(m), m_n(n), m_coordinates(coordinates)
+ {
+ }
+
+ private:
+ inline unsigned vertex(unsigned idx, double *x, double *y)
+ {
+ size_t m = m_m + ((idx & 0x2) >> 1);
+ size_t n = m_n + (((idx + 1) & 0x2) >> 1);
+ *x = (*m_coordinates)(n, m, 0);
+ *y = (*m_coordinates)(n, m, 1);
+ return (idx) ? agg::path_cmd_line_to : agg::path_cmd_move_to;
+ }
+
+ public:
+ inline unsigned vertex(double *x, double *y)
+ {
+ if (m_iterator >= total_vertices()) {
+ return agg::path_cmd_stop;
+ }
+ return vertex(m_iterator++, x, y);
+ }
+
+ inline void rewind(unsigned path_id)
+ {
+ m_iterator = path_id;
+ }
+
+ inline unsigned total_vertices()
+ {
+ return 5;
+ }
+
+ inline bool should_simplify()
+ {
+ return false;
+ }
+ };
+
+ public:
+ typedef QuadMeshPathIterator path_iterator;
+
+ inline QuadMeshGenerator(unsigned meshWidth, unsigned meshHeight, CoordinateArray &coordinates)
+ : m_meshWidth(meshWidth), m_meshHeight(meshHeight), m_coordinates(coordinates)
+ {
+ }
+
+ inline size_t num_paths() const
+ {
+ return m_meshWidth * m_meshHeight;
+ }
+
+ inline path_iterator operator()(size_t i) const
+ {
+ return QuadMeshPathIterator(i % m_meshWidth, i / m_meshWidth, &m_coordinates);
+ }
};
+template
+inline void RendererAgg::draw_quad_mesh(GCAgg &gc,
+ agg::trans_affine &master_transform,
+ size_t mesh_width,
+ size_t mesh_height,
+ CoordinateArray &coordinates,
+ OffsetArray &offsets,
+ agg::trans_affine &offset_trans,
+ ColorArray &facecolors,
+ bool antialiased,
+ ColorArray &edgecolors)
+{
+ QuadMeshGenerator path_generator(mesh_width, mesh_height, coordinates);
+
+ array::empty transforms;
+ array::scalar linewidths(points_to_pixels(gc.linewidth));
+ array::scalar antialiaseds(antialiased);
+ DashesVector linestyles;
+ ColorArray *edgecolors_ptr = &edgecolors;
+
+ if (edgecolors.size() == 0) {
+ if (antialiased) {
+ edgecolors_ptr = &facecolors;
+ }
+ }
+ _draw_path_collection_generic(gc,
+ master_transform,
+ gc.cliprect,
+ gc.clippath.path,
+ gc.clippath.trans,
+ path_generator,
+ transforms,
+ offsets,
+ offset_trans,
+ facecolors,
+ *edgecolors_ptr,
+ linewidths,
+ linestyles,
+ antialiaseds,
+ OFFSET_POSITION_FIGURE,
+ 0,
+ 0);
+}
+
+template
+inline void RendererAgg::_draw_gouraud_triangle(PointArray &points,
+ ColorArray &colors,
+ agg::trans_affine trans,
+ bool has_clippath)
+{
+ typedef agg::rgba8 color_t;
+ typedef agg::span_gouraud_rgba span_gen_t;
+ typedef agg::span_allocator span_alloc_t;
+
+ trans *= agg::trans_affine_scaling(1.0, -1.0);
+ trans *= agg::trans_affine_translation(0.0, (double)height);
+
+ double tpoints[3][2];
+
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 2; ++j) {
+ tpoints[i][j] = points(i, j);
+ }
+ trans.transform(&tpoints[i][0], &tpoints[i][1]);
+ }
+
+ span_alloc_t span_alloc;
+ span_gen_t span_gen;
+
+ span_gen.colors(agg::rgba(colors(0, 0), colors(0, 1), colors(0, 2), colors(0, 3)),
+ agg::rgba(colors(1, 0), colors(1, 1), colors(1, 2), colors(1, 3)),
+ agg::rgba(colors(2, 0), colors(2, 1), colors(2, 2), colors(2, 3)));
+ span_gen.triangle(tpoints[0][0],
+ tpoints[0][1],
+ tpoints[1][0],
+ tpoints[1][1],
+ tpoints[2][0],
+ tpoints[2][1],
+ 0.5);
+
+ theRasterizer.add_path(span_gen);
+
+ if (has_clippath) {
+ typedef agg::pixfmt_amask_adaptor pixfmt_amask_type;
+ typedef agg::renderer_base amask_ren_type;
+ typedef agg::renderer_scanline_aa
+ amask_aa_renderer_type;
+
+ pixfmt_amask_type pfa(pixFmt, alphaMask);
+ amask_ren_type r(pfa);
+ amask_aa_renderer_type ren(r, span_alloc, span_gen);
+ agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren);
+ } else {
+ agg::render_scanlines_aa(theRasterizer, slineP8, rendererBase, span_alloc, span_gen);
+ }
+}
+
+template
+inline void RendererAgg::draw_gouraud_triangle(GCAgg &gc,
+ PointArray &points,
+ ColorArray &colors,
+ agg::trans_affine &trans)
+{
+ theRasterizer.reset_clipping();
+ rendererBase.reset_clipping(true);
+ set_clipbox(gc.cliprect, theRasterizer);
+ bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans);
+
+ if (points.dim(0) != 3 || points.dim(1) != 2) {
+ throw "points must be a 3x2 array";
+ }
+
+ if (colors.dim(0) != 3 || colors.dim(1) != 4) {
+ throw "colors must be a 3x4 array";
+ }
+
+ _draw_gouraud_triangle(points, colors, trans, has_clippath);
+}
+
+template
+inline void RendererAgg::draw_gouraud_triangles(GCAgg &gc,
+ PointArray &points,
+ ColorArray &colors,
+ agg::trans_affine &trans)
+{
+ theRasterizer.reset_clipping();
+ rendererBase.reset_clipping(true);
+ set_clipbox(gc.cliprect, theRasterizer);
+ bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans);
+
+ if (points.dim(1) != 3 || points.dim(2) != 2) {
+ throw "points must be a Nx3x2 array";
+ }
+
+ if (colors.dim(1) != 3 || colors.dim(2) != 4) {
+ throw "colors must be a Nx3x4 array";
+ }
+
+ if (points.dim(0) != colors.dim(0)) {
+ throw "points and colors arrays must be the same length";
+ }
+
+ for (int i = 0; i < points.dim(0); ++i) {
+ typename PointArray::sub_t point = points[i];
+ typename ColorArray::sub_t color = colors[i];
+
+ _draw_gouraud_triangle(point, color, trans, has_clippath);
+ }
+}
+
+template
+void RendererAgg::set_clipbox(const agg::rect_d &cliprect, R &rasterizer)
+{
+ // set the clip rectangle from the gc
+
+ if (cliprect.x1 != 0.0 || cliprect.y1 != 0.0 || cliprect.x2 != 0.0 || cliprect.y2 != 0.0) {
+ rasterizer.clip_box(std::max(int(floor(cliprect.x1 + 0.5)), 0),
+ std::max(int(floor(height - cliprect.y1 + 0.5)), 0),
+ std::min(int(floor(cliprect.x2 + 0.5)), int(width)),
+ std::min(int(floor(height - cliprect.y2 + 0.5)), int(height)));
+ } else {
+ rasterizer.clip_box(0, 0, width, height);
+ }
+}
#endif
diff --git a/src/_backend_agg_basic_types.h b/src/_backend_agg_basic_types.h
new file mode 100644
index 000000000000..cea2b7e14f0f
--- /dev/null
+++ b/src/_backend_agg_basic_types.h
@@ -0,0 +1,124 @@
+#ifndef __BACKEND_AGG_BASIC_TYPES_H__
+#define __BACKEND_AGG_BASIC_TYPES_H__
+
+/* Contains some simple types from the Agg backend that are also used
+ by other modules */
+
+#include
+
+#include "agg_color_rgba.h"
+#include "agg_math_stroke.h"
+#include "path_converters.h"
+
+#include "py_adaptors.h"
+
+struct ClipPath
+{
+ py::PathIterator path;
+ agg::trans_affine trans;
+};
+
+struct SketchParams
+{
+ double scale;
+ double length;
+ double randomness;
+};
+
+class Dashes
+{
+ typedef std::vector > dash_t;
+ double dash_offset;
+ dash_t dashes;
+
+ public:
+ double get_dash_offset() const
+ {
+ return dash_offset;
+ }
+ void set_dash_offset(double x)
+ {
+ dash_offset = x;
+ }
+ void add_dash_pair(double length, double skip)
+ {
+ dashes.push_back(std::make_pair(length, skip));
+ }
+ size_t size() const
+ {
+ return dashes.size();
+ }
+
+ template
+ void dash_to_stroke(T &stroke, double dpi, bool isaa)
+ {
+ for (dash_t::const_iterator i = dashes.begin(); i != dashes.end(); ++i) {
+ double val0 = i->first;
+ double val1 = i->second;
+ val0 = val0 * dpi / 72.0;
+ val1 = val1 * dpi / 72.0;
+ if (!isaa) {
+ val0 = (int)val0 + 0.5;
+ val1 = (int)val1 + 0.5;
+ }
+ stroke.add_dash(val0, val1);
+ }
+ }
+};
+
+typedef std::vector DashesVector;
+
+enum e_offset_position {
+ OFFSET_POSITION_FIGURE,
+ OFFSET_POSITION_DATA
+};
+
+class GCAgg
+{
+ public:
+ GCAgg()
+ : linewidth(1.0),
+ alpha(1.0),
+ cap(agg::butt_cap),
+ join(agg::round_join),
+ snap_mode(SNAP_FALSE)
+ {
+ }
+
+ ~GCAgg()
+ {
+ }
+
+ double linewidth;
+ double alpha;
+ bool forced_alpha;
+ agg::rgba color;
+ bool isaa;
+
+ agg::line_cap_e cap;
+ agg::line_join_e join;
+
+ agg::rect_d cliprect;
+
+ ClipPath clippath;
+
+ Dashes dashes;
+
+ e_snap_mode snap_mode;
+
+ py::PathIterator hatchpath;
+
+ SketchParams sketch;
+
+ bool has_hatchpath()
+ {
+ return hatchpath.total_vertices();
+ }
+
+ private:
+ // prevent copying
+ GCAgg(const GCAgg &);
+ GCAgg &operator=(const GCAgg &);
+};
+
+#endif
diff --git a/src/_backend_agg_wrapper.cpp b/src/_backend_agg_wrapper.cpp
new file mode 100644
index 000000000000..b83fb0e46c1c
--- /dev/null
+++ b/src/_backend_agg_wrapper.cpp
@@ -0,0 +1,730 @@
+#include "_backend_agg_wrapper.h"
+
+/**********************************************************************
+ * BufferRegion
+ * */
+
+static PyObject *PyBufferRegion_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyBufferRegion *self;
+ self = (PyBufferRegion *)type->tp_alloc(type, 0);
+ self->x = NULL;
+ return (PyObject *)self;
+}
+
+static void PyBufferRegion_dealloc(PyBufferRegion *self)
+{
+ delete self->x;
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static PyObject *PyBufferRegion_to_string(PyBufferRegion *self, PyObject *args, PyObject *kwds)
+{
+ return PyBytes_FromStringAndSize((const char *)self->x->get_data(),
+ self->x->get_height() * self->x->get_stride());
+}
+
+/* TODO: This doesn't seem to be used internally. Remove? */
+
+static PyObject *PyBufferRegion_set_x(PyBufferRegion *self, PyObject *args, PyObject *kwds)
+{
+ int x;
+ if (!PyArg_ParseTuple(args, "i:set_x", &x)) {
+ return NULL;
+ }
+ self->x->get_rect().x1 = x;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *PyBufferRegion_set_y(PyBufferRegion *self, PyObject *args, PyObject *kwds)
+{
+ int y;
+ if (!PyArg_ParseTuple(args, "i:set_y", &y)) {
+ return NULL;
+ }
+ self->x->get_rect().y1 = y;
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *PyBufferRegion_get_extents(PyBufferRegion *self, PyObject *args, PyObject *kwds)
+{
+ agg::rect_i rect = self->x->get_rect();
+
+ return Py_BuildValue("IIII", rect.x1, rect.y1, rect.x2, rect.y2);
+}
+
+static PyObject *PyBufferRegion_to_string_argb(PyBufferRegion *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *bufobj;
+ uint8_t *buf;
+
+ bufobj = PyBytes_FromStringAndSize(NULL, self->x->get_height() * self->x->get_stride());
+ buf = (uint8_t *)PyBytes_AS_STRING(bufobj);
+
+ CALL_CPP_CLEANUP("to_string_argb", (self->x->to_string_argb(buf)), Py_DECREF(bufobj));
+
+ return bufobj;
+}
+
+int PyBufferRegion_get_buffer(PyBufferRegion *self, Py_buffer *buf, int flags)
+{
+ Py_INCREF(self);
+ buf->obj = (PyObject *)self;
+ buf->buf = self->x->get_data();
+ buf->len = self->x->get_width() * self->x->get_height() * 4;
+ buf->readonly = 0;
+ buf->format = (char *)"B";
+ buf->ndim = 3;
+ self->shape[0] = self->x->get_height();
+ 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[1] = 4;
+ self->strides[2] = 1;
+ buf->strides = self->strides;
+ buf->suboffsets = NULL;
+ buf->itemsize = 1;
+ buf->internal = NULL;
+
+ return 1;
+}
+
+static PyTypeObject PyBufferRegionType;
+
+static PyTypeObject *PyBufferRegion_init_type(PyObject *m, PyTypeObject *type)
+{
+ static PyMethodDef methods[] = {
+ { "to_string", (PyCFunction)PyBufferRegion_to_string, METH_NOARGS, NULL },
+ { "to_string_argb", (PyCFunction)PyBufferRegion_to_string_argb, METH_NOARGS, NULL },
+ { "set_x", (PyCFunction)PyBufferRegion_set_x, METH_VARARGS, NULL },
+ { "set_y", (PyCFunction)PyBufferRegion_set_y, METH_VARARGS, NULL },
+ { "get_extents", (PyCFunction)PyBufferRegion_get_extents, METH_NOARGS, NULL },
+ { NULL }
+ };
+
+ static PyBufferProcs buffer_procs;
+ memset(&buffer_procs, 0, sizeof(PyBufferProcs));
+ buffer_procs.bf_getbuffer = (getbufferproc)PyBufferRegion_get_buffer;
+
+ memset(type, 0, sizeof(PyTypeObject));
+ type->tp_name = "matplotlib.backends._backend_agg.BufferRegion";
+ type->tp_basicsize = sizeof(PyBufferRegion);
+ type->tp_dealloc = (destructor)PyBufferRegion_dealloc;
+ type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_NEWBUFFER;
+ type->tp_methods = methods;
+ type->tp_new = PyBufferRegion_new;
+ type->tp_as_buffer = &buffer_procs;
+
+ if (PyType_Ready(type) < 0) {
+ return NULL;
+ }
+
+ /* Don't need to add to module, since you can't create buffer
+ regions directly from Python */
+
+ return type;
+}
+
+/**********************************************************************
+ * RendererAgg
+ * */
+
+static PyObject *PyRendererAgg_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyRendererAgg *self;
+ self = (PyRendererAgg *)type->tp_alloc(type, 0);
+ self->x = NULL;
+ return (PyObject *)self;
+}
+
+static int PyRendererAgg_init(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ unsigned int width;
+ unsigned int height;
+ double dpi;
+ int debug = 0;
+
+ if (!PyArg_ParseTuple(args, "IId|i:RendererAgg", &width, &height, &dpi, &debug)) {
+ return -1;
+ }
+
+ if (dpi <= 0.0) {
+ PyErr_SetString(PyExc_ValueError, "dpi must be positive");
+ return -1;
+ }
+
+ CALL_CPP_INIT("RendererAgg", self->x = new RendererAgg(width, height, dpi))
+
+ return 0;
+}
+
+static void PyRendererAgg_dealloc(PyRendererAgg *self)
+{
+ delete self->x;
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static PyObject *PyRendererAgg_draw_path(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ GCAgg gc;
+ py::PathIterator path;
+ agg::trans_affine trans;
+ PyObject *faceobj = NULL;
+ agg::rgba face;
+
+ if (!PyArg_ParseTuple(args,
+ "O&O&O&|O:draw_path",
+ &convert_gcagg,
+ &gc,
+ &convert_path,
+ &path,
+ &convert_trans_affine,
+ &trans,
+ &faceobj)) {
+ return NULL;
+ }
+
+ if (!convert_face(faceobj, gc, &face)) {
+ return NULL;
+ }
+
+ CALL_CPP("draw_path", (self->x->draw_path(gc, path, trans, face)));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *PyRendererAgg_draw_text_image(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ numpy::array_view image;
+ double x;
+ double y;
+ double angle;
+ GCAgg gc;
+
+ if (!PyArg_ParseTuple(args,
+ "O&dddO&:draw_text_image",
+ &image.converter_contiguous,
+ &image,
+ &x,
+ &y,
+ &angle,
+ &convert_gcagg,
+ &gc)) {
+ return NULL;
+ }
+
+ CALL_CPP("draw_text_image", (self->x->draw_text_image(gc, image, x, y, angle)));
+
+ Py_RETURN_NONE;
+}
+
+PyObject *PyRendererAgg_draw_markers(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ GCAgg gc;
+ py::PathIterator marker_path;
+ agg::trans_affine marker_path_trans;
+ py::PathIterator path;
+ agg::trans_affine trans;
+ PyObject *faceobj = NULL;
+ agg::rgba face;
+
+ if (!PyArg_ParseTuple(args,
+ "O&O&O&O&O&|O:draw_markers",
+ &convert_gcagg,
+ &gc,
+ &convert_path,
+ &marker_path,
+ &convert_trans_affine,
+ &marker_path_trans,
+ &convert_path,
+ &path,
+ &convert_trans_affine,
+ &trans,
+ &faceobj)) {
+ return NULL;
+ }
+
+ if (!convert_face(faceobj, gc, &face)) {
+ return NULL;
+ }
+
+ CALL_CPP("draw_markers",
+ (self->x->draw_markers(gc, marker_path, marker_path_trans, path, trans, face)));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *PyRendererAgg_draw_image(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ GCAgg gc;
+ double x;
+ double y;
+ numpy::array_view image;
+ double w = 0;
+ double h = 0;
+ agg::trans_affine trans;
+ bool resize = false;
+
+ if (!PyArg_ParseTuple(args,
+ "O&ddO&|ddO&:draw_image",
+ &convert_gcagg,
+ &gc,
+ &x,
+ &y,
+ &image.converter_contiguous,
+ &image,
+ &w,
+ &h,
+ &convert_trans_affine,
+ &trans)) {
+ return NULL;
+ }
+
+ if (PyTuple_Size(args) == 4) {
+ x = mpl_round(x);
+ y = mpl_round(y);
+ } else {
+ resize = true;
+ }
+
+ CALL_CPP("draw_image", (self->x->draw_image(gc, x, y, image, w, h, trans, resize)));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+PyRendererAgg_draw_path_collection(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ GCAgg gc;
+ agg::trans_affine master_transform;
+ PyObject *pathobj;
+ numpy::array_view transforms;
+ numpy::array_view offsets;
+ agg::trans_affine offset_trans;
+ numpy::array_view facecolors;
+ numpy::array_view edgecolors;
+ numpy::array_view linewidths;
+ DashesVector dashes;
+ numpy::array_view antialiaseds;
+ PyObject *ignored;
+ e_offset_position offset_position;
+
+ if (!PyArg_ParseTuple(args,
+ "O&O&OO&O&O&O&O&O&O&O&OO&:draw_path_collection",
+ &convert_gcagg,
+ &gc,
+ &convert_trans_affine,
+ &master_transform,
+ &pathobj,
+ &transforms.converter,
+ &transforms,
+ &offsets.converter,
+ &offsets,
+ &convert_trans_affine,
+ &offset_trans,
+ &facecolors.converter,
+ &facecolors,
+ &edgecolors.converter,
+ &edgecolors,
+ &linewidths.converter,
+ &linewidths,
+ &convert_dashes_vector,
+ &dashes,
+ &antialiaseds.converter,
+ &antialiaseds,
+ &ignored,
+ &convert_offset_position,
+ &offset_position)) {
+ return NULL;
+ }
+
+ try
+ {
+ py::PathGenerator path(pathobj);
+
+ CALL_CPP("draw_path_collection",
+ (self->x->draw_path_collection(gc,
+ master_transform,
+ path,
+ transforms,
+ offsets,
+ offset_trans,
+ facecolors,
+ edgecolors,
+ linewidths,
+ dashes,
+ antialiaseds,
+ offset_position)));
+ }
+ catch (py::exception &e)
+ {
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *PyRendererAgg_draw_quad_mesh(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ GCAgg gc;
+ agg::trans_affine master_transform;
+ size_t mesh_width;
+ size_t mesh_height;
+ numpy::array_view coordinates;
+ numpy::array_view offsets;
+ agg::trans_affine offset_trans;
+ numpy::array_view facecolors;
+ int antialiased;
+ numpy::array_view edgecolors;
+
+ if (!PyArg_ParseTuple(args,
+ "O&O&IIO&O&O&O&iO&:draw_quad_mesh",
+ &convert_gcagg,
+ &gc,
+ &convert_trans_affine,
+ &master_transform,
+ &mesh_width,
+ &mesh_height,
+ &coordinates.converter,
+ &coordinates,
+ &offsets.converter,
+ &offsets,
+ &convert_trans_affine,
+ &offset_trans,
+ &facecolors.converter,
+ &facecolors,
+ &antialiased,
+ &edgecolors.converter,
+ &edgecolors)) {
+ return NULL;
+ }
+
+ CALL_CPP("draw_quad_mesh",
+ (self->x->draw_quad_mesh(gc,
+ master_transform,
+ mesh_width,
+ mesh_height,
+ coordinates,
+ offsets,
+ offset_trans,
+ facecolors,
+ antialiased,
+ edgecolors)));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+PyRendererAgg_draw_gouraud_triangle(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ GCAgg gc;
+ numpy::array_view points;
+ numpy::array_view colors;
+ agg::trans_affine trans;
+
+ if (!PyArg_ParseTuple(args,
+ "O&O&O&O&|O:draw_gouraud_triangle",
+ &convert_gcagg,
+ &gc,
+ &points.converter,
+ &points,
+ &colors.converter,
+ &colors,
+ &convert_trans_affine,
+ &trans)) {
+ return NULL;
+ }
+
+ CALL_CPP("draw_gouraud_triangle", (self->x->draw_gouraud_triangle(gc, points, colors, trans)));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+PyRendererAgg_draw_gouraud_triangles(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ GCAgg gc;
+ numpy::array_view points;
+ numpy::array_view colors;
+ agg::trans_affine trans;
+
+ if (!PyArg_ParseTuple(args,
+ "O&O&O&O&|O:draw_gouraud_triangles",
+ &convert_gcagg,
+ &gc,
+ &points.converter,
+ &points,
+ &colors.converter,
+ &colors,
+ &convert_trans_affine,
+ &trans)) {
+ return NULL;
+ }
+
+ CALL_CPP("draw_gouraud_triangles", self->x->draw_gouraud_triangles(gc, points, colors, trans));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *PyRendererAgg_tostring_rgb(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *buffobj = NULL;
+
+ buffobj = PyBytes_FromStringAndSize(NULL, self->x->get_width() * self->x->get_height() * 3);
+ if (buffobj == NULL) {
+ return NULL;
+ }
+
+ CALL_CPP_CLEANUP("tostring_rgb",
+ (self->x->tostring_rgb((uint8_t *)PyBytes_AS_STRING(buffobj))),
+ Py_DECREF(buffobj));
+
+ return buffobj;
+}
+
+static PyObject *PyRendererAgg_tostring_argb(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *buffobj = NULL;
+
+ buffobj = PyBytes_FromStringAndSize(NULL, self->x->get_width() * self->x->get_height() * 4);
+ if (buffobj == NULL) {
+ return NULL;
+ }
+
+ CALL_CPP_CLEANUP("tostring_argb",
+ (self->x->tostring_argb((uint8_t *)PyBytes_AS_STRING(buffobj))),
+ Py_DECREF(buffobj));
+
+ return buffobj;
+}
+
+static PyObject *PyRendererAgg_tostring_bgra(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *buffobj = NULL;
+
+ buffobj = PyBytes_FromStringAndSize(NULL, self->x->get_width() * self->x->get_height() * 4);
+ if (buffobj == NULL) {
+ return NULL;
+ }
+
+ CALL_CPP_CLEANUP("to_string_bgra",
+ (self->x->tostring_bgra((uint8_t *)PyBytes_AS_STRING(buffobj))),
+ Py_DECREF(buffobj));
+
+ return buffobj;
+}
+
+static PyObject *
+PyRendererAgg_get_content_extents(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ agg::rect_i extents;
+
+ CALL_CPP("get_content_extents", (extents = self->x->get_content_extents()));
+
+ return Py_BuildValue(
+ "iiii", extents.x1, extents.y1, extents.x2 - extents.x1, extents.y2 - extents.y1);
+}
+
+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
+}
+
+int PyRendererAgg_get_buffer(PyRendererAgg *self, Py_buffer *buf, int flags)
+{
+ Py_INCREF(self);
+ buf->obj = (PyObject *)self;
+ buf->buf = self->x->pixBuffer;
+ buf->len = self->x->get_width() * self->x->get_height() * 4;
+ buf->readonly = 0;
+ buf->format = (char *)"B";
+ buf->ndim = 3;
+ self->shape[0] = self->x->get_height();
+ 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[1] = 4;
+ self->strides[2] = 1;
+ buf->strides = self->strides;
+ buf->suboffsets = NULL;
+ buf->itemsize = 1;
+ buf->internal = NULL;
+
+ return 1;
+}
+
+static PyObject *PyRendererAgg_clear(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ CALL_CPP("clear", self->x->clear());
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *PyRendererAgg_copy_from_bbox(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ agg::rect_d bbox;
+ BufferRegion *reg;
+ PyObject *regobj;
+
+ if (!PyArg_ParseTuple(args, "O&:copy_from_bbox", &convert_rect, &bbox)) {
+ return 0;
+ }
+
+ CALL_CPP("copy_from_bbox", (reg = self->x->copy_from_bbox(bbox)));
+
+ regobj = PyBufferRegion_new(&PyBufferRegionType, NULL, NULL);
+ ((PyBufferRegion *)regobj)->x = reg;
+
+ return regobj;
+}
+
+static PyObject *PyRendererAgg_restore_region(PyRendererAgg *self, PyObject *args, PyObject *kwds)
+{
+ PyBufferRegion *regobj;
+ int xx1 = 0, yy1 = 0, xx2 = 0, yy2 = 0, x = 0, y = 0;
+
+ if (!PyArg_ParseTuple(args,
+ "O!|iiiiii:restore_region",
+ &PyBufferRegionType,
+ ®obj,
+ &xx1,
+ &yy1,
+ &xx2,
+ &yy2,
+ &x,
+ &y)) {
+ return 0;
+ }
+
+ if (PySequence_Size(args) == 1) {
+ CALL_CPP("restore_region", (self->x->restore_region(*(regobj->x))));
+ } else {
+ CALL_CPP("restore_region", self->x->restore_region(*(regobj->x), xx1, yy1, xx2, yy2, x, y));
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyTypeObject PyRendererAggType;
+
+static PyTypeObject *PyRendererAgg_init_type(PyObject *m, PyTypeObject *type)
+{
+ static PyMethodDef methods[] = {
+ {"draw_path", (PyCFunction)PyRendererAgg_draw_path, METH_VARARGS, NULL},
+ {"draw_markers", (PyCFunction)PyRendererAgg_draw_markers, METH_VARARGS, NULL},
+ {"draw_text_image", (PyCFunction)PyRendererAgg_draw_text_image, METH_VARARGS, NULL},
+ {"draw_image", (PyCFunction)PyRendererAgg_draw_image, METH_VARARGS, NULL},
+ {"draw_path_collection", (PyCFunction)PyRendererAgg_draw_path_collection, METH_VARARGS, NULL},
+ {"draw_quad_mesh", (PyCFunction)PyRendererAgg_draw_quad_mesh, METH_VARARGS, NULL},
+ {"draw_gouraud_triangle", (PyCFunction)PyRendererAgg_draw_gouraud_triangle, METH_VARARGS, NULL},
+ {"draw_gouraud_triangles", (PyCFunction)PyRendererAgg_draw_gouraud_triangles, METH_VARARGS, NULL},
+
+ {"tostring_rgb", (PyCFunction)PyRendererAgg_tostring_rgb, METH_NOARGS, NULL},
+ {"tostring_argb", (PyCFunction)PyRendererAgg_tostring_argb, METH_NOARGS, NULL},
+ {"tostring_bgra", (PyCFunction)PyRendererAgg_tostring_bgra, METH_NOARGS, NULL},
+ {"get_content_extents", (PyCFunction)PyRendererAgg_get_content_extents, METH_NOARGS, NULL},
+ {"buffer_rgba", (PyCFunction)PyRendererAgg_buffer_rgba, METH_NOARGS, NULL},
+ {"clear", (PyCFunction)PyRendererAgg_clear, METH_NOARGS, NULL},
+
+ {"copy_from_bbox", (PyCFunction)PyRendererAgg_copy_from_bbox, METH_VARARGS, NULL},
+ {"restore_region", (PyCFunction)PyRendererAgg_restore_region, METH_VARARGS, NULL},
+ {NULL}
+ };
+
+ static PyBufferProcs buffer_procs;
+ memset(&buffer_procs, 0, sizeof(PyBufferProcs));
+ buffer_procs.bf_getbuffer = (getbufferproc)PyRendererAgg_get_buffer;
+
+ memset(type, 0, sizeof(PyTypeObject));
+ type->tp_name = "matplotlib.backends._backend_agg.RendererAgg";
+ type->tp_basicsize = sizeof(PyRendererAgg);
+ type->tp_dealloc = (destructor)PyRendererAgg_dealloc;
+ type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_NEWBUFFER;
+ type->tp_methods = methods;
+ type->tp_init = (initproc)PyRendererAgg_init;
+ type->tp_new = PyRendererAgg_new;
+ type->tp_as_buffer = &buffer_procs;
+
+ if (PyType_Ready(type) < 0) {
+ return NULL;
+ }
+
+ if (PyModule_AddObject(m, "RendererAgg", (PyObject *)type)) {
+ return NULL;
+ }
+
+ return type;
+}
+
+extern "C" {
+
+struct module_state
+{
+/* The Sun compiler can't handle empty structs */
+#if defined(__SUNPRO_C) || defined(_MSC_VER)
+ int _dummy;
+#endif
+};
+
+#if PY3K
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "_backend_agg",
+ NULL,
+ sizeof(struct module_state),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+#define INITERROR return NULL
+
+PyMODINIT_FUNC PyInit__backend_agg(void)
+
+#else
+#define INITERROR return
+
+PyMODINIT_FUNC init_backend_agg(void)
+#endif
+
+{
+ PyObject *m;
+
+#if PY3K
+ m = PyModule_Create(&moduledef);
+#else
+ m = Py_InitModule3("_backend_agg", NULL, NULL);
+#endif
+
+ if (m == NULL) {
+ INITERROR;
+ }
+
+ import_array();
+
+ if (!PyRendererAgg_init_type(m, &PyRendererAggType)) {
+ INITERROR;
+ }
+
+ if (!PyBufferRegion_init_type(m, &PyBufferRegionType)) {
+ INITERROR;
+ }
+
+#if PY3K
+ return m;
+#endif
+}
+
+} // extern "C"
diff --git a/src/_backend_agg_wrapper.h b/src/_backend_agg_wrapper.h
new file mode 100644
index 000000000000..f625b4ce43b7
--- /dev/null
+++ b/src/_backend_agg_wrapper.h
@@ -0,0 +1,29 @@
+#ifndef __BACKEND_AGG_WRAPPER_H__
+#define __BACKEND_AGG_WRAPPER_H__
+
+#include "mplutils.h"
+#include "py_converters.h"
+#include "_backend_agg.h"
+
+extern "C" {
+
+typedef struct
+{
+ PyObject_HEAD;
+ RendererAgg *x;
+ Py_ssize_t shape[3];
+ Py_ssize_t strides[3];
+ Py_ssize_t suboffsets[3];
+} PyRendererAgg;
+
+typedef struct
+{
+ PyObject_HEAD;
+ BufferRegion *x;
+ Py_ssize_t shape[3];
+ Py_ssize_t strides[3];
+ Py_ssize_t suboffsets[3];
+} PyBufferRegion;
+}
+
+#endif
diff --git a/src/_backend_gdk.c b/src/_backend_gdk.c
index 7d52c652586e..9ccc3c5f0acb 100644
--- a/src/_backend_gdk.c
+++ b/src/_backend_gdk.c
@@ -7,12 +7,10 @@
#include
-
static PyTypeObject *_PyGdkPixbuf_Type;
#define PyGdkPixbuf_Type (*_PyGdkPixbuf_Type)
-static PyObject *
-pixbuf_get_pixels_array(PyObject *self, PyObject *args)
+static PyObject *pixbuf_get_pixels_array(PyObject *self, PyObject *args)
{
/* 1) read in Python pixbuf, get the underlying gdk_pixbuf */
PyGObject *py_pixbuf;
@@ -20,9 +18,8 @@ pixbuf_get_pixels_array(PyObject *self, PyObject *args)
PyArrayObject *array;
npy_intp dims[3] = { 0, 0, 3 };
- if (!PyArg_ParseTuple(args, "O!:pixbuf_get_pixels_array",
- &PyGdkPixbuf_Type, &py_pixbuf))
- return NULL;
+ if (!PyArg_ParseTuple(args, "O!:pixbuf_get_pixels_array", &PyGdkPixbuf_Type, &py_pixbuf))
+ return NULL;
gdk_pixbuf = GDK_PIXBUF(py_pixbuf->obj);
@@ -35,8 +32,8 @@ pixbuf_get_pixels_array(PyObject *self, PyObject *args)
if (gdk_pixbuf_get_has_alpha(gdk_pixbuf))
dims[2] = 4;
- array = (PyArrayObject *)PyArray_SimpleNewFromData(3, dims, NPY_UBYTE,
- (char *)gdk_pixbuf_get_pixels(gdk_pixbuf));
+ array = (PyArrayObject *)PyArray_SimpleNewFromData(
+ 3, dims, PyArray_UBYTE, (char *)gdk_pixbuf_get_pixels(gdk_pixbuf));
if (array == NULL)
return NULL;
@@ -52,12 +49,10 @@ static PyMethodDef _backend_gdk_functions[] = {
{ NULL, NULL, 0 }
};
-PyMODINIT_FUNC
-init_backend_gdk(void)
+PyMODINIT_FUNC init_backend_gdk(void)
{
PyObject *mod;
- mod = Py_InitModule("matplotlib.backends._backend_gdk",
- _backend_gdk_functions);
+ mod = Py_InitModule("matplotlib.backends._backend_gdk", _backend_gdk_functions);
import_array();
init_pygtk();
diff --git a/src/_gtkagg.cpp b/src/_gtkagg.cpp
index e2c112b8acda..cbad94523fb1 100644
--- a/src/_gtkagg.cpp
+++ b/src/_gtkagg.cpp
@@ -1,153 +1,163 @@
-
/* -*- mode: c++; c-basic-offset: 4 -*- */
+#include
+
#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
#include "agg_basics.h"
-#include "numpy/arrayobject.h"
-#include "_backend_agg.h"
-#include "agg_py_transforms.h"
+#include "agg_pixfmt_rgba.h"
+#include "agg_renderer_base.h"
+#include "agg_rendering_buffer.h"
+
+#include "numpy_cpp.h"
+#include "py_converters.h"
-// the extension module
-class _gtkagg_module : public Py::ExtensionModule<_gtkagg_module>
+static PyObject *Py_agg_to_gtk_drawable(PyObject *self, PyObject *args, PyObject *kwds)
{
-public:
- _gtkagg_module()
- : Py::ExtensionModule<_gtkagg_module>("_gtkagg")
- {
- add_varargs_method("agg_to_gtk_drawable",
- &_gtkagg_module::agg_to_gtk_drawable,
- "Draw to a gtk drawable from a agg buffer.");
- initialize("The _gtkagg module");
+ typedef agg::pixfmt_rgba32_plain pixfmt;
+ typedef agg::renderer_base renderer_base;
+
+ PyGObject *py_drawable;
+ numpy::array_view buffer;
+ agg::rect_d rect;
+
+ // args are gc, renderer, bbox where bbox is a transforms BBox
+ // (possibly None). If bbox is None, blit the entire agg buffer
+ // to gtk. If bbox is not None, blit only the region defined by
+ // the bbox
+
+ if (!PyArg_ParseTuple(args,
+ "OO&O&:agg_to_gtk_drawable",
+ &py_drawable,
+ &buffer.converter,
+ &buffer,
+ &convert_rect,
+ &rect)) {
+ return NULL;
}
- virtual ~_gtkagg_module() {}
-
-private:
-
- Py::Object agg_to_gtk_drawable(const Py::Tuple &args)
- {
- // args are gc, renderer, bbox where bbox is a transforms BBox
- // (possibly None). If bbox is None, blit the entire agg buffer
- // to gtk. If bbox is not None, blit only the region defined by
- // the bbox
- args.verify_length(3);
-
- PyGObject *py_drawable = (PyGObject *)(args[0].ptr());
- RendererAgg* aggRenderer = static_cast(args[1].ptr());
+ if (buffer.dim(2) != 4) {
+ PyErr_SetString(PyExc_ValueError, "Invalid image buffer. Must be NxMx4.");
+ return NULL;
+ }
- GdkDrawable *drawable = GDK_DRAWABLE(py_drawable->obj);
- GdkGC* gc = gdk_gc_new(drawable);
+ GdkDrawable *drawable = GDK_DRAWABLE(py_drawable->obj);
+ GdkGC *gc = gdk_gc_new(drawable);
+
+ int srcstride = buffer.dim(1) * 4;
+ int srcwidth = buffer.dim(1);
+ int srcheight = buffer.dim(0);
+
+ // these three will be overridden below
+ int destx = 0;
+ int desty = 0;
+ int destwidth = 1;
+ int destheight = 1;
+ int deststride = 1;
+
+ std::vector destbuffer;
+ agg::int8u *destbufferptr;
+
+ if (rect.x1 == 0.0 && rect.x2 == 0.0 && rect.y1 == 0.0 && rect.y2 == 0.0) {
+ // bbox is None; copy the entire image
+ destbufferptr = (agg::int8u *)buffer.data();
+ destwidth = srcwidth;
+ destheight = srcheight;
+ deststride = srcstride;
+ } else {
+ destx = (int)rect.x1;
+ desty = srcheight - (int)rect.y2;
+ destwidth = (int)(rect.x2 - rect.x1);
+ destheight = (int)(rect.y2 - rect.y1);
+ deststride = destwidth * 4;
+ destbuffer.reserve(destheight * deststride);
+ destbufferptr = &destbuffer[0];
+
+ agg::rendering_buffer destrbuf;
+ destrbuf.attach(destbufferptr, destwidth, destheight, deststride);
+ pixfmt destpf(destrbuf);
+ renderer_base destrb(destpf);
+
+ agg::rendering_buffer srcrbuf;
+ srcrbuf.attach((agg::int8u *)buffer.data(), buffer.dim(1), buffer.dim(0), buffer.dim(1) * 4);
+
+ agg::rect_base region(destx, desty, (int)rect.x2, srcheight - (int)rect.y1);
+ destrb.copy_from(srcrbuf, ®ion, -destx, -desty);
+ }
- int srcstride = aggRenderer->get_width() * 4;
- int srcwidth = (int)aggRenderer->get_width();
- int srcheight = (int)aggRenderer->get_height();
+ gdk_draw_rgb_32_image(drawable,
+ gc,
+ destx,
+ desty,
+ destwidth,
+ destheight,
+ GDK_RGB_DITHER_NORMAL,
+ destbufferptr,
+ deststride);
- // these three will be overridden below
- int destx = 0;
- int desty = 0;
- int destwidth = 1;
- int destheight = 1;
- int deststride = 1;
+ gdk_gc_destroy(gc);
+ Py_RETURN_NONE;
+}
- bool needfree = false;
+static PyMethodDef module_methods[] = {
+ {"agg_to_gtk_drawable", (PyCFunction)Py_agg_to_gtk_drawable, METH_VARARGS, NULL},
+ NULL
+};
- agg::int8u *destbuffer = NULL;
+extern "C" {
- if (args[2].ptr() == Py_None)
- {
- //bbox is None; copy the entire image
- destbuffer = aggRenderer->pixBuffer;
- destwidth = srcwidth;
- destheight = srcheight;
- deststride = srcstride;
- }
- else
- {
- //bbox is not None; copy the image in the bbox
- PyObject* clipbox = args[2].ptr();
- double l, b, r, t;
-
- if (!py_convert_bbox(clipbox, l, b, r, t))
- {
- throw Py::TypeError
- ("Argument 3 to agg_to_gtk_drawable must be a Bbox object.");
- }
-
- destx = (int)l;
- desty = srcheight - (int)t;
- destwidth = (int)(r - l);
- destheight = (int)(t - b);
- deststride = destwidth * 4;
-
- needfree = true;
- destbuffer = new agg::int8u[deststride*destheight];
- if (destbuffer == NULL)
- {
- throw Py::MemoryError("_gtkagg could not allocate memory for destbuffer");
- }
-
- agg::rendering_buffer destrbuf;
- destrbuf.attach(destbuffer, destwidth, destheight, deststride);
- pixfmt destpf(destrbuf);
- renderer_base destrb(destpf);
-
- //destrb.clear(agg::rgba(1, 1, 1, 0));
-
- agg::rect_base region(destx, desty, (int)r, srcheight - (int)b);
- destrb.copy_from(aggRenderer->renderingBuffer, ®ion,
- -destx, -desty);
- }
-
- /*std::cout << desty << " "
- << destheight << " "
- << srcheight << std::endl;*/
+ struct module_state
+ {
+ /* The Sun compiler can't handle empty structs */
+#if defined(__SUNPRO_C) || defined(_MSC_VER)
+ int _dummy;
+#endif
+ };
+
+#if PY3K
+ static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "_gtkagg",
+ NULL,
+ sizeof(struct module_state),
+ module_methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+#define INITERROR return NULL
+
+ PyMODINIT_FUNC PyInit__gtkagg(void)
+
+#else
+#define INITERROR return
+
+ PyMODINIT_FUNC init_gtkagg(void)
+#endif
+ {
+ PyObject *m;
- //gdk_rgb_init();
- gdk_draw_rgb_32_image(drawable, gc, destx, desty,
- destwidth,
- destheight,
- GDK_RGB_DITHER_NORMAL,
- destbuffer,
- deststride);
+#if PY3K
+ m = PyModule_Create(&moduledef);
+#else
+ m = Py_InitModule3("_gtkagg", module_methods, NULL);
+#endif
- gdk_gc_destroy(gc);
- if (needfree)
- {
- delete [] destbuffer;
+ if (m == NULL) {
+ INITERROR;
}
- return Py::Object();
+ init_pygobject();
+ init_pygtk();
+ import_array();
+#if PY3K
+ return m;
+#endif
}
-};
-
-PyMODINIT_FUNC
-init_gtkagg(void)
-{
- init_pygobject();
- init_pygtk();
-
- import_array();
- //suppress unused warning by creating in two lines
- static _gtkagg_module* _gtkagg = NULL;
- _gtkagg = new _gtkagg_module;
}
-
-
-
-
-
-
-
diff --git a/src/_image.cpp b/src/_image.cpp
index 4114d68e84e0..df670397e37d 100644
--- a/src/_image.cpp
+++ b/src/_image.cpp
@@ -1,15 +1,8 @@
/* -*- mode: c++; c-basic-offset: 4 -*- */
-/* Python API mandates Python.h is included *first* */
-#include "Python.h"
-#include
+#define NO_IMPORT_ARRAY
-#include
-#include
-#include
-#include
-
-#include "numpy/arrayobject.h"
+#include
#include "agg_color_rgba.h"
#include "agg_conv_transform.h"
@@ -18,6 +11,7 @@
#include "agg_pixfmt_rgb.h"
#include "agg_pixfmt_rgba.h"
#include "agg_rasterizer_scanline_aa.h"
+#include "agg_rasterizer_sl_clip.h"
#include "agg_renderer_scanline.h"
#include "agg_rendering_buffer.h"
#include "agg_scanline_bin.h"
@@ -27,233 +21,123 @@
#include "agg_span_image_filter_rgb.h"
#include "agg_span_image_filter_rgba.h"
#include "agg_span_interpolator_linear.h"
-#include "agg_rasterizer_sl_clip.h"
#include "util/agg_color_conv_rgb8.h"
+
#include "_image.h"
#include "mplutils.h"
-
typedef agg::pixfmt_rgba32_plain pixfmt;
typedef agg::pixfmt_rgba32_pre pixfmt_pre;
typedef agg::renderer_base renderer_base;
typedef agg::span_interpolator_linear<> interpolator_type;
typedef agg::rasterizer_scanline_aa rasterizer;
-
-Image::Image() :
- bufferIn(NULL), rbufIn(NULL), colsIn(0), rowsIn(0),
- bufferOut(NULL), rbufOut(NULL), colsOut(0), rowsOut(0), BPP(4),
- interpolation(BILINEAR), aspect(ASPECT_FREE), bg(1, 1, 1, 0), resample(true)
-{
- _VERBOSE("Image::Image");
+Image::Image()
+ : bufferIn(NULL),
+ rbufIn(NULL),
+ colsIn(0),
+ rowsIn(0),
+ bufferOut(NULL),
+ rbufOut(NULL),
+ colsOut(0),
+ rowsOut(0),
+ BPP(4),
+ interpolation(BILINEAR),
+ aspect(ASPECT_FREE),
+ bg(1, 1, 1, 0),
+ resample(true)
+{
+
+}
+
+Image::Image(unsigned numrows, unsigned numcols, bool isoutput)
+ : bufferIn(NULL),
+ rbufIn(NULL),
+ colsIn(0),
+ rowsIn(0),
+ bufferOut(NULL),
+ rbufOut(NULL),
+ colsOut(0),
+ rowsOut(0),
+ BPP(4),
+ interpolation(BILINEAR),
+ aspect(ASPECT_FREE),
+ bg(1, 1, 1, 0),
+ resample(true)
+{
+ if (isoutput) {
+ rowsOut = numrows;
+ colsOut = numcols;
+ unsigned NUMBYTES(numrows * numcols * BPP);
+ bufferOut = new agg::int8u[NUMBYTES];
+ rbufOut = new agg::rendering_buffer;
+ rbufOut->attach(bufferOut, colsOut, rowsOut, colsOut * BPP);
+ } else {
+ rowsIn = numrows;
+ colsIn = numcols;
+ unsigned NUMBYTES(numrows * numcols * BPP);
+ bufferIn = new agg::int8u[NUMBYTES];
+ rbufIn = new agg::rendering_buffer;
+ rbufIn->attach(bufferIn, colsIn, rowsIn, colsIn * BPP);
+ }
}
Image::~Image()
{
- _VERBOSE("Image::~Image");
- delete [] bufferIn;
+ delete[] bufferIn;
bufferIn = NULL;
delete rbufIn;
rbufIn = NULL;
delete rbufOut;
rbufOut = NULL;
- delete [] bufferOut;
+ delete[] bufferOut;
bufferOut = NULL;
}
-int
-Image::setattr(const char * name, const Py::Object & value)
+void Image::apply_rotation(double r)
{
- _VERBOSE("Image::setattr");
- __dict__[name] = value;
- return 0;
-}
-
-Py::Object
-Image::getattr(const char * name)
-{
- _VERBOSE("Image::getattro");
- if (__dict__.hasKey(name)) return __dict__[name];
- else return getattr_default(name);
-}
-
-char Image::apply_rotation__doc__[] =
- "apply_rotation(angle)\n"
- "\n"
- "Apply the rotation (degrees) to image"
- ;
-Py::Object
-Image::apply_rotation(const Py::Tuple& args)
-{
- _VERBOSE("Image::apply_rotation");
-
- args.verify_length(1);
- double r = Py::Float(args[0]);
-
-
agg::trans_affine M = agg::trans_affine_rotation(r * agg::pi / 180.0);
srcMatrix *= M;
imageMatrix *= M;
- return Py::Object();
}
-char Image::flipud_out__doc__[] =
- "flipud()\n"
- "\n"
- "Flip the output image upside down"
- ;
-
-char Image::flipud_in__doc__[] =
- "flipud()\n"
- "\n"
- "Flip the input image upside down"
- ;
-Py::Object
-Image::flipud_in(const Py::Tuple& args)
+void Image::set_bg(double r, double g, double b, double a)
{
- _VERBOSE("Image::flipud_in");
-
- args.verify_length(0);
- int stride = rbufIn->stride();
- rbufIn->attach(bufferIn, colsIn, rowsIn, -stride);
-
- return Py::Object();
+ bg.r = r;
+ bg.g = g;
+ bg.b = b;
+ bg.a = a;
}
-char Image::set_bg__doc__[] =
- "set_bg(r,g,b,a)\n"
- "\n"
- "Set the background color"
- ;
-
-Py::Object
-Image::set_bg(const Py::Tuple& args)
+void Image::apply_scaling(double sx, double sy)
{
- _VERBOSE("Image::set_bg");
-
- args.verify_length(4);
- bg.r = Py::Float(args[0]);
- bg.g = Py::Float(args[1]);
- bg.b = Py::Float(args[2]);
- bg.a = Py::Float(args[3]);
- return Py::Object();
-}
-
-char Image::apply_scaling__doc__[] =
- "apply_scaling(sx, sy)\n"
- "\n"
- "Apply the scale factors sx, sy to the transform matrix"
- ;
-
-Py::Object
-Image::apply_scaling(const Py::Tuple& args)
-{
- _VERBOSE("Image::apply_scaling");
-
- args.verify_length(2);
- double sx = Py::Float(args[0]);
- double sy = Py::Float(args[1]);
-
- //printf("applying scaling %1.2f, %1.2f\n", sx, sy);
agg::trans_affine M = agg::trans_affine_scaling(sx, sy);
srcMatrix *= M;
imageMatrix *= M;
-
- return Py::Object();
}
-char Image::apply_translation__doc__[] =
- "apply_translation(tx, ty)\n"
- "\n"
- "Apply the translation tx, ty to the transform matrix"
- ;
-
-Py::Object
-Image::apply_translation(const Py::Tuple& args)
+void Image::apply_translation(double tx, double ty)
{
- _VERBOSE("Image::apply_translation");
-
- args.verify_length(2);
- double tx = Py::Float(args[0]);
- double ty = Py::Float(args[1]);
-
- //printf("applying translation %1.2f, %1.2f\n", tx, ty);
agg::trans_affine M = agg::trans_affine_translation(tx, ty);
srcMatrix *= M;
imageMatrix *= M;
-
- return Py::Object();
}
-char Image::as_rgba_str__doc__[] =
- "numrows, numcols, s = as_rgba_str()"
- "\n"
- "Call this function after resize to get the data as string\n"
- "The string is a numrows by numcols x 4 (RGBA) unsigned char buffer\n"
- ;
-
-Py::Object
-Image::as_rgba_str(const Py::Tuple& args, const Py::Dict& kwargs)
+void Image::as_rgba_str(agg::int8u *outbuf)
{
- _VERBOSE("Image::as_rgba_str");
-
- args.verify_length(0);
-
- std::pair bufpair = _get_output_buffer();
-
- #if PY3K
- Py::Object ret = Py::asObject(Py_BuildValue("nny#", rowsOut, colsOut,
- bufpair.first, colsOut * rowsOut * 4));
- #else
- Py::Object ret = Py::asObject(Py_BuildValue("nns#", rowsOut, colsOut,
- bufpair.first, colsOut * rowsOut * 4));
- #endif
-
- if (bufpair.second) delete [] bufpair.first;
- return ret;
+ agg::rendering_buffer rb;
+ rb.attach(outbuf, colsOut, rowsOut, colsOut * 4);
+ rb.copy_from(*rbufOut);
}
-
-char Image::color_conv__doc__[] =
- "numrows, numcols, buffer = color_conv(format)"
- "\n"
- "format 0(BGRA) or 1(ARGB)\n"
- "Convert image to format and return in a writable buffer\n"
- ;
-Py::Object
-Image::color_conv(const Py::Tuple& args)
+void Image::color_conv(int format, agg::int8u *outbuf)
{
- _VERBOSE("Image::color_conv");
-
- args.verify_length(1);
- int format = Py::Int(args[0]);
- PyObject* py_buffer = NULL;
int row_len = colsOut * 4;
-#if PY3K
- unsigned char* buf = (unsigned char *)malloc(row_len * rowsOut);
- if (buf == NULL)
- throw Py::MemoryError("Image::color_conv could not allocate memory");
-#else
- py_buffer = PyBuffer_New(row_len * rowsOut);
- if (py_buffer == NULL)
- throw Py::MemoryError("Image::color_conv could not allocate memory");
-
- void* buf;
- Py_ssize_t buffer_len;
- int ret = PyObject_AsWriteBuffer(py_buffer, &buf, &buffer_len);
- if (ret != 0)
- {
- Py_XDECREF(py_buffer);
- throw Py::MemoryError("Image::color_conv could not allocate memory");
- }
-#endif
agg::rendering_buffer rtmp;
- rtmp.attach(reinterpret_cast(buf), colsOut, rowsOut,
- row_len);
+ rtmp.attach(outbuf, colsOut, rowsOut, row_len);
- switch (format)
- {
+ switch (format) {
case 0:
agg::color_conv(&rtmp, rbufOut, agg::color_conv_rgba32_to_bgra32());
break;
@@ -261,123 +145,24 @@ Image::color_conv(const Py::Tuple& args)
agg::color_conv(&rtmp, rbufOut, agg::color_conv_rgba32_to_argb32());
break;
default:
- Py_XDECREF(py_buffer);
- throw Py::ValueError("Image::color_conv unknown format");
- }
-
-#if PY3K
- py_buffer = PyByteArray_FromStringAndSize((char *)buf, row_len * rowsOut);
- if (py_buffer == NULL) {
- free(buf);
+ throw "Image::color_conv unknown format";
}
-#endif
-
- PyObject* o = Py_BuildValue("nnN", rowsOut, colsOut, py_buffer);
- return Py::asObject(o);
}
-char Image::buffer_rgba__doc__[] =
- "buffer = buffer_rgba()"
- "\n"
- "Return the image buffer as rgba32\n"
- ;
-Py::Object
-Image::buffer_rgba(const Py::Tuple& args)
+void Image::reset_matrix(void)
{
- //"Return the image object as rgba";
-
- _VERBOSE("RendererAgg::buffer_rgba");
-
- args.verify_length(0);
- int row_len = colsOut * 4;
- PyObject* o = Py_BuildValue("nns#", rowsOut, colsOut,
- rbufOut, row_len * rowsOut);
- return Py::asObject(o);
-}
-
-char Image::reset_matrix__doc__[] =
- "reset_matrix()"
- "\n"
- "Reset the transformation matrix"
- ;
-
-Py::Object
-Image::reset_matrix(const Py::Tuple& args)
-{
- _VERBOSE("Image::reset_matrix");
-
- args.verify_length(0);
srcMatrix.reset();
imageMatrix.reset();
-
- return Py::Object();
}
-char Image::get_matrix__doc__[] =
- "(m11,m21,m12,m22,m13,m23) = get_matrix()\n"
- "\n"
- "Get the affine transformation matrix\n"
- " /m11,m12,m13\\\n"
- " /m21,m22,m23|\n"
- " \\ 0 , 0 , 1 /"
- ;
-
-Py::Object
-Image::get_matrix(const Py::Tuple& args)
+void Image::resize(int numcols, int numrows, int norm, double radius)
{
- _VERBOSE("Image::get_matrix");
-
- args.verify_length(0);
-
- double m[6];
- srcMatrix.store_to(m);
- Py::Tuple ret(6);
- for (int i = 0;i < 6;i++)
- {
- ret[i] = Py::Float(m[i]);
+ if (bufferIn == NULL) {
+ throw "You must first load the image";
}
- return ret;
-}
-
-char Image::resize__doc__[] =
- "resize(width, height, norm=1, radius=4.0)\n"
- "\n"
- "Resize the image to width, height using interpolation\n"
- "norm and radius are optional args for some of the filters and must be\n"
- "passed as kwargs\n"
- ;
-Py::Object
-Image::resize(const Py::Tuple& args, const Py::Dict& kwargs)
-{
- _VERBOSE("Image::resize");
-
- args.verify_length(2);
-
- int norm = 1;
- if (kwargs.hasKey("norm"))
- {
- norm = Py::Int(kwargs["norm"]);
- }
-
- double radius = 4.0;
- if (kwargs.hasKey("radius"))
- {
- radius = Py::Float(kwargs["radius"]);
- }
-
- if (bufferIn == NULL)
- {
- throw Py::RuntimeError("You must first load the image");
- }
-
- int numcols = Py::Int(args[0]);
- int numrows = Py::Int(args[1]);
-
- if (numcols <= 0 || numrows <= 0)
- {
- throw Py::RuntimeError(
- "Width and height must have positive values");
+ if (numcols <= 0 || numrows <= 0) {
+ throw "Width and height must have positive values";
}
colsOut = numcols;
@@ -385,11 +170,11 @@ Image::resize(const Py::Tuple& args, const Py::Dict& kwargs)
size_t NUMBYTES(numrows * numcols * BPP);
- delete [] bufferOut;
+ delete[] bufferOut;
bufferOut = new agg::int8u[NUMBYTES];
- if (bufferOut == NULL) //todo: also handle allocation throw
+ if (bufferOut == NULL) // todo: also handle allocation throw
{
- throw Py::MemoryError("Image::resize could not allocate memory");
+ throw "Image::resize could not allocate memory";
}
delete rbufOut;
@@ -405,8 +190,6 @@ Image::resize(const Py::Tuple& args, const Py::Dict& kwargs)
ras.clip_box(0, 0, numcols, numrows);
- //srcMatrix *= resizingMatrix;
- //imageMatrix *= resizingMatrix;
imageMatrix.invert();
interpolator_type interpolator(imageMatrix);
@@ -437,26 +220,22 @@ Image::resize(const Py::Tuple& args, const Py::Dict& kwargs)
pixfmt_pre pixfmtin(*rbufIn);
img_accessor_type ia(pixfmtin);
- switch (interpolation)
- {
+ switch (interpolation) {
- case NEAREST:
- {
+ case NEAREST: {
typedef agg::span_image_filter_rgba_nn span_gen_type;
- typedef agg::renderer_scanline_aa renderer_type;
+ typedef agg::renderer_scanline_aa
+ renderer_type;
span_gen_type sg(ia, interpolator);
renderer_type ri(rb, sa, sg);
agg::render_scanlines(ras, sl, ri);
- }
- break;
+ } break;
case HANNING:
case HAMMING:
- case HERMITE:
- {
+ case HERMITE: {
agg::image_filter_lut filter;
- switch (interpolation)
- {
+ switch (interpolation) {
case HANNING:
filter.calculate(agg::image_filter_hanning(), norm);
break;
@@ -467,24 +246,23 @@ Image::resize(const Py::Tuple& args, const Py::Dict& kwargs)
filter.calculate(agg::image_filter_hermite(), norm);
break;
}
- if (resample)
- {
+ if (resample) {
typedef agg::span_image_resample_rgba_affine span_gen_type;
- typedef agg::renderer_scanline_aa renderer_type;
+ typedef agg::renderer_scanline_aa
+ renderer_type;
span_gen_type sg(ia, interpolator, filter);
renderer_type ri(rb, sa, sg);
agg::render_scanlines(ras, sl, ri);
- }
- else
- {
- typedef agg::span_image_filter_rgba_2x2 span_gen_type;
- typedef agg::renderer_scanline_aa renderer_type;
+ } else {
+ typedef agg::span_image_filter_rgba_2x2
+ span_gen_type;
+ typedef agg::renderer_scanline_aa
+ renderer_type;
span_gen_type sg(ia, interpolator, filter);
renderer_type ri(rb, sa, sg);
agg::render_scanlines(ras, sl, ri);
}
- }
- break;
+ } break;
case BILINEAR:
case BICUBIC:
case SPLINE16:
@@ -497,11 +275,9 @@ Image::resize(const Py::Tuple& args, const Py::Dict& kwargs)
case MITCHELL:
case SINC:
case LANCZOS:
- case BLACKMAN:
- {
+ case BLACKMAN: {
agg::image_filter_lut filter;
- switch (interpolation)
- {
+ switch (interpolation) {
case BILINEAR:
filter.calculate(agg::image_filter_bilinear(), norm);
break;
@@ -542,801 +318,85 @@ Image::resize(const Py::Tuple& args, const Py::Dict& kwargs)
filter.calculate(agg::image_filter_blackman(radius), norm);
break;
}
- if (resample)
- {
+ if (resample) {
typedef agg::span_image_resample_rgba_affine