Skip to content

Commit

Permalink
Implement image interpolation modes (celiagg#100)
Browse files Browse the repository at this point in the history
  • Loading branch information
jwiggins authored Mar 26, 2021
1 parent 85a4785 commit 56c86bf
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 42 deletions.
14 changes: 7 additions & 7 deletions celiagg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
from ._celiagg import (
AggError, BSpline, BlendMode, DrawingMode, FontCache, FontWeight,
FreeTypeFont, GradientSpread, GradientUnits, GraphicsState, Image,
InnerJoin, LineCap, LineJoin, LinearGradientPaint, Path, PatternPaint,
PatternStyle, PixelFormat, RadialGradientPaint, Rect, ShapeAtPoints,
SolidPaint, TextDrawingMode, Transform, Win32Font,
InterpolationMode, InnerJoin, LineCap, LineJoin, LinearGradientPaint,
Path, PatternPaint, PatternStyle, PixelFormat, RadialGradientPaint, Rect,
ShapeAtPoints, SolidPaint, TextDrawingMode, Transform, Win32Font,
)

# Query the library
Expand Down Expand Up @@ -61,10 +61,10 @@ def example_font():

'AggError', 'BlendMode', 'BSpline', 'DrawingMode', 'Font', 'FontCache',
'FontWeight', 'FreeTypeFont', 'GradientSpread', 'GradientUnits',
'GraphicsState', 'Image', 'InnerJoin', 'LinearGradientPaint', 'LineCap',
'LineJoin', 'RadialGradientPaint', 'Path', 'PatternPaint', 'PatternStyle',
'PixelFormat', 'Rect', 'ShapeAtPoints', 'SolidPaint', 'TextDrawingMode',
'Transform', 'Win32Font',
'GraphicsState', 'Image', 'InterpolationMode', 'InnerJoin',
'LinearGradientPaint', 'LineCap', 'LineJoin', 'RadialGradientPaint',
'Path', 'PatternPaint', 'PatternStyle', 'PixelFormat', 'Rect',
'ShapeAtPoints', 'SolidPaint', 'TextDrawingMode', 'Transform', 'Win32Font',

'CanvasG8', 'CanvasGA16', 'CanvasRGB24', 'CanvasRGBA32', 'CanvasBGRA32',
'CanvasRGBA128',
Expand Down
13 changes: 13 additions & 0 deletions celiagg/_enums.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ cdef extern from "graphics_state.h" namespace "GraphicsState":
BlendDifference
BlendExclusion

cdef enum InterpolationMode:
InterpolationNearest
InterpolationBilinear
InterpolationBicubic
InterpolationSpline16
InterpolationSpline36
InterpolationSinc64
InterpolationSinc144
InterpolationSinc256
InterpolationBlackman64
InterpolationBlackman100
InterpolationBlackman256


cdef extern from "paint.h" namespace "Paint":
cdef enum PaintType:
Expand Down
3 changes: 3 additions & 0 deletions celiagg/_graphics_state.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ cdef extern from "graphics_state.h":
void image_blend_mode(_enums.BlendMode m)
_enums.BlendMode image_blend_mode() const

void image_interpolation_mode(_enums.InterpolationMode m)
_enums.InterpolationMode image_interpolation_mode() const

void master_alpha(double a)
double master_alpha() const

Expand Down
13 changes: 13 additions & 0 deletions celiagg/enums.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,19 @@ cpdef enum BlendMode:
BlendDifference = _enums.BlendDifference
BlendExclusion = _enums.BlendExclusion

cpdef enum InterpolationMode:
Nearest = _enums.InterpolationNearest
Bilinear = _enums.InterpolationBilinear
Bicubic = _enums.InterpolationBicubic
Spline16 = _enums.InterpolationSpline16
Spline36 = _enums.InterpolationSpline36
Sinc64 = _enums.InterpolationSinc64
Sinc144 = _enums.InterpolationSinc144
Sinc256 = _enums.InterpolationSinc256
Blackman64 = _enums.InterpolationBlackman64
Blackman100 = _enums.InterpolationBlackman100
Blackman256 = _enums.InterpolationBlackman256

cpdef enum FontWeight:
Any = _enums.k_FontWeightAny
Thin = _enums.k_FontWeightThin
Expand Down
52 changes: 36 additions & 16 deletions celiagg/graphics_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,29 @@ class GraphicsState
BlendExclusion = agg::comp_op_exclusion,
};

enum InterpolationMode
{
InterpolationNearest = 0,
InterpolationBilinear,
InterpolationBicubic,
InterpolationSpline16,
InterpolationSpline36,
InterpolationSinc64,
InterpolationSinc144,
InterpolationSinc256,
InterpolationBlackman64,
InterpolationBlackman100,
InterpolationBlackman256,
};

GraphicsState() :
m_clip_box(0.0, 0.0, -1.0, -1.0), // Invalid by default!
m_stencil(NULL),
m_drawing_mode(DrawFillStroke),
m_text_drawing_mode(TextDrawRaster),
m_blend_mode(BlendAlpha),
m_image_blend_mode(BlendDst),
m_image_interp_mode(InterpolationNearest),
m_master_alpha(1.0),
m_line_dash_phase(0.0),
m_miter_limit(1.0),
Expand Down Expand Up @@ -152,6 +168,9 @@ class GraphicsState
void image_blend_mode(BlendMode m) { m_image_blend_mode = m; }
BlendMode image_blend_mode() const { return m_image_blend_mode; }

void image_interpolation_mode(InterpolationMode m) { m_image_interp_mode = m; }
InterpolationMode image_interpolation_mode() const { return m_image_interp_mode; }

void master_alpha(double a) { m_master_alpha = a; }
double master_alpha() const { return m_master_alpha; }

Expand Down Expand Up @@ -192,22 +211,23 @@ class GraphicsState
const Image* stencil() const { return m_stencil; }

private:
Rect m_clip_box;
DashPattern m_dashes;
const Image* m_stencil;
DrawingMode m_drawing_mode;
TextDrawingMode m_text_drawing_mode;
BlendMode m_blend_mode;
BlendMode m_image_blend_mode;
double m_master_alpha;
double m_line_dash_phase;
double m_miter_limit;
double m_inner_miter_limit;
double m_line_width;
LineCap m_line_cap;
LineJoin m_line_join;
InnerJoin m_inner_join;
bool m_anti_aliased;
Rect m_clip_box;
DashPattern m_dashes;
const Image* m_stencil;
DrawingMode m_drawing_mode;
TextDrawingMode m_text_drawing_mode;
BlendMode m_blend_mode;
BlendMode m_image_blend_mode;
InterpolationMode m_image_interp_mode;
double m_master_alpha;
double m_line_dash_phase;
double m_miter_limit;
double m_inner_miter_limit;
double m_line_width;
LineCap m_line_cap;
LineJoin m_line_join;
InnerJoin m_inner_join;
bool m_anti_aliased;
};

#endif // CELIAGG_GRAPHICS_STATE_H
8 changes: 8 additions & 0 deletions celiagg/graphics_state.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ cdef class GraphicsState:
mode.
* blend_mode: A ``BlendMode`` for non-image drawing. (ignored)
* image_blend_mode: A ``BlendMode`` for image drawing. (ignored)
* image_interpolation_moded: An ``InterpolationMode`` for image drawing.
* line_cap: A ``LineCap`` value denoting the style of line ends.
* line_join: A ``LineJoin`` value denoting the style of joins.
* inner_join: An ``InnerJoin`` value denoting the style of inner joins.
Expand Down Expand Up @@ -199,6 +200,13 @@ cdef class GraphicsState:
def __set__(self, BlendMode m):
self._this.image_blend_mode(m)

property image_interpolation_mode:
def __get__(self):
return InterpolationMode(self._this.image_interpolation_mode())

def __set__(self, InterpolationMode m):
self._this.image_interpolation_mode(m)

property master_alpha:
def __get__(self):
return self._this.master_alpha()
Expand Down
5 changes: 4 additions & 1 deletion celiagg/ndarray_canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,14 @@ class ndarray_canvas : public ndarray_canvas_base

private:

template<typename base_renderer_t, typename span_gen_t>
template<typename base_renderer_t>
void _draw_image_internal(Image& img,
const agg::trans_affine& transform,
const GraphicsState& gs,
base_renderer_t& renderer);
template<typename base_renderer_t, typename span_gen_t>
void _draw_image_final(base_renderer_t& renderer,
span_gen_t& span_generator);
template<typename base_renderer_t>
void _draw_shape_internal(VertexSource& shape,
const agg::trans_affine& transform,
Expand Down
94 changes: 78 additions & 16 deletions celiagg/ndarray_canvas.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,17 @@ template<typename pixfmt_t>
void ndarray_canvas<pixfmt_t>::draw_image(Image& img,
const agg::trans_affine& transform, const GraphicsState& gs)
{
typedef typename image_filters<pixfmt_t>::nearest_t span_gen_t;

_set_clipping(gs.clip_box());
// XXX: Apply master alpha here somehow!

if (gs.stencil() == NULL)
{
_draw_image_internal<renderer_t, span_gen_t>(img, transform, gs, m_renderer);
_draw_image_internal<renderer_t>(img, transform, gs, m_renderer);
}
else
{
_WITH_MASKED_RENDERER(gs, renderer)
_draw_image_internal<masked_renderer_t, span_gen_t>(img, transform, gs, renderer);
_draw_image_internal<masked_renderer_t>(img, transform, gs, renderer);
}
}

Expand Down Expand Up @@ -175,33 +173,97 @@ void ndarray_canvas<pixfmt_t>::draw_text(const char* text,
}

template<typename pixfmt_t>
template<typename base_renderer_t, typename span_gen_t>
template<typename base_renderer_t>
void ndarray_canvas<pixfmt_t>::_draw_image_internal(Image& img,
const agg::trans_affine& transform, const GraphicsState& gs,
base_renderer_t& renderer)
{
typedef typename image_filters<pixfmt_t>::source_t source_t;
typedef typename agg::span_allocator<typename pixfmt_t::color_type> span_alloc_t;
typedef agg::renderer_scanline_aa<base_renderer_t, span_alloc_t, span_gen_t> img_renderer_t;
typedef agg::conv_transform<agg::path_storage> trans_curve_t;

agg::path_storage img_outline = img.image_outline();
pixfmt_t src_pix(img.get_buffer());
typename pixfmt_t::color_type back_color(agg::rgba(0.5, 0.5, 0.5, 1.0));
source_t source(src_pix, back_color);

agg::trans_affine src_mtx = transform;
agg::trans_affine inv_img_mtx = transform;
inv_img_mtx.invert();
agg::trans_affine inv_img_mtx = ~src_mtx;
interpolator_t interpolator(inv_img_mtx);

typename pixfmt_t::color_type back_color(agg::rgba(0.5, 0.5, 0.5, 1.0));
source_t source(src_pix, back_color);
span_gen_t span_generator(source, interpolator);
span_alloc_t span_allocator;
img_renderer_t img_renderer(renderer, span_allocator, span_generator);

m_rasterizer.reset();
agg::path_storage img_outline = img.image_outline();
trans_curve_t trans_outline(img_outline, src_mtx);
m_rasterizer.add_path(trans_outline);

switch (gs.image_interpolation_mode())
{
case GraphicsState::InterpolationNearest:
{
typedef typename image_filters<pixfmt_t>::nearest_t span_gen_t;

span_gen_t span_generator(source, interpolator);
_draw_image_final<base_renderer_t>(renderer, span_generator);
break;
}
case GraphicsState::InterpolationBilinear:
{
typedef typename image_filters<pixfmt_t>::bilinear_t span_gen_t;

span_gen_t span_generator(source, interpolator);
_draw_image_final<base_renderer_t>(renderer, span_generator);
break;
}
case GraphicsState::InterpolationBicubic:
case GraphicsState::InterpolationSpline16:
case GraphicsState::InterpolationSpline36:
case GraphicsState::InterpolationSinc64:
case GraphicsState::InterpolationSinc144:
case GraphicsState::InterpolationSinc256:
case GraphicsState::InterpolationBlackman64:
case GraphicsState::InterpolationBlackman100:
case GraphicsState::InterpolationBlackman256:
{
typedef typename image_filters<pixfmt_t>::general_t span_gen_t;
agg::image_filter_lut filter;
switch (gs.image_interpolation_mode())
{
case GraphicsState::InterpolationBicubic:
filter.calculate(agg::image_filter_bicubic()); break;
case GraphicsState::InterpolationSpline16:
filter.calculate(agg::image_filter_spline16()); break;
case GraphicsState::InterpolationSpline36:
filter.calculate(agg::image_filter_spline36()); break;
case GraphicsState::InterpolationSinc64:
filter.calculate(agg::image_filter_sinc64()); break;
case GraphicsState::InterpolationSinc144:
filter.calculate(agg::image_filter_sinc144()); break;
case GraphicsState::InterpolationSinc256:
filter.calculate(agg::image_filter_sinc256()); break;
case GraphicsState::InterpolationBlackman64:
filter.calculate(agg::image_filter_blackman64()); break;
case GraphicsState::InterpolationBlackman100:
filter.calculate(agg::image_filter_blackman100()); break;
case GraphicsState::InterpolationBlackman256:
filter.calculate(agg::image_filter_blackman256()); break;
default: break;
}

span_gen_t span_generator(source, interpolator, filter);
_draw_image_final<base_renderer_t>(renderer, span_generator);
break;
}
}
}

template<typename pixfmt_t>
template<typename base_renderer_t, typename span_gen_t>
void ndarray_canvas<pixfmt_t>::_draw_image_final(
base_renderer_t& renderer, span_gen_t& span_generator)
{
typedef typename agg::span_allocator<typename pixfmt_t::color_type> span_alloc_t;
typedef agg::renderer_scanline_aa<base_renderer_t, span_alloc_t, span_gen_t> img_renderer_t;

span_alloc_t span_allocator;
img_renderer_t img_renderer(renderer, span_allocator, span_generator);
agg::render_scanlines(m_rasterizer, m_scanline, img_renderer);
}

Expand Down
15 changes: 13 additions & 2 deletions celiagg/tests/test_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@

import numpy as np

from celiagg import (GraphicsState, BlendMode, DrawingMode, Image, InnerJoin,
LineCap, LineJoin, PixelFormat, Rect, TextDrawingMode)
from celiagg import (
BlendMode, DrawingMode, GraphicsState, Image, InterpolationMode, InnerJoin,
LineCap, LineJoin, PixelFormat, Rect, TextDrawingMode
)


def array_bases_equal(arr0, arr1):
Expand Down Expand Up @@ -89,6 +91,12 @@ def test_state_properties(self):
gs.image_blend_mode = BlendMode.BlendLighten
self.assertEqual(gs.image_blend_mode, BlendMode.BlendLighten)

gs.image_interpolation_mode = InterpolationMode.Sinc64
self.assertEqual(gs.image_interpolation_mode, InterpolationMode.Sinc64)
gs.image_interpolation_mode = InterpolationMode.Blackman100
self.assertEqual(gs.image_interpolation_mode,
InterpolationMode.Blackman100)

gs.line_cap = LineCap.CapSquare
self.assertEqual(gs.line_cap, LineCap.CapSquare)
gs.line_cap = LineCap.CapRound
Expand Down Expand Up @@ -195,6 +203,7 @@ def test_copy(self):
text_drawing_mode=TextDrawingMode.TextDrawFillStroke,
blend_mode=BlendMode.BlendXor,
image_blend_mode=BlendMode.BlendXor,
image_interpolation_mode=InterpolationMode.Sinc64,
line_cap=LineCap.CapSquare,
line_join=LineJoin.JoinRound,
inner_join=InnerJoin.InnerRound,
Expand All @@ -215,6 +224,8 @@ def test_copy(self):
TextDrawingMode.TextDrawFillStroke)
self.assertEqual(cpy.blend_mode, BlendMode.BlendXor)
self.assertEqual(cpy.image_blend_mode, BlendMode.BlendXor)
self.assertEqual(cpy.image_interpolation_mode,
InterpolationMode.Sinc64)
self.assertEqual(cpy.line_cap, LineCap.CapSquare)
self.assertEqual(cpy.line_join, LineJoin.JoinRound)
self.assertEqual(cpy.inner_join, InnerJoin.InnerRound)
Expand Down

0 comments on commit 56c86bf

Please sign in to comment.