// To remove a gcc warning
#ifdef _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#endif
#include "Python.h"
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdio>
#include "numpy/arrayobject.h"
#include "agg_color_rgba.h"
#include "agg_conv_transform.h"
#include "agg_image_accessors.h"
#include "agg_path_storage.h"
#include "agg_pixfmt_rgb.h"
#include "agg_pixfmt_rgba.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_renderer_scanline.h"
#include "agg_rendering_buffer.h"
#include "agg_scanline_bin.h"
#include "agg_scanline_bin.h"
#include "agg_scanline_u.h"
#include "agg_span_allocator.h"
#include "agg_span_image_filter_rgb.h"
#include "agg_span_image_filter_rgba.h"
#include "agg_span_interpolator_linear.h"
#include "util/agg_color_conv_rgb8.h"
#include "_image.h"
#include "mplutils.h"
typedef agg::pixfmt_rgba32 pixfmt;
typedef agg::renderer_base<pixfmt> 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) {
_VERBOSE("Image::Image");
}
Image::~Image() {
_VERBOSE("Image::~Image");
delete [] bufferIn; bufferIn = NULL;
delete rbufIn; rbufIn=NULL;
delete rbufOut; rbufOut = NULL;
delete [] bufferOut; bufferOut=NULL;
}
int
Image::setattr( const char * name, const Py::Object & value ) {
_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) {
_VERBOSE("Image::flipud_in");
args.verify_length(0);
int stride = rbufIn->stride();
rbufIn->attach(bufferIn, colsIn, rowsIn, -stride);
return Py::Object();
}
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) {
_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) {
_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) {
_VERBOSE("Image::as_rgba_str");
args.verify_length(0);
std::pair<agg::int8u*,bool> bufpair = _get_output_buffer();
Py::Object ret = Py::asObject(Py_BuildValue("lls#", rowsOut, colsOut,
bufpair.first, colsOut*rowsOut*4));
if (bufpair.second) delete [] bufpair.first;
return ret;
}
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) {
_VERBOSE("Image::color_conv");
args.verify_length(1);
int format = Py::Int(args[0]);
int row_len = colsOut * 4;
PyObject* 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)
throw Py::MemoryError("Image::color_conv could not allocate memory");
agg::rendering_buffer rtmp;
rtmp.attach(reinterpret_cast<unsigned char*>(buf), colsOut, rowsOut,
row_len);
switch (format) {
case 0:
agg::color_conv(&rtmp, rbufOut, agg::color_conv_rgba32_to_bgra32());
break;
case 1:
agg::color_conv(&rtmp, rbufOut, agg::color_conv_rgba32_to_argb32());
break;
default:
throw Py::ValueError("Image::color_conv unknown format");
}
PyObject* o = Py_BuildValue("llN", 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) {
//"Return the image object as rgba";
_VERBOSE("RendererAgg::buffer_rgba");
args.verify_length(0);
int row_len = colsOut * 4;
PyObject* o = Py_BuildValue("lls#", 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) {
_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]);
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]);
colsOut = numcols;
rowsOut = numrows;
size_t NUMBYTES(numrows * numcols * BPP);
delete [] bufferOut;
bufferOut = new agg::int8u[NUMBYTES];
if (bufferOut ==NULL) //todo: also handle allocation throw
throw Py::MemoryError("Image::resize could not allocate memory");
delete rbufOut;
rbufOut = new agg::rendering_buffer;
rbufOut->attach(bufferOut, numcols, numrows, numcols * BPP);
// init the output rendering/rasterizing stuff
pixfmt pixf(*rbufOut);
renderer_base rb(pixf);
rb.clear(bg);
agg::rasterizer_scanline_aa<> ras;
agg::scanline_u8 sl;
//srcMatrix *= resizingMatrix;
//imageMatrix *= resizingMatrix;
imageMatrix.invert();
interpolator_type interpolator(imageMatrix);
typedef agg::span_allocator<agg::rgba8> span_alloc_type;
span_alloc_type sa;
agg::rgba8 background(agg::rgba8(int(255*bg.r),
int(255*bg.g),
int(255*bg.b),
int(255*bg.a)));
// the image path
agg::path_storage path;
agg::int8u *bufferPad = NULL;
agg::rendering_buffer rbufPad;
double x0, y0, x1, y1;
x0 = 0.0;
x1 = colsIn;
y0 = 0.0;
y1 = rowsIn;
path.move_to(x0, y0);
path.line_to(x1, y0);
path.line_to(x1, y1);
path.line_to(x0, y1);
path.close_polygon();
agg::conv_transform<agg::path_storage> imageBox(path, srcMatrix);
ras.add_path(imageBox);
typedef agg::wrap_mode_reflect reflect_type;
typedef agg::image_accessor_wrap<pixfmt, reflect_type, reflect_type> img_accessor_type;
pixfmt pixfmtin(*rbufIn);
img_accessor_type ia(pixfmtin);
switch(interpolation)
{
case NEAREST:
{
typedef agg::span_image_filter_rgba_nn<img_accessor_type, interpolator_type> span_gen_type;
typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type;
span_gen_type sg(ia, interpolator);
renderer_type ri(rb, sa, sg);
agg::render_scanlines(ras, sl, ri);
}
break;
case HANNING:
case HAMMING:
case HERMITE:
{
agg::image_filter_lut filter;
switch (interpolation) {
case HANNING: filter.calculate(agg::image_filter_hanning(), norm); break;
case HAMMING: filter.calculate(agg::image_filter_hamming(), norm); break;
case HERMITE: filter.calculate(agg::image_filter_hermite(), norm); break;
}
if (resample)
{
typedef agg::span_image_resample_rgba_affine<img_accessor_type> span_gen_type;
typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> 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<img_accessor_type, interpolator_type> span_gen_type;
typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type;
span_gen_type sg(ia, interpolator, filter);
renderer_type ri(rb, sa, sg);
agg::render_scanlines(ras, sl, ri);
}
}
break;
case BILINEAR:
case BICUBIC:
case SPLINE16:
case SPLINE36:
case KAISER:
case QUADRIC:
case CATROM:
case GAUSSIAN:
case BESSEL:
case MITCHELL:
case SINC:
case LANCZOS:
case BLACKMAN:
{
agg::image_filter_lut filter;
switch(interpolation)
{
case BILINEAR: filter.calculate(agg::image_filter_bilinear(), norm); break;
case BICUBIC: filter.calculate(agg::image_filter_bicubic(), norm); break;
case SPLINE16: filter.calculate(agg::image_filter_spline16(), norm); break;
case SPLINE36: filter.calculate(agg::image_filter_spline36(), norm); break;
case KAISER: filter.calculate(agg::image_filter_kaiser(), norm); break;
case QUADRIC: filter.calculate(agg::image_filter_quadric(), norm); break;
case CATROM: filter.calculate(agg::image_filter_catrom(), norm); break;
case GAUSSIAN: filter.calculate(agg::image_filter_gaussian(), norm); break;
case BESSEL: filter.calculate(agg::image_filter_bessel(), norm); break;
case MITCHELL: filter.calculate(agg::image_filter_mitchell(), norm); break;
case SINC: filter.calculate(agg::image_filter_sinc(radius), norm); break;
case LANCZOS: filter.calculate(agg::image_filter_lanczos(radius), norm); break;
case BLACKMAN: filter.calculate(agg::image_filter_blackman(radius), norm); break;
}
if (resample)
{
typedef agg::span_image_resample_rgba_affine<img_accessor_type> span_gen_type;
typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> 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<img_accessor_type, interpolator_type> span_gen_type;
typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type;
span_gen_type sg(ia, interpolator, filter);
renderer_type ri(rb, sa, sg);
agg::render_scanlines(ras, sl, ri);
}
}
break;
}
delete [] bufferPad;
return Py::Object();
}
char Image::get_interpolation__doc__[] =
"get_interpolation()\n"
"\n"
"Get the interpolation scheme to one of the module constants, "
"one of image.NEAREST, image.BILINEAR, etc..."
;
Py::Object
Image::get_interpolation(const Py::Tuple& args) {
_VERBOSE("Image::get_interpolation");
args.verify_length(0);
return Py::Int((int)interpolation);
}
char Image::get_aspect__doc__[] =
"get_aspect()\n"
"\n"
"Get the aspect constraint constants"
;
Py::Object
Image::get_aspect(const Py::Tuple& args) {
_VERBOSE("Image::get_aspect");
args.verify_length(0);
return Py::Int((int)aspect);
}
char Image::get_size__doc__[] =
"numrows, numcols = get_size()\n"
"\n"
"Get the number or rows and columns of the input image"
;
Py::Object
Image::get_size(const Py::Tuple& args) {
_VERBOSE("Image::get_size");
args.verify_length(0);
Py::Tuple ret(2);
ret[0] = Py::Int((long)rowsIn);
ret[1] = Py::Int((long)colsIn);
return ret;
}
char Image::get_resample__doc__[] =
"get_resample()\n"
"\n"
"Get the resample flag."
;
Py::Object
Image::get_resample(const Py::Tuple& args) {
_VERBOSE("Image::get_resample");
args.verify_length(0);
return Py::Int((int)resample);
}
char Image::get_size_out__doc__[] =
"numrows, numcols = get_size()\n"
"\n"
"Get the number or rows and columns of the output image"
;
Py::Object
Image::get_size_out(const Py::Tuple& args) {
_VERBOSE("Image::get_size_out");
args.verify_length(0);
Py::Tuple ret(2);
ret[0] = Py::Int((long)rowsOut);
ret[1] = Py::Int((long)colsOut);
return ret;
}
//get the output buffer, flipped if necessary. The second element of
//the pair is a bool that indicates whether you need to free the
//memory
std::pair<agg::int8u*, bool>
Image::_get_output_buffer() {
_VERBOSE("Image::_get_output_buffer");
std::pair<agg::int8u*, bool> ret;
bool flipy = rbufOut->stride()<0;
if (flipy) {
agg::int8u* buffer = new agg::int8u[rowsOut*colsOut*4];
agg::rendering_buffer rb;
rb.attach(buffer, colsOut, rowsOut, colsOut*4);
rb.copy_from(*rbufOut);
ret.first = buffer;
ret.second = true;
}
else {
ret.first = bufferOut;
ret.second = false;
}
return ret;
}
char Image::set_interpolation__doc__[] =
"set_interpolation(scheme)\n"
"\n"
"Set the interpolation scheme to one of the module constants, "
"eg, image.NEAREST, image.BILINEAR, etc..."
;
Py::Object
Image::set_interpolation(const Py::Tuple& args) {
_VERBOSE("Image::set_interpolation");
args.verify_length(1);
size_t method = Py::Int(args[0]);
interpolation = (unsigned)method;
return Py::Object();
}
char Image::set_resample__doc__[] =
"set_resample(boolean)\n"
"\n"
"Set the resample flag."
;
Py::Object
Image::set_resample(const Py::Tuple& args) {
_VERBOSE("Image::set_resample");
args.verify_length(1);
int flag = Py::Int(args[0]);
resample = (bool)flag;
return Py::Object();
}
char Image::set_aspect__doc__[] =
"set_aspect(scheme)\n"
"\n"
"Set the aspect ration to one of the image module constant."
"eg, one of image.ASPECT_PRESERVE, image.ASPECT_FREE"
;
Py::Object
Image::set_aspect(const Py::Tuple& args) {
_VERBOSE("Image::set_aspect");
args.verify_length(1);
size_t method = Py::Int(args[0]);
aspect = (unsigned)method;
return Py::Object();
}
void
Image::init_type() {
_VERBOSE("Image::init_type");
behaviors().name("Image");
behaviors().doc("Image");
behaviors().supportGetattr();
behaviors().supportSetattr();
add_varargs_method( "apply_rotation", &Image::apply_rotation, Image::apply_rotation__doc__);
add_varargs_method( "apply_scaling", &Image::apply_scaling, Image::apply_scaling__doc__);
add_varargs_method( "apply_translation", &Image::apply_translation, Image::apply_translation__doc__);
add_keyword_method( "as_rgba_str", &Image::as_rgba_str, Image::as_rgba_str__doc__);
add_varargs_method( "color_conv", &Image::color_conv, Image::color_conv__doc__);
add_varargs_method( "buffer_rgba", &Image::buffer_rgba, Image::buffer_rgba__doc__);
add_varargs_method( "get_aspect", &Image::get_aspect, Image::get_aspect__doc__);
add_varargs_method( "get_interpolation", &Image::get_interpolation, Image::get_interpolation__doc__);
add_varargs_method( "get_resample", &Image::get_resample, Image::get_resample__doc__);
add_varargs_method( "get_size", &Image::get_size, Image::get_size__doc__);
add_varargs_method( "get_size_out", &Image::get_size_out, Image::get_size_out__doc__);
add_varargs_method( "reset_matrix", &Image::reset_matrix, Image::reset_matrix__doc__);
add_varargs_method( "get_matrix", &Image::get_matrix, Image::get_matrix__doc__);
add_keyword_method( "resize", &Image::resize, Image::resize__doc__);
add_varargs_method( "set_interpolation", &Image::set_interpolation, Image::set_interpolation__doc__);
add_varargs_method( "set_resample", &Image::set_resample, Image::set_resample__doc__);
add_varargs_method( "set_aspect", &Image::set_aspect, Image::set_aspect__doc__);
add_varargs_method( "set_bg", &Image::set_bg, Image::set_bg__doc__);
add_varargs_method( "flipud_out", &Image::flipud_out, Image::flipud_out__doc__);
add_varargs_method( "flipud_in", &Image::flipud_in, Image::flipud_in__doc__);
}
char _image_module_from_images__doc__[] =
"from_images(numrows, numcols, seq)\n"
"\n"
"return an image instance with numrows, numcols from a seq of image\n"
"instances using alpha blending. seq is a list of (Image, ox, oy)"
;
Py::Object
_image_module::from_images(const Py::Tuple& args) {
_VERBOSE("_image_module::from_images");
args.verify_length(3);
size_t numrows = (size_t)Py::Int(args[0]);
size_t numcols = (size_t)Py::Int(args[1]);
if (numrows >= 32768 || numcols >= 32768) {
throw Py::RuntimeError("numrows and numcols must both be less than 32768");
}
Py::SeqBase<Py::Object> tups = args[2];
size_t N = tups.length();
if (N==0)
throw Py::RuntimeError("Empty list of images");
Py::Tuple tup;
size_t ox(0), oy(0), thisx(0), thisy(0);
//copy image 0 output buffer into return images output buffer
Image* imo = new Image;
imo->rowsOut = numrows;
imo->colsOut = numcols;
size_t NUMBYTES(numrows * numcols * imo->BPP);
imo->bufferOut = new agg::int8u[NUMBYTES];
if (imo->bufferOut==NULL) //todo: also handle allocation throw
throw Py::MemoryError("_image_module::from_images could not allocate memory");
delete imo->rbufOut;
imo->rbufOut = new agg::rendering_buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
pixfmt pixf(*imo->rbufOut);
renderer_base rb(pixf);
rb.clear(agg::rgba(1, 1, 1, 1));
for (size_t imnum=0; imnum< N; imnum++) {
tup = Py::Tuple(tups[imnum]);
Image* thisim = static_cast<Image*>(tup[0].ptr());
ox = Py::Int(tup[1]);
oy = Py::Int(tup[2]);
bool isflip = (thisim->rbufOut->stride())<0;
//std::cout << "from images " << isflip << "; stride=" << thisim->rbufOut->stride() << std::endl;
size_t ind=0;
for (size_t j=0; j<thisim->rowsOut; j++) {
for (size_t i=0; i<thisim->colsOut; i++) {
thisx = i+ox;
if (isflip)
thisy = thisim->rowsOut - j + oy;
else
thisy = j+oy;
if (thisx>=numcols || thisy>=numrows) {
ind +=4;
continue;
}
pixfmt::color_type p;
p.r = *(thisim->bufferOut+ind++);
p.g = *(thisim->bufferOut+ind++);
p.b = *(thisim->bufferOut+ind++);
p.a = *(thisim->bufferOut+ind++);
pixf.blend_pixel(thisx, thisy, p, 255);
}
}
}
return Py::asObject(imo);
}
char _image_module_fromarray__doc__[] =
"fromarray(A, isoutput)\n"
"\n"
"Load the image from a numpy array\n"
"By default this function fills the input buffer, which can subsequently\n"
"be resampled using resize. If isoutput=1, fill the output buffer.\n"
"This is used to support raw pixel images w/o resampling"
;
Py::Object
_image_module::fromarray(const Py::Tuple& args) {
_VERBOSE("_image_module::fromarray");
args.verify_length(2);
Py::Object x = args[0];
int isoutput = Py::Int(args[1]);
//PyArrayObject *A = (PyArrayObject *) PyArray_ContiguousFromObject(x.ptr(), PyArray_DOUBLE, 2, 3);
PyArrayObject *A = (PyArrayObject *) PyArray_FromObject(x.ptr(), PyArray_DOUBLE, 2, 3);
if (A==NULL)
throw Py::ValueError("Array must be rank 2 or 3 of doubles");
Image* imo = new Image;
imo->rowsIn = A->dimensions[0];
imo->colsIn = A->dimensions[1];
size_t NUMBYTES(imo->colsIn * imo->rowsIn * imo->BPP);
agg::int8u *buffer = new agg::int8u[NUMBYTES];
if (buffer==NULL) //todo: also handle allocation throw
throw Py::MemoryError("_image_module::fromarray could not allocate memory");
if (isoutput) {
// make the output buffer point to the input buffer
imo->rowsOut = imo->rowsIn;
imo->colsOut = imo->colsIn;
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
}
else {
imo->bufferIn = buffer;
imo->rbufIn = new agg::rendering_buffer;
imo->rbufIn->attach(buffer, imo->colsIn, imo->rowsIn, imo->colsIn*imo->BPP);
}
if (A->nd == 2) { //assume luminance for now;
agg::int8u gray;
for (size_t rownum=0; rownum<imo->rowsIn; rownum++) {
for (size_t colnum=0; colnum<imo->colsIn; colnum++) {
double val = *(double *)(A->data + rownum*A->strides[0] + colnum*A->strides[1]);
gray = int(255 * val);
*buffer++ = gray; // red
*buffer++ = gray; // green
*buffer++ = gray; // blue
*buffer++ = 255; // alpha
}
}
}
else if (A->nd == 3) { // assume RGB
if (A->dimensions[2] != 3 && A->dimensions[2] != 4 ) {
Py_XDECREF(A);
throw Py::ValueError(Printf("3rd dimension must be length 3 (RGB) or 4 (RGBA); found %d", A->dimensions[2]).str());
}
int rgba = A->dimensions[2]==4;
double r,g,b,alpha;
int offset =0;
for (size_t rownum=0; rownum<imo->rowsIn; rownum++) {
for (size_t colnum=0; colnum<imo->colsIn; colnum++) {
offset = rownum*A->strides[0] + colnum*A->strides[1];
r = *(double *)(A->data + offset);
g = *(double *)(A->data + offset + A->strides[2] );
b = *(double *)(A->data + offset + 2*A->strides[2] );
if (rgba)
alpha = *(double *)(A->data + offset + 3*A->strides[2] );
else
alpha = 1.0;
*buffer++ = int(255*r); // red
*buffer++ = int(255*g); // green
*buffer++ = int(255*b); // blue
*buffer++ = int(255*alpha); // alpha
}
}
}
else { // error
Py_XDECREF(A);
throw Py::ValueError("Illegal array rank; must be rank; must 2 or 3");
}
buffer -= NUMBYTES;
Py_XDECREF(A);
return Py::asObject( imo );
}
char _image_module_fromarray2__doc__[] =
"fromarray2(A, isoutput)\n"
"\n"
"Load the image from a numpy array\n"
"By default this function fills the input buffer, which can subsequently\n"
"be resampled using resize. If isoutput=1, fill the output buffer.\n"
"This is used to support raw pixel images w/o resampling"
;
Py::Object
_image_module::fromarray2(const Py::Tuple& args) {
_VERBOSE("_image_module::fromarray2");
args.verify_length(2);
Py::Object x = args[0];
int isoutput = Py::Int(args[1]);
PyArrayObject *A = (PyArrayObject *) PyArray_ContiguousFromObject(x.ptr(), PyArray_DOUBLE, 2, 3);
//PyArrayObject *A = (PyArrayObject *) PyArray_FromObject(x.ptr(), PyArray_DOUBLE, 2, 3);
if (A==NULL)
throw Py::ValueError("Array must be rank 2 or 3 of doubles");
Image* imo = new Image;
imo->rowsIn = A->dimensions[0];
imo->colsIn = A->dimensions[1];
size_t NUMBYTES(imo->colsIn * imo->rowsIn * imo->BPP);
agg::int8u *buffer = new agg::int8u[NUMBYTES];
if (buffer==NULL) //todo: also handle allocation throw
throw Py::MemoryError("_image_module::fromarray could not allocate memory");
if (isoutput) {
// make the output buffer point to the input buffer
imo->rowsOut = imo->rowsIn;
imo->colsOut = imo->colsIn;
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
}
else {
imo->bufferIn = buffer;
imo->rbufIn = new agg::rendering_buffer;
imo->rbufIn->attach(buffer, imo->colsIn, imo->rowsIn, imo->colsIn*imo->BPP);
}
if (A->nd == 2) { //assume luminance for now;
agg::int8u gray;
const size_t N = imo->rowsIn * imo->colsIn;
size_t i = 0;
while (i++<N) {
double val = *(double *)(A->data++);
gray = int(255 * val);
*buffer++ = gray; // red
*buffer++ = gray; // green
*buffer++ = gray; // blue
*buffer++ = 255; // alpha
}
}
else if (A->nd == 3) { // assume RGB
if (A->dimensions[2] != 3 && A->dimensions[2] != 4 ) {
Py_XDECREF(A);
throw Py::ValueError(Printf("3rd dimension must be length 3 (RGB) or 4 (RGBA); found %d", A->dimensions[2]).str());
}
int rgba = A->dimensions[2]==4;
double r,g,b,alpha;
const size_t N = imo->rowsIn * imo->colsIn;
size_t i = 0;
while (i<N) {
r = *(double *)(A->data++);
g = *(double *)(A->data++);
b = *(double *)(A->data++);
if (rgba)
alpha = *(double *)(A->data++);
else
alpha = 1.0;
*buffer++ = int(255*r); // red
*buffer++ = int(255*g); // green
*buffer++ = int(255*b); // blue
*buffer++ = int(255*alpha); // alpha
}
}
else { // error
Py_XDECREF(A);
throw Py::ValueError("Illegal array rank; must be rank; must 2 or 3");
}
buffer -= NUMBYTES;
Py_XDECREF(A);
return Py::asObject( imo );
}
char _image_module_frombyte__doc__[] =
"frombyte(A, isoutput)\n"
"\n"
"Load the image from a byte array.\n"
"By default this function fills the input buffer, which can subsequently\n"
"be resampled using resize. If isoutput=1, fill the output buffer.\n"
"This is used to support raw pixel images w/o resampling."
;
Py::Object
_image_module::frombyte(const Py::Tuple& args) {
_VERBOSE("_image_module::frombyte");
args.verify_length(2);
Py::Object x = args[0];
int isoutput = Py::Int(args[1]);
PyArrayObject *A = (PyArrayObject *) PyArray_ContiguousFromObject(x.ptr(), PyArray_UBYTE, 3, 3);
if (A == NULL)
throw Py::ValueError("Array must have 3 dimensions");
if (A->dimensions[2]<3 || A->dimensions[2]>4)
throw Py::ValueError("Array dimension 3 must have size 3 or 4");
Image* imo = new Image;
imo->rowsIn = A->dimensions[0];
imo->colsIn = A->dimensions[1];
agg::int8u *arrbuf;
agg::int8u *buffer;
arrbuf = reinterpret_cast<agg::int8u *>(A->data);
size_t NUMBYTES(imo->colsIn * imo->rowsIn * imo->BPP);
buffer = new agg::int8u[NUMBYTES];
if (buffer==NULL) //todo: also handle allocation throw
throw Py::MemoryError("_image_module::frombyte could not allocate memory");
const size_t N = imo->rowsIn * imo->colsIn * imo->BPP;
size_t i = 0;
if (A->dimensions[2] == 4) {
memmove(buffer, arrbuf, N);
} else {
while (i < N) {
memmove(buffer, arrbuf, 3);
buffer += 3;
arrbuf += 3;
*buffer++ = 255;
i += 4;
}
buffer -= N;
arrbuf -= imo->rowsIn * imo->colsIn;
}
Py_XDECREF(A);
if (isoutput) {
// make the output buffer point to the input buffer
imo->rowsOut = imo->rowsIn;
imo->colsOut = imo->colsIn;
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
}
else {
imo->bufferIn = buffer;
imo->rbufIn = new agg::rendering_buffer;
imo->rbufIn->attach(buffer, imo->colsIn, imo->rowsIn, imo->colsIn*imo->BPP);
}
return Py::asObject( imo );
}
char _image_module_frombuffer__doc__[] =
"frombuffer(buffer, width, height, isoutput)\n"
"\n"
"Load the image from a character buffer\n"
"By default this function fills the input buffer, which can subsequently\n"
"be resampled using resize. If isoutput=1, fill the output buffer.\n"
"This is used to support raw pixel images w/o resampling."
;
Py::Object
_image_module::frombuffer(const Py::Tuple& args) {
_VERBOSE("_image_module::frombuffer");
args.verify_length(4);
PyObject *bufin = new_reference_to(args[0]);
size_t x = Py::Int(args[1]);
size_t y = Py::Int(args[2]);
if (x >= 32768 || y >= 32768) {
throw Py::ValueError("x and y must both be less than 32768");
}
int isoutput = Py::Int(args[3]);
if (PyObject_CheckReadBuffer(bufin) != 1)
throw Py::ValueError("First argument must be a buffer.");
Image* imo = new Image;
imo->rowsIn = y;
imo->colsIn = x;
Py_ssize_t NUMBYTES(imo->colsIn * imo->rowsIn * imo->BPP);
Py_ssize_t buflen;
const agg::int8u *rawbuf;
if (PyObject_AsReadBuffer(bufin, reinterpret_cast<const void**>(&rawbuf), &buflen) != 0)
throw Py::ValueError("Cannot get buffer from object.");
// Check buffer is required size.
if (buflen != NUMBYTES)
throw Py::ValueError("Buffer length must be width * height * 4.");
// Copy from input buffer to new buffer for agg.
agg::int8u* buffer = new agg::int8u[NUMBYTES];
if (buffer==NULL) //todo: also handle allocation throw
throw Py::MemoryError("_image_module::frombuffer could not allocate memory");
memmove(buffer, rawbuf, NUMBYTES);
if (isoutput) {
// make the output buffer point to the input buffer
imo->rowsOut = imo->rowsIn;
imo->colsOut = imo->colsIn;
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
}
else {
imo->bufferIn = buffer;
imo->rbufIn = new agg::rendering_buffer;
imo->rbufIn->attach(buffer, imo->colsIn, imo->rowsIn, imo->colsIn*imo->BPP);
}
return Py::asObject(imo);
}
// utilities for irregular grids
void _bin_indices_middle(unsigned int *irows, int nrows, float *ys1, int ny,float dy, float y_min)
{ int i,j, j_last;
unsigned int * rowstart = irows;
float *ys2 = ys1+1;
float *yl = ys1 + ny ;
float yo = y_min + dy/2.0;
float ym = 0.5f*(*ys1 + *ys2);
// y/rows
j = 0;
j_last = j;
for (i=0;i<nrows;i++,yo+=dy,rowstart++) {
while(ys2 != yl && yo > ym) {
ys1 = ys2;
ys2 = ys1+1;
ym = 0.5f*(*ys1 + *ys2);
j++;
}
*rowstart = j - j_last;
j_last = j;
}
}
void _bin_indices_middle_linear(float *arows, unsigned int *irows, int nrows, float *y, int ny,float dy, float y_min)
{ int i;
int ii = 0;
int iilast = ny-1;
float sc = 1/dy;
int iy0 = (int)floor(sc * (y[ii] - y_min) );
int iy1 = (int)floor(sc * (y[ii+1] - y_min) );
float invgap=1.0f/(iy1-iy0);
for (i=0; i<nrows && i<=iy0; i++) {
irows[i] = 0;
arows[i] = 1.0;
//std::cerr<<"i="<<i<<" ii="<<0<<" a="<< arows[i]<< std::endl;
}
for (; i<nrows; i++) {
while (i > iy1 && ii < iilast) {
ii++;
iy0 = iy1;
iy1 = (int)floor(sc * (y[ii+1] - y_min));
invgap=1.0f/(iy1-iy0);
}
if (i >= iy0 && i <= iy1) {
irows[i] = ii;
arows[i]=(iy1-i)*invgap;
//std::cerr<<"i="<<i<<" ii="<<ii<<" a="<< arows[i]<< std::endl;
}
else break;
}
for (; i<nrows; i++) {
irows[i] =iilast-1;
arows[i] = 0.0;
//std::cerr<<"i="<<i<<" ii="<<iilast-1<<" a="<< arows[i]<< std::endl;
}
}
void _bin_indices(int *irows, int nrows, double *y, int ny,
double sc, double offs)
{
int i;
if (sc*(y[ny-1] - y[0]) > 0)
{
int ii = 0;
int iilast = ny-1;
int iy0 = (int)floor(sc * (y[ii] - offs));
int iy1 = (int)floor(sc * (y[ii+1] - offs));
for (i=0; i<nrows && i<iy0; i++) {
irows[i] = -1;
}
for (; i<nrows; i++) {
while (i > iy1 && ii < iilast) {
ii++;
iy0 = iy1;
iy1 = (int)floor(sc * (y[ii+1] - offs));
}
if (i >= iy0 && i <= iy1) irows[i] = ii;
else break;
}
for (; i<nrows; i++) {
irows[i] = -1;
}
}
else
{
int iilast = ny-1;
int ii = iilast;
int iy0 = (int)floor(sc * (y[ii] - offs));
int iy1 = (int)floor(sc * (y[ii-1] - offs));
for (i=0; i<nrows && i<iy0; i++) {
irows[i] = -1;
}
for (; i<nrows; i++) {
while (i > iy1 && ii > 1) {
ii--;
iy0 = iy1;
iy1 = (int)floor(sc * (y[ii-1] - offs));
}
if (i >= iy0 && i <= iy1) irows[i] = ii-1;
else break;
}
for (; i<nrows; i++) {
irows[i] = -1;
}
}
}
void _bin_indices_linear(float *arows, int *irows, int nrows, double *y, int ny,
double sc, double offs)
{
int i;
if (sc*(y[ny-1] - y[0]) > 0)
{
int ii = 0;
int iilast = ny-1;
int iy0 = (int)floor(sc * (y[ii] - offs));
int iy1 = (int)floor(sc * (y[ii+1] - offs));
float invgap=1.0/(iy1-iy0);
for (i=0; i<nrows && i<iy0; i++) {
irows[i] = -1;
}
for (; i<nrows; i++) {
while (i > iy1 && ii < iilast) {
ii++;
iy0 = iy1;
iy1 = (int)floor(sc * (y[ii+1] - offs));
invgap=1.0/(iy1-iy0);
}
if (i >= iy0 && i <= iy1) {
irows[i] = ii;
arows[i]=(iy1-i)*invgap;
}
else break;
}
for (; i<nrows; i++) {
irows[i] = -1;
}
}
else
{
int iilast = ny-1;
int ii = iilast;
int iy0 = (int)floor(sc * (y[ii] - offs));
int iy1 = (int)floor(sc * (y[ii-1] - offs));
float invgap=1.0/(iy1-iy0);
for (i=0; i<nrows && i<iy0; i++) {
irows[i] = -1;
}
for (; i<nrows; i++) {
while (i > iy1 && ii > 1) {
ii--;
iy0 = iy1;
iy1 = (int)floor(sc * (y[ii-1] - offs));
invgap=1.0/(iy1-iy0);
}
if (i >= iy0 && i <= iy1) {
irows[i] = ii-1;
arows[i]=(i-iy0)*invgap;
}
else break;
}
for (; i<nrows; i++) {
irows[i] = -1;
}
}
}
char __image_module_pcolor__doc__[] =
"pcolor(x, y, data, rows, cols, bounds)\n"
"\n"
"Generate a pseudo-color image from data on a non-uniform grid using\n"
"nearest neighbour or linear interpolation.\n"
"bounds = (x_min, x_max, y_min, y_max)\n"
"interpolation = NEAREST or BILINEAR \n"
;
void _pcolor_cleanup(PyArrayObject* x, PyArrayObject* y, PyArrayObject *d,
unsigned int * rowstarts ,unsigned int*colstarts ,
float *acols , float *arows) {
if (x) {
Py_XDECREF(x);
}
if (y) {
Py_XDECREF(y);
}
if(d) {
Py_XDECREF(d);
}
if(rowstarts) {
PyMem_Free(rowstarts);
}
if(colstarts) {
PyMem_Free(colstarts);
}
if(acols) {
PyMem_Free(acols);
}
if(arows) {
PyMem_Free(arows);
}
return;
}
Py::Object
_image_module::pcolor(const Py::Tuple& args) {
_VERBOSE("_image_module::pcolor");
if (args.length() != 7)
throw Py::TypeError("Incorrect number of arguments (7 expected)");
Py::Object xp = args[0];
Py::Object yp = args[1];
Py::Object dp = args[2];
unsigned int rows = Py::Int(args[3]);
unsigned int cols = Py::Int(args[4]);
Py::Tuple bounds = args[5];
unsigned int interpolation = Py::Int(args[6]);
if (rows >= 32768 || cols >= 32768) {
throw Py::ValueError("rows and cols must both be less than 32768");
}
if (bounds.length() !=4)
throw Py::TypeError("Incorrect number of bounds (4 expected)");
float x_min = Py::Float(bounds[0]);
float x_max = Py::Float(bounds[1]);
float y_min = Py::Float(bounds[2]);
float y_max = Py::Float(bounds[3]);
float width = x_max - x_min;
float height = y_max - y_min;
float dx = width / ((float) cols);
float dy = height / ((float) rows);
// Check we have something to output to
if (rows == 0 || cols ==0)
throw Py::ValueError("Cannot scale to zero size");
PyArrayObject *x = NULL; PyArrayObject *y = NULL; PyArrayObject *d = NULL;
unsigned int * rowstarts = NULL; unsigned int*colstarts = NULL;
float *acols = NULL; float *arows = NULL;
// Get numpy arrays
x = (PyArrayObject *) PyArray_ContiguousFromObject(xp.ptr(), PyArray_FLOAT, 1, 1);
if (x == NULL) {
_pcolor_cleanup(x,y,d,rowstarts,colstarts,acols,arows);
throw Py::ValueError("x is of incorrect type (wanted 1D float)");
}
y = (PyArrayObject *) PyArray_ContiguousFromObject(yp.ptr(), PyArray_FLOAT, 1, 1);
if (y == NULL) {
_pcolor_cleanup(x,y,d,rowstarts,colstarts,acols,arows);
throw Py::ValueError("y is of incorrect type (wanted 1D float)");
}
d = (PyArrayObject *) PyArray_ContiguousFromObject(dp.ptr(), PyArray_UBYTE, 3, 3);
if (d == NULL) {
_pcolor_cleanup(x,y,d,rowstarts,colstarts,acols,arows);
throw Py::ValueError("data is of incorrect type (wanted 3D UInt8)");
}
if (d->dimensions[2] != 4) {
_pcolor_cleanup(x,y,d,rowstarts,colstarts,acols,arows);
throw Py::ValueError("data must be in RGBA format");
}
// Check dimensions match
int nx = x->dimensions[0];
int ny = y->dimensions[0];
if (nx != d->dimensions[1] || ny != d->dimensions[0]) {
_pcolor_cleanup(x,y,d,rowstarts,colstarts,acols,arows);
throw Py::ValueError("data and axis dimensions do not match");
}
// Allocate memory for pointer arrays
rowstarts = reinterpret_cast<unsigned int*>(PyMem_Malloc(sizeof(unsigned int)*rows));
if (rowstarts == NULL) {
_pcolor_cleanup(x,y,d,rowstarts,colstarts,acols,arows);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
colstarts = reinterpret_cast<unsigned int*>(PyMem_Malloc(sizeof(unsigned int)*cols));
if (colstarts == NULL) {
_pcolor_cleanup(x,y,d,rowstarts,colstarts,acols,arows);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
// Create output
Image* imo = new Image;
imo->rowsIn = rows;
imo->colsIn = cols;
imo->rowsOut = rows;
imo->colsOut = cols;
size_t NUMBYTES(rows * cols * 4);
agg::int8u *buffer = new agg::int8u[NUMBYTES];
if (buffer == NULL) {
_pcolor_cleanup(x,y,d,rowstarts,colstarts,acols,arows);
throw Py::MemoryError("Could not allocate memory for image");
}
// Calculate the pointer arrays to map input x to output x
unsigned int i, j;
unsigned int * colstart = colstarts;
unsigned int * rowstart = rowstarts;
float *xs1 = reinterpret_cast<float*>(x->data);
float *ys1 = reinterpret_cast<float*>(y->data);
// Copy data to output buffer
unsigned char *start;
unsigned char *inposition;
size_t inrowsize(nx*4);
size_t rowsize(cols*4);
rowstart = rowstarts;
agg::int8u * position = buffer;
agg::int8u * oldposition = NULL;
start = reinterpret_cast<unsigned char*>(d->data);
int s0 = d->strides[0];
int s1 = d->strides[1];
if(interpolation == Image::NEAREST) {
_bin_indices_middle(colstart, cols, xs1, nx,dx,x_min);
_bin_indices_middle(rowstart, rows, ys1, ny, dy,y_min);
for(i=0;i<rows;i++,rowstart++)
{
if (i > 0 && *rowstart == 0) {
memcpy(position, oldposition, rowsize*sizeof(agg::int8u));
oldposition = position;
position += rowsize;
} else {
oldposition = position;
start += *rowstart * inrowsize;
inposition = start;
for(j=0,colstart=colstarts;j<cols;j++,position+=4,colstart++) {
inposition += *colstart * 4;
memcpy(position, inposition, 4*sizeof(agg::int8u));
}
}
}
}
else if(interpolation == Image::BILINEAR) {
arows = reinterpret_cast<float *>(PyMem_Malloc(sizeof(float)*rows));
if (arows == NULL ) {
_pcolor_cleanup(x,y,d,rowstarts,colstarts,acols,arows);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
acols = reinterpret_cast<float*>(PyMem_Malloc(sizeof(float)*cols));
if (acols == NULL) {
_pcolor_cleanup(x,y,d,rowstarts,colstarts,acols,arows);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
_bin_indices_middle_linear(acols, colstart, cols, xs1, nx,dx,x_min);
_bin_indices_middle_linear(arows, rowstart, rows, ys1, ny, dy,y_min);
double a00,a01,a10,a11,alpha,beta;
agg::int8u * start00;
agg::int8u * start01;
agg::int8u * start10;
agg::int8u * start11;
// Copy data to output buffer
for (i=0; i<rows; i++)
{
for (j=0; j<cols; j++)
{
alpha=arows[i];
beta=acols[j];
a00=alpha*beta;
a01=alpha*(1.0-beta);
a10=(1.0-alpha)*beta;
a11=1.0-a00-a01-a10;
start00=(agg::int8u *)(start + s0*rowstart[i] + s1*colstart[j]);
start01=start00+s1;
start10=start00+s0;
start11=start10+s1;
position[0] =(agg::int8u)(start00[0]*a00+start01[0]*a01+start10[0]*a10+start11[0]*a11);
position[1] =(agg::int8u)(start00[1]*a00+start01[1]*a01+start10[1]*a10+start11[1]*a11);
position[2] =(agg::int8u)(start00[2]*a00+start01[2]*a01+start10[2]*a10+start11[2]*a11);
position[3] =(agg::int8u)(start00[3]*a00+start01[3]*a01+start10[3]*a10+start11[3]*a11);
position += 4;
}
}
}
// Attach output buffer to output buffer
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
_pcolor_cleanup(x,y,d,rowstarts,colstarts,acols,arows);
return Py::asObject(imo);
}
char __image_module_pcolor2__doc__[] =
"pcolor2(x, y, data, rows, cols, bounds, bg)\n"
"\n"
"Generate a pseudo-color image from data on a non-uniform grid\n"
"specified by its cell boundaries.\n"
"bounds = (x_left, x_right, y_bot, y_top)\n"
"bg = ndarray of 4 uint8 representing background rgba\n"
;
Py::Object
_image_module::pcolor2(const Py::Tuple& args) {
_VERBOSE("_image_module::pcolor2");
if (args.length() != 7)
throw Py::TypeError("Incorrect number of arguments (6 expected)");
Py::Object xp = args[0];
Py::Object yp = args[1];
Py::Object dp = args[2];
int rows = Py::Int(args[3]);
int cols = Py::Int(args[4]);
Py::Tuple bounds = args[5];
Py::Object bgp = args[6];
if (rows >= 32768 || cols >= 32768) {
throw Py::ValueError("rows and cols must both be less than 32768");
}
if (bounds.length() !=4)
throw Py::TypeError("Incorrect number of bounds (4 expected)");
double x_left = Py::Float(bounds[0]);
double x_right = Py::Float(bounds[1]);
double y_bot = Py::Float(bounds[2]);
double y_top = Py::Float(bounds[3]);
// Check we have something to output to
if (rows == 0 || cols ==0)
throw Py::ValueError("rows or cols is zero; there are no pixels");
// Get numpy arrays
PyArrayObject *x = (PyArrayObject *) PyArray_ContiguousFromObject(xp.ptr(),
PyArray_DOUBLE, 1, 1);
if (x == NULL)
throw Py::ValueError("x is of incorrect type (wanted 1D double)");
PyArrayObject *y = (PyArrayObject *) PyArray_ContiguousFromObject(yp.ptr(),
PyArray_DOUBLE, 1, 1);
if (y == NULL) {
Py_XDECREF(x);
throw Py::ValueError("y is of incorrect type (wanted 1D double)");
}
PyArrayObject *d = (PyArrayObject *) PyArray_ContiguousFromObject(dp.ptr(),
PyArray_UBYTE, 3, 3);
if (d == NULL) {
Py_XDECREF(x);
Py_XDECREF(y);
throw Py::ValueError("data is of incorrect type (wanted 3D uint8)");
}
if (d->dimensions[2] != 4) {
Py_XDECREF(x);
Py_XDECREF(y);
Py_XDECREF(d);
throw Py::ValueError("data must be in RGBA format");
}
// Check dimensions match
int nx = x->dimensions[0];
int ny = y->dimensions[0];
if (nx != d->dimensions[1]+1 || ny != d->dimensions[0]+1) {
Py_XDECREF(x);
Py_XDECREF(y);
Py_XDECREF(d);
throw Py::ValueError("data and axis bin boundary dimensions are incompatible");
}
PyArrayObject *bg = (PyArrayObject *) PyArray_ContiguousFromObject(bgp.ptr(),
PyArray_UBYTE, 1, 1);
if (bg == NULL) {
Py_XDECREF(x);
Py_XDECREF(y);
Py_XDECREF(d);
throw Py::ValueError("bg is of incorrect type (wanted 1D uint8)");
}
if (bg->dimensions[0] != 4) {
Py_XDECREF(x);
Py_XDECREF(y);
Py_XDECREF(d);
Py_XDECREF(bg);
throw Py::ValueError("bg must be in RGBA format");
}
// Allocate memory for pointer arrays
int * irows = reinterpret_cast<int*>(PyMem_Malloc(sizeof(int)*rows));
if (irows == NULL) {
Py_XDECREF(x);
Py_XDECREF(y);
Py_XDECREF(d);
Py_XDECREF(bg);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
int * jcols = reinterpret_cast<int*>(PyMem_Malloc(sizeof(int)*cols));
if (jcols == NULL) {
Py_XDECREF(x);
Py_XDECREF(y);
Py_XDECREF(d);
Py_XDECREF(bg);
PyMem_Free(irows);
throw Py::MemoryError("Cannot allocate memory for lookup table");
}
// Create output
Image* imo = new Image;
imo->rowsIn = rows;
imo->rowsOut = rows;
imo->colsIn = cols;
imo->colsOut = cols;
size_t NUMBYTES(rows * cols * 4);
agg::int8u *buffer = new agg::int8u[NUMBYTES];
if (buffer == NULL) {
Py_XDECREF(x);
Py_XDECREF(y);
Py_XDECREF(d);
Py_XDECREF(bg);
PyMem_Free(irows);
PyMem_Free(jcols);
throw Py::MemoryError("Could not allocate memory for image");
}
// Calculate the pointer arrays to map input x to output x
int i, j;
double *x0 = reinterpret_cast<double*>(x->data);
double *y0 = reinterpret_cast<double*>(y->data);
double sx = cols/(x_right - x_left);
double sy = rows/(y_top - y_bot);
_bin_indices(jcols, cols, x0, nx, sx, x_left);
_bin_indices(irows, rows, y0, ny, sy, y_bot);
// Copy data to output buffer
agg::int8u * position = buffer;
unsigned char *start = reinterpret_cast<unsigned char*>(d->data);
unsigned char *bgptr = reinterpret_cast<unsigned char*>(bg->data);
int s0 = d->strides[0];
int s1 = d->strides[1];
for (i=0; i<rows; i++)
{
for (j=0; j<cols; j++)
{
if (irows[i] == -1 || jcols[j] == -1) {
memcpy(position, bgptr, 4*sizeof(agg::int8u));
}
else {
memcpy(position, (start + s0*irows[i] + s1*jcols[j]),
4*sizeof(agg::int8u));
}
position += 4;
}
}
// Attach output buffer to output buffer
imo->rbufOut = new agg::rendering_buffer;
imo->bufferOut = buffer;
imo->rbufOut->attach(imo->bufferOut, imo->colsOut, imo->rowsOut, imo->colsOut * imo->BPP);
Py_XDECREF(x);
Py_XDECREF(y);
Py_XDECREF(d);
Py_XDECREF(bg);
PyMem_Free(irows);
PyMem_Free(jcols);
return Py::asObject(imo);
}
#if defined(_MSC_VER)
DL_EXPORT(void)
#elif defined(__cplusplus)
extern "C" void
#else
void
#endif
init_image(void) {
_VERBOSE("init_image");
static _image_module* _image = new _image_module;
import_array();
Py::Dict d = _image->moduleDictionary();
d["NEAREST"] = Py::Int(Image::NEAREST);
d["BILINEAR"] = Py::Int(Image::BILINEAR);
d["BICUBIC"] = Py::Int(Image::BICUBIC);
d["SPLINE16"] = Py::Int(Image::SPLINE16);
d["SPLINE36"] = Py::Int(Image::SPLINE36);
d["HANNING"] = Py::Int(Image::HANNING);
d["HAMMING"] = Py::Int(Image::HAMMING);
d["HERMITE"] = Py::Int(Image::HERMITE);
d["KAISER"] = Py::Int(Image::KAISER);
d["QUADRIC"] = Py::Int(Image::QUADRIC);
d["CATROM"] = Py::Int(Image::CATROM);
d["GAUSSIAN"] = Py::Int(Image::GAUSSIAN);
d["BESSEL"] = Py::Int(Image::BESSEL);
d["MITCHELL"] = Py::Int(Image::MITCHELL);
d["SINC"] = Py::Int(Image::SINC);
d["LANCZOS"] = Py::Int(Image::LANCZOS);
d["BLACKMAN"] = Py::Int(Image::BLACKMAN);
d["ASPECT_FREE"] = Py::Int(Image::ASPECT_FREE);
d["ASPECT_PRESERVE"] = Py::Int(Image::ASPECT_PRESERVE);
}