/* -*- mode: c++; c-basic-offset: 4 -*- */
#define NO_IMPORT_ARRAY
#include
#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_rasterizer_sl_clip.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"
#include "agg_workaround.h"
typedef fixed_blender_rgba_plain fixed_blender_rgba32_plain;
typedef agg::pixfmt_alpha_blend_rgba pixfmt;
typedef fixed_blender_rgba_pre fixed_blender_rgba32_pre;
typedef agg::pixfmt_alpha_blend_rgba 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)
{
}
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()
{
delete[] bufferIn;
bufferIn = NULL;
delete rbufIn;
rbufIn = NULL;
delete rbufOut;
rbufOut = NULL;
delete[] bufferOut;
bufferOut = NULL;
}
void Image::apply_rotation(double r)
{
agg::trans_affine M = agg::trans_affine_rotation(r * agg::pi / 180.0);
srcMatrix *= M;
imageMatrix *= M;
}
void Image::set_bg(double r, double g, double b, double a)
{
bg.r = r;
bg.g = g;
bg.b = b;
bg.a = a;
}
void Image::apply_scaling(double sx, double sy)
{
agg::trans_affine M = agg::trans_affine_scaling(sx, sy);
srcMatrix *= M;
imageMatrix *= M;
}
void Image::apply_translation(double tx, double ty)
{
agg::trans_affine M = agg::trans_affine_translation(tx, ty);
srcMatrix *= M;
imageMatrix *= M;
}
void Image::as_rgba_str(agg::int8u *outbuf)
{
agg::rendering_buffer rb;
rb.attach(outbuf, colsOut, rowsOut, colsOut * 4);
rb.copy_from(*rbufOut);
}
void Image::color_conv(int format, agg::int8u *outbuf)
{
int row_len = colsOut * 4;
agg::rendering_buffer rtmp;
rtmp.attach(outbuf, 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 "Image::color_conv unknown format";
}
}
void Image::reset_matrix(void)
{
srcMatrix.reset();
imageMatrix.reset();
}
void Image::resize(int numcols, int numrows, int norm, double radius)
{
if (bufferIn == NULL) {
throw "You must first load the image";
}
if (numcols <= 0 || numrows <= 0) {
throw "Width and height must have positive values";
}
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 "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);
rasterizer ras;
agg::scanline_u8 sl;
ras.clip_box(0, 0, numcols, numrows);
imageMatrix.invert();
interpolator_type interpolator(imageMatrix);
typedef agg::span_allocator span_alloc_type;
span_alloc_type sa;
// the image path
agg::path_storage path;
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 imageBox(path, srcMatrix);
ras.add_path(imageBox);
typedef agg::wrap_mode_reflect reflect_type;
typedef agg::image_accessor_wrap img_accessor_type;
pixfmt_pre pixfmtin(*rbufIn);
img_accessor_type ia(pixfmtin);
switch (interpolation) {
case NEAREST: {
typedef agg::span_image_filter_rgba_nn span_gen_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;
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 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);
} 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;
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 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);
} else {
typedef agg::span_image_filter_rgba 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;
}
}
void Image::clear()
{
pixfmt pixf(*rbufOut);
renderer_base rb(pixf);
rb.clear(bg);
}
void Image::blend_image(Image &im, unsigned ox, unsigned oy, bool apply_alpha, float alpha)
{
unsigned thisx = 0, thisy = 0;
pixfmt pixf(*rbufOut);
renderer_base rb(pixf);
bool isflip = (im.rbufOut->stride()) < 0;
size_t ind = 0;
for (unsigned j = 0; j < im.rowsOut; j++) {
if (isflip) {
thisy = im.rowsOut - j + oy;
} else {
thisy = j + oy;
}
for (unsigned i = 0; i < im.colsOut; i++) {
thisx = i + ox;
if (thisx >= colsOut || thisy >= rowsOut) {
ind += 4;
continue;
}
pixfmt::color_type p;
p.r = *(im.bufferOut + ind++);
p.g = *(im.bufferOut + ind++);
p.b = *(im.bufferOut + ind++);
if (apply_alpha) {
p.a = (pixfmt::value_type) * (im.bufferOut + ind++) * alpha;
} else {
p.a = *(im.bufferOut + ind++);
}
pixf.blend_pixel(thisx, thisy, p, 255);
}
}
}
// utilities for irregular grids
void _bin_indices_middle(
unsigned int *irows, int nrows, const float *ys1, unsigned long ny, float dy, float y_min)
{
int i, j, j_last;
unsigned int *rowstart = irows;
const float *ys2 = ys1 + 1;
const 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,
const float *y,
unsigned long ny,
float dy,
float y_min)
{
int i;
int ii = 0;
int iilast = (int)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;
}
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;
} else
break;
}
for (; i < nrows; i++) {
irows[i] = iilast - 1;
arows[i] = 0.0;
}
}
void _bin_indices(int *irows, int nrows, const double *y, unsigned long ny, double sc, double offs)
{
int i;
if (sc * (y[ny - 1] - y[0]) > 0) {
int ii = 0;
int iilast = (int)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 = (int)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, unsigned long ny, double sc, double offs)
{
int i;
if (sc * (y[ny - 1] - y[0]) > 0) {
int ii = 0;
int iilast = (int)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 = (int)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;
}
}
}