You are on page 1of 38

Anti-Grain Geometry - Tips and Tricks

Home/
News Docs Download Mailing List CVS

Some Interesting Issues, not Classified


Simple Color Interpolation in a Square Image Parallelogram Transformations Using WinAPI to Render Text Compiling AGG under Microsoft eMbedded VC 4.0 Working with Gradients The Problem of Line Alignment
Copyright 2002-2006 Maxim Shemanarev Web Design and Programming Maxim Shemanarev

Tips and Tricks

http://www.antigrain.com/tips/index.html[2. 11. 2010 20:37:08]

Anti-Grain Geometry - Tips and Tricks

http://www.antigrain.com/tips/index.html[2. 11. 2010 20:37:08]

Anti-Grain Geometry - Color Interpolation

Home/ Tips & Tricks/


News Docs Download Mailing List CVS

Simple Color Interpolation in a Square


Anti-Grain Geometry has classes for linear, Bresenham-like interpolation with subpixel accuracy. They are dda_line_interpolator, dda2_line_interpolator, and line_bresenham_interpolator. And they can be also used to interpolate colors in color selection controls like the one in Xara X:

Color Interpolation

Below is the class that interpolates colors of the rgba8 type, and the function to draw a square with interpolation. It can be easily modified to draw a rectangle, but it's not really necessary to render the color editing control. #include #include #include #include #include #include <stdio.h> <string.h> "agg_rendering_buffer.h" "agg_pixfmt_rgb24.h" "agg_renderer_base.h" "agg_dda_line.h"

enum { square_size };

= 200

// Writing the buffer to a .PPM file, assuming it has // RGB-structure, one byte per color component //-------------------------------------------------bool write_ppm(const unsigned char* buf, unsigned width, unsigned height, const char* file_name) { FILE* fd = fopen(file_name, "wb"); if(fd) { fprintf(fd, "P6 %d %d 255 ", width, height); fwrite(buf, 1, width * height * 3, fd); fclose(fd); return true; } return false; } namespace agg { class color_interpolator_rgba8 { public: color_interpolator_rgba8(agg::rgba8 c1, agg::rgba8 c2, unsigned len) : m_r(c1.r, c2.r, len), m_g(c1.g, c2.g, len), m_b(c1.b, c2.b, len), m_a(c1.a, c2.a, len) { } void operator ++ () { ++m_r; ++m_g; ++m_b; ++m_a;

http://www.antigrain.com/tips/square_color_interpolation/square_color_interpolation.agdoc.html[2. 11. 2010 20:37:11]

Anti-Grain Geometry - Color Interpolation } rgba8 color() const { return rgba8(m_r.y(), m_g.y(), m_b.y(), m_a.y()); } private: dda_line_interpolator<16> dda_line_interpolator<16> dda_line_interpolator<16> dda_line_interpolator<16> }; m_r; m_g; m_b; m_a;

// Rendering a square with color interpolation between its corners // The colors of the corners are ordered CCW started from bottom-left, // assuming that the Y axis goes up. //-----------------------------------------------------------------template<class Renderer> void color_square_rgba8(Renderer& r, int x, int y, int size, rgba8 c1, rgba8 c2, rgba8 c3, rgba8 c4) { int i, j; color_interpolator_rgba8 cy1(c1, c4, size); color_interpolator_rgba8 cy2(c2, c3, size); for(i = 0; i < size; ++i) { color_interpolator_rgba8 cx(cy1.color(), cy2.color(), size); for(j = 0; j < size; ++j) { r.copy_pixel(x + j, y + i, cx.color()); ++cx; } ++cy1; ++cy2; } } } int main() { unsigned char* buffer = new unsigned char[square_size * square_size * 3]; agg::rendering_buffer rbuf(buffer, square_size, square_size, -square_size * 3); // Flip Y to go up agg::pixfmt_rgb24 pf(rbuf); agg::renderer_base<agg::pixfmt_rgb24> rbase(pf); agg::color_square_rgba8(rbase, 0, 0, square_size, agg::rgba8(0xc6, 0, 0), agg::rgba8(0xc6, 0, 0xff), agg::rgba8(0xc6, 0xff, 0xff), agg::rgba8(0xc6, 0xfe, 0)); // // // // Bottom-left Bottom-right Top-right Top-left

write_ppm(buffer, square_size, square_size, "agg_test.ppm"); delete [] buffer; return 0; } Here is the result:

It's not included into the distribution package because it's rather a specific class. Besides, it depends on the rgba8 color type.
Copyright

2002-2006

Maxim Shemanarev

http://www.antigrain.com/tips/square_color_interpolation/square_color_interpolation.agdoc.html[2. 11. 2010 20:37:11]

Anti-Grain Geometry - Color Interpolation


Web Design and Programming Maxim Shemanarev

http://www.antigrain.com/tips/square_color_interpolation/square_color_interpolation.agdoc.html[2. 11. 2010 20:37:11]

Anti-Grain Geometry - Image Parallelogram Transformations

Home/ Tips & Tricks/


News Docs Download Mailing List CVS

Image Parallelogram Transformations


Using perspective transformations to simulate the functionality of WinAPI PlgBlt()
The declaration of PlgBlt() is: BOOL PlgBlt( HDC hdcDest, CONST POINT *lpPoint, HDC hdcSrc, int nXSrc, int nYSrc, int nWidth, int nHeight, HBITMAP hbmMask, int xMask, int yMask ); // handle to destination device context // vertices of destination parallelogram // handle to source device context // x-coord. of upper-left corner of source rectangle. // y-coord. of upper-left corner of source rectangle. // width of source rectangle // height of source rectangle // handle to bitmask // x-coord. of upper-left corner of bitmask rectangle. // y-coord. of upper-left corner of bitmask rectangle.

Here the most important argument is: lpPoint Pointer to an array of three points in logical space that identify three corners of the destination parallelogram. The upper-left corner of the source rectangle is mapped to the first point in this array, the upper-right corner to the second point in this array, and the lower-left corner to the third point. The lower-right corner of the source rectangle is mapped to the implicit fourth point in the parallelogram. It means that this function can apply arbitrary affine transformations to the image. Anti-Grain Geometry can do that too, but there's a problem with proper calculating of the affine transformation matrix. It really is tricky. In AGG there are good news, bad news, and then good news again. The good news is that you can use the perspective transformations that in general can transform an arbitrary convex qudrangle to another convex quadrangle, particularly, a rectangle to an arbitrary parallelogram. The bad news is that in general case, the perspective transformations work much slower than the affine ones. It's because the image transformations use the scanline approach. You take your destination scanline (a row of pixels in the destination canvas), then apply the reverse transformations to each pixel and pick up the source pixel possibly considering a filter (bilinear, bicubic, etc). In case of affine transformations we don't have to calculete every point directly. Instead, we can calculate only two points for each scanline (begin and end) and use a bresenham-like linear interpolator that works in integer coordinates, thus very fast. But the restriction is that the transformations must be linear and parellel. It means that any straight line must remain straight after applying the transformation, and any two parallel lines must remain parallel. In case of perspective transformations it is not so (they are not parallel), and we cannot use linear interpolation. The good news again is that the parallelogram case of the perspective transformations is linear and parallel, so, the the linear interpolation is perfectly applicable and it will work as fast as the image affine transformations. To demonstrate it we modify the AGG example image_perspective.cpp (it can be found in agg2/examples/). Just replace the code of image_perspective.cpp to the following: #include #include #include #include #include #include #include #include #include <stdlib.h> <ctype.h> <stdio.h> "agg_basics.h" "agg_rendering_buffer.h" "agg_rasterizer_scanline_aa.h" "agg_scanline_u.h" "agg_renderer_scanline.h" "agg_path_storage.h"

http://www.antigrain.com/tips/image_plg/image_plg.agdoc.html[2. 11. 2010 20:37:14]

Anti-Grain Geometry - Image Parallelogram Transformations #include #include #include #include #include #include #include #include #include #include "agg_conv_transform.h" "agg_trans_bilinear.h" "agg_trans_perspective.h" "agg_span_interpolator_trans.h" "agg_span_interpolator_linear.h" "agg_pixfmt_rgb24.h" "agg_span_image_filter_rgb24.h" "ctrl/agg_rbox_ctrl.h" "platform/agg_platform_support.h" "interactive_polygon.h"

enum { flip_y = true }; agg::rasterizer_scanline_aa<> g_rasterizer; agg::scanline_u8 g_scanline; double g_x1 = 0; double g_y1 = 0; double g_x2 = 0; double g_y2 = 0; class the_application : public agg::platform_support { public: typedef agg::pixfmt_bgr24 pixfmt; typedef agg::renderer_base<pixfmt> renderer_base; typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_solid; agg::interactive_polygon m_triangle;

the_application(agg::pix_format_e format, bool flip_y) : agg::platform_support(format, flip_y), m_triangle(4, 5.0) { } virtual void on_init() { g_x1 = 0.0; g_y1 = 0.0; g_x2 = rbuf_img(0).width(); g_y2 = rbuf_img(0).height(); double dx = width() / 2.0 - (g_x2 - g_x1) / 2.0; double dy = height() / 2.0 - (g_y2 - g_y1) / 2.0; m_triangle.xn(0) = g_x1 + dx; m_triangle.yn(0) = g_y1 + dy; m_triangle.xn(1) = g_x2 + dx; m_triangle.yn(1) = g_y1 + dy; m_triangle.xn(2) = g_x2 + dx; m_triangle.yn(2) = g_y2 + dy; m_triangle.xn(3) = g_x1 + dx; m_triangle.yn(3) = g_y2 + dy; } virtual void on_draw() { // Calculate the 4-th point of the parallelogram m_triangle.xn(3) = m_triangle.xn(0) + (m_triangle.xn(2) - m_triangle.xn(1)); m_triangle.yn(3) = m_triangle.yn(0) + (m_triangle.yn(2) - m_triangle.yn(1)); pixfmt pixf(rbuf_window()); renderer_base rb(pixf); renderer_solid r(rb); rb.clear(agg::rgba(1, 1, 1)); g_rasterizer.clip_box(0, 0, width(), height()); typedef agg::span_allocator<agg::rgba8> span_alloc_type; span_alloc_type sa; agg::trans_perspective tr(m_triangle.polygon(), g_x1, g_y1, g_x2, g_y2); if(tr.is_valid()) { //=================== The trick with interpolator. // ------- Slow variant // span_interpolator_trans is a general purpose interpolator. // It calls the Transformer::transform() for each point of the // scanline, thus, it's slow. But it can be used with any // kind of transformations, linear or non-linear. //---------------------------//typedef agg::span_interpolator_trans<agg::trans_perspective> // interpolator_type;

// // // //

------- Fast variant span_interpolator_linear is an accelerated version of the general purpose one, span_interpolator_trans. It calculates actual coordinates only for the beginning and the ending points

http://www.antigrain.com/tips/image_plg/image_plg.agdoc.html[2. 11. 2010 20:37:14]

Anti-Grain Geometry - Image Parallelogram Transformations // of the span. But the transformations must be linear and parallel, // that is, any straight line must remain straight after applying the // transformation, and any two parallel lines must remain parallel. // It's not sutable for perspective transformations in general // (they are not parallel), but quite OK for this particular case, // i.e., parallelogram transformations. //---------------------------typedef agg::span_interpolator_linear<agg::trans_perspective> interpolator_type; //=================== interpolator_type interpolator(tr); // "hardcoded" bilinear filter //-----------------------------------------typedef agg::span_image_filter_rgb24_bilinear<agg::order_bgr24, interpolator_type> span_gen_type; typedef agg::renderer_scanline_aa<renderer_base, span_gen_type> renderer_type; span_gen_type sg(sa, rbuf_img(0), agg::rgba(1, 1, 1, 0), interpolator); renderer_type ri(rb, sg); g_rasterizer.reset(); g_rasterizer.move_to_d(m_triangle.xn(0), g_rasterizer.line_to_d(m_triangle.xn(1), g_rasterizer.line_to_d(m_triangle.xn(2), g_rasterizer.line_to_d(m_triangle.xn(3), } //-------------------------// Render the "quad" tool and controls g_rasterizer.add_path(m_triangle); r.color(agg::rgba(0, 0.3, 0.5, 0.6)); agg::render_scanlines(g_rasterizer, g_scanline, r); //-------------------------} m_triangle.yn(0)); m_triangle.yn(1)); m_triangle.yn(2)); m_triangle.yn(3));

agg::render_scanlines(g_rasterizer, g_scanline, ri);

virtual void on_mouse_button_down(int x, int y, unsigned flags) { if(flags & agg::mouse_left) { if(m_triangle.on_mouse_button_down(x, y)) { force_redraw(); } } } virtual void on_mouse_move(int x, int y, unsigned flags) { if(flags & agg::mouse_left) { if(m_triangle.on_mouse_move(x, y)) { force_redraw(); } } if((flags & agg::mouse_left) == 0) { on_mouse_button_up(x, y, flags); } } virtual void on_mouse_button_up(int x, int y, unsigned flags) { if(m_triangle.on_mouse_button_up(x, y)) { force_redraw(); } } }; int agg_main(int argc, char* argv[]) { the_application app(agg::pix_format_bgr24, flip_y); app.caption("AGG Example. Image Perspective Transformations"); const char* img_name = "spheres"; if(argc >= 2) img_name = argv[1];

http://www.antigrain.com/tips/image_plg/image_plg.agdoc.html[2. 11. 2010 20:37:14]

Anti-Grain Geometry - Image Parallelogram Transformations if(!app.load_img(0, img_name)) { char buf[256]; if(strcmp(img_name, "spheres") == 0) { sprintf(buf, "File not found: %s%s. Download http://www.antigrain.com/%s%s\n" "or copy it from another directory if available.", img_name, app.img_ext(), img_name, app.img_ext()); } else { sprintf(buf, "File not found: %s%s", img_name, app.img_ext()); } app.message(buf); return 1; } if(app.init(600, 600, agg::window_resize)) { return app.run(); } return 1; }

There is a screenshot:

NOTE The arcticle is actually outdated. Now class trans_affine has methods to calculate an affine matrix that transforms a parellelogram to another one, a rectangle to a parellelogram, and a parellelogram to a rectangle. See agg2/examples/image_perspective.cpp . However, the above material is useful because it helps understand better the AGG concepts.
Copyright 2002-2006 Maxim Shemanarev Web Design and Programming Maxim Shemanarev

http://www.antigrain.com/tips/image_plg/image_plg.agdoc.html[2. 11. 2010 20:37:14]

Anti-Grain Geometry - Image Parallelogram Transformations

http://www.antigrain.com/tips/image_plg/image_plg.agdoc.html[2. 11. 2010 20:37:14]

Anti-Grain Geometry - Using WinAPI to Render Text

Home/ Tips & Tricks/


News Docs Download Mailing List CVS

A simple class to extract True Type glyphs using WinAPI GetGlyphOutline()


There are many questions about drawing of high quality text. AGG provides a class that produces vector text of a fixed typeface (very primitive, ANSI 7-bit character set). I added this class just to have a simple mechanism to draw text in demo examples. It has a propriatory data format and isn't worth further developing. The good news is that you can use any available library or API to extract glyphs and render them with AGG. One can say if the glyph format consists of line segments, conic and cubic bezier curves, it's possible to render it with AGG. All available converters and transformers are applicable, as well as all the renderers. For example, you can draw an outlined text with conv_stroke, or change the font weight (make it bolder or lighter) using conv_contour. You can also render it with gradients or fill the glyphs with images. FreeType. This example It's a long story how to integrate AGG with different font engines, like demonstrates a simplest way to use Windows API to extract the glyphs and to render text with AGG. It calls GetGlyphOutline(), extracts native curved contours, applies conv_curve and renders the text with any available scanline renderers. It works relatively slow, not only because each glyph is being rasterized every time (no cache mechanism), but also because GetGlyphOutline() works terribly slow. More than a half of total time is spent in GetGlyphOutline(). It would be a good solution to cache the glyphs or even prerendered bitmaps; it could speed up rendering vastly, but it's out of this topic. Below is a simple console application that creates agg_test.ppm file (the simplest possible RGB bitmap file). The file isn't natively supported by Microsoft Windows, but there are many viewers and converters that can work with it, for example, IrfanView (www.irfanview.com). Class tt_glyph is not included into AGG because it depends on the Windows API ( #include <windows.h> ), while the main part of AGG is supposed to be fully platform independent. To build the example you need to indicate the AGG include directory and to add to the project files agg_curves.cpp and agg_rasterizer_scanline_aa.cpp. #include #include #include #include #include #include #include #include <stdio.h> <string.h> "agg_pixfmt_rgb24.h" "agg_renderer_base.h" "agg_renderer_scanline.h" "agg_rasterizer_scanline_aa.h" "agg_scanline_p.h" "agg_conv_curve.h"

Using WinAPI to Render Text

#include <windows.h> namespace agg { //================================================================= class tt_glyph { enum { buf_size = 16384-32 }; public: ~tt_glyph(); tt_glyph(); // Set the created font and the "flip_y" flag. //-----------------------void font(HDC dc, HFONT f) { m_dc = dc; m_font = f; } void flip_y(bool flip) { m_flip_y = flip; } bool glyph(unsigned chr, bool hinted = true);

http://www.antigrain.com/tips/win_glyph/win_glyph.agdoc.html[2. 11. 2010 20:37:18]

Anti-Grain Geometry - Using WinAPI to Render Text // The following functions can be called after // and return the respective values of the // GLYPHMETRICS structure. //------------------------int origin_x() const { return m_origin_x; int origin_y() const { return m_origin_y; unsigned width() const { return m_width; unsigned height() const { return m_height; int inc_x() const { return m_inc_x; int inc_y() const { return m_inc_y; // Set the starting point of the Glyph //------------------------void start_point(double x, double y) { m_start_x = x; m_start_y = y; } // Vertex Source Interface //------------------------void rewind(unsigned) { m_cur_vertex = m_vertices; m_cur_flag = m_flags; } unsigned vertex(double* x, double* y) { *x = m_start_x + *m_cur_vertex++; *y = m_start_y + *m_cur_vertex++; return *m_cur_flag++; } private: HDC HFONT char* int8u* double* unsigned const int8u* const double* double double MAT2 int int unsigned unsigned int int bool }; m_dc; m_font; m_gbuf; m_flags; m_vertices; m_max_vertices; m_cur_flag; m_cur_vertex; m_start_x; m_start_y; m_mat2; glyph()

} } } } } }

m_origin_x; m_origin_y; m_width; m_height; m_inc_x; m_inc_y; m_flip_y;

tt_glyph::~tt_glyph() { delete [] m_vertices; delete [] m_flags; delete [] m_gbuf; } tt_glyph::tt_glyph() : m_dc(0), m_font(0), m_gbuf(new char [buf_size]), m_flags(new int8u [256]), m_vertices(new double[512]), m_max_vertices(256), m_cur_flag(m_flags), m_cur_vertex(m_vertices), m_start_x(0.0), m_start_y(0.0), m_flip_y(false) { m_vertices[0] = m_vertices[1] = 0.0; m_flags[0] = path_cmd_stop; memset(&m_mat2, 0, sizeof(m_mat2)); m_mat2.eM11.value = 1; m_mat2.eM22.value = 1; }

static inline double fx_to_dbl(const FIXED& p) { return double(p.value) + double(p.fract) * (1.0 / 65536.0); } static inline FIXED dbl_to_fx(double d) {

http://www.antigrain.com/tips/win_glyph/win_glyph.agdoc.html[2. 11. 2010 20:37:18]

Anti-Grain Geometry - Using WinAPI to Render Text int l; l = long(d * 65536.0); return *(FIXED*)&l; } bool tt_glyph::glyph(unsigned chr, bool hinted) { m_vertices[0] = m_vertices[1] = 0.0; m_flags[0] = path_cmd_stop; rewind(0); if (m_font == 0) return false; #ifndef GGO_UNHINTED #define GGO_UNHINTED 0x0100 #endif // For compatibility with old SDKs.

int unhinted = hinted ? 0 : GGO_UNHINTED; GLYPHMETRICS gm; int total_size = GetGlyphOutline(m_dc, chr, GGO_NATIVE | unhinted, &gm, buf_size, (void*)m_gbuf, &m_mat2); if (total_size < 0) return false; m_origin_x m_origin_y m_width m_height m_inc_x m_inc_y = = = = = = gm.gmptGlyphOrigin.x; gm.gmptGlyphOrigin.y; gm.gmBlackBoxX; gm.gmBlackBoxY; gm.gmCellIncX; gm.gmCellIncY;

if (m_max_vertices <= total_size / sizeof(POINTFX)) { delete [] m_vertices; delete [] m_flags; m_max_vertices = total_size / sizeof(POINTFX) + 256; m_flags = new int8u [m_max_vertices]; m_vertices = new double [m_max_vertices * 2]; } const char* cur_glyph = m_gbuf; const char* end_glyph = m_gbuf + total_size; double* vertex_ptr = m_vertices; int8u* flag_ptr = m_flags; while(cur_glyph < end_glyph) { const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph; const char* end_poly = cur_glyph + th->cb; const char* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER); *vertex_ptr++ = fx_to_dbl(th->pfxStart.x); *vertex_ptr++ = m_flip_y ? -fx_to_dbl(th->pfxStart.y): fx_to_dbl(th->pfxStart.y); *flag_ptr++ = path_cmd_move_to; while(cur_poly < end_poly) { const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly; if (pc->wType == TT_PRIM_LINE) { int i; for (i = 0; i < pc->cpfx; i++) { *vertex_ptr++ = fx_to_dbl(pc->apfx[i].x); *vertex_ptr++ = m_flip_y ? -fx_to_dbl(pc->apfx[i].y): fx_to_dbl(pc->apfx[i].y); *flag_ptr++ = path_cmd_line_to; } } if (pc->wType == TT_PRIM_QSPLINE) { int u; for (u = 0; u < pc->cpfx - 1; u++) spline { POINTFX pnt_b = pc->apfx[u]; point POINTFX pnt_c = pc->apfx[u+1]; if (u < pc->cpfx - 2) compute C { // midpoint (x,y) // If not on last spline, // B is always the current

// Walk through points in

http://www.antigrain.com/tips/win_glyph/win_glyph.agdoc.html[2. 11. 2010 20:37:18]

Anti-Grain Geometry - Using WinAPI to Render Text *(int*)&pnt_c.x = (*(int*)&pnt_b.x + *(int*)&pnt_c.x) / 2; *(int*)&pnt_c.y = (*(int*)&pnt_b.y + *(int*)&pnt_c.y) / 2; } *vertex_ptr++ = fx_to_dbl(pnt_b.x); *vertex_ptr++ = m_flip_y ? -fx_to_dbl(pnt_b.y): fx_to_dbl(pnt_b.y); *flag_ptr++ = path_cmd_curve3; *vertex_ptr++ = fx_to_dbl(pnt_c.x); *vertex_ptr++ = m_flip_y ? -fx_to_dbl(pnt_c.y): fx_to_dbl(pnt_c.y); *flag_ptr++ = path_cmd_curve3; } } cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx; } cur_glyph += th->cb; *vertex_ptr++ = 0.0; *vertex_ptr++ = 0.0; *flag_ptr++ = path_cmd_end_poly | path_flags_close | path_flags_ccw; } *vertex_ptr++ = 0.0; *vertex_ptr++ = 0.0; *flag_ptr++ = path_cmd_stop; return true; } }

enum { frame_width = 320, frame_height = 200 }; // Writing the buffer to a .PPM file, assuming it has // RGB-structure, one byte per color component //-------------------------------------------------bool write_ppm(const unsigned char* buf, unsigned width, unsigned height, const char* file_name) { FILE* fd = fopen(file_name, "wb"); if(fd) { fprintf(fd, "P6 %d %d 255 ", width, height); fwrite(buf, 1, width * height * 3, fd); fclose(fd); return true; } return false; }

template<class Rasterizer, class Renderer, class Scanline, class CharT> void render_text(Rasterizer& ras, Renderer& ren, Scanline& sl, agg::tt_glyph& gl, double x, double y, const CharT* str, bool hinted = true) { // The minimal pipeline is the curve converter. Of course, there // any other transformations are applicapble, conv_stroke<>, for example. // If there are other thransformations, it probably makes sense to // turn off the hints (hinted=false), i.e., to use unhinted glyphs. //-------------------------agg::conv_curve<agg::tt_glyph> curve(gl); while(*str) { gl.start_point(x, y); gl.glyph(*str++, hinted); ras.add_path(curve); agg::render_scanlines(ras, sl, ren); x += gl.inc_x(); y += gl.inc_y(); } }

int main() { // Create the rendering buffer //------------------------

http://www.antigrain.com/tips/win_glyph/win_glyph.agdoc.html[2. 11. 2010 20:37:18]

Anti-Grain Geometry - Using WinAPI to Render Text unsigned char* buffer = new unsigned char[frame_width * frame_height * 3]; agg::rendering_buffer rbuf(buffer, frame_width, frame_height, -frame_width * 3); // Create the renderers, the rasterizer, and the scanline container //-----------------------agg::pixfmt_rgb24 pixf(rbuf); agg::renderer_base<agg::pixfmt_rgb24> rbase(pixf); agg::renderer_scanline_aa_solid<agg::renderer_base<agg::pixfmt_rgb24> > ren(rbase); agg::rasterizer_scanline_aa<> ras; agg::scanline_p8 sl; rbase.clear(agg::rgba8(255, 255, 255)); // Font parameters //-----------------------int fontHeight = 60; // in Pixels in this case int fontWidth = 0; int iAngle = 0; bool bold = true; bool italic = true; const char* typeFace = "Times New Roman"; // I'm not sure how to deal with those sneaky WinGDI functions correctly, // so, please correct me if there's something wrong. // I'm not sure if I need to call ReleaseDC() for the screen. //---------------------------------HFONT font = ::CreateFont(fontHeight, // height of font fontWidth, // average character width iAngle, // angle of escapement iAngle, // base-line orientation angle bold ? 700 : 400, // font weight italic, // italic attribute option FALSE, // underline attribute option FALSE, // strikeout attribute option ANSI_CHARSET, // character set identifier OUT_DEFAULT_PRECIS, // output precision CLIP_DEFAULT_PRECIS, // clipping precision ANTIALIASED_QUALITY, // output quality FF_DONTCARE, // pitch and family typeFace); // typeface name if(font) { HDC dc = ::GetDC(0); if(dc) { HGDIOBJ old_font = ::SelectObject(dc, font); agg::tt_glyph gl; gl.font(dc, font); ren.color(agg::rgba8(0,0,128)); render_text(ras, ren, sl, gl, 10, 100, "Hello, World!"); write_ppm(buffer, frame_width, frame_height, "agg_test.ppm"); ::SelectObject(dc, old_font); ::ReleaseDC(0, dc); } ::DeleteObject(font); } delete [] buffer; return 0; }

enum { frame_width = 320, frame_height = 200 }; // Writing the buffer to a .PPM file, assuming it has // RGB-structure, one byte per color component //-------------------------------------------------bool write_ppm(const unsigned char* buf, unsigned width, unsigned height, const char* file_name) { FILE* fd = fopen(file_name, "wb"); if(fd) { fprintf(fd, "P6 %d %d 255 ", width, height); fwrite(buf, 1, width * height * 3, fd);

http://www.antigrain.com/tips/win_glyph/win_glyph.agdoc.html[2. 11. 2010 20:37:18]

Anti-Grain Geometry - Using WinAPI to Render Text fclose(fd); return true; } return false; }

template<class Rasterizer, class Renderer, class Scanline, class CharT> void render_text(Rasterizer& ras, Renderer& ren, Scanline& sl, agg::tt_glyph& gl, double x, double y, const CharT* str, bool hinted = true) { // The minimal pipeline is the curve converter. Of course, there // any other transformations are applicapble, conv_stroke<>, for example. // If there are other thransformations, it probably makes sense to // turn off the hints (hinted=false), i.e., to use unhinted glyphs. //-------------------------agg::conv_curve<agg::tt_glyph> curve(gl); while(*str) { gl.start_point(x, y); gl.glyph(*str++, hinted); ras.add_path(curve); agg::render_scanlines(ras, sl, ren); x += gl.inc_x(); y += gl.inc_y(); } }

int main() { // Create the rendering buffer //-----------------------unsigned char* buffer = new unsigned char[frame_width * frame_height * 3]; agg::rendering_buffer rbuf(buffer, frame_width, frame_height, -frame_width * 3); // Create the renderers, the rasterizer, and the scanline container //-----------------------agg::pixfmt_rgb24 pixf(rbuf); agg::renderer_base<agg::pixfmt_rgb24> rbase(pixf); agg::renderer_scanline_aa_solid<agg::renderer_base<agg::pixfmt_rgb24> > ren(rbase); agg::rasterizer_scanline_aa<> ras; agg::scanline_p8 sl; rbase.clear(agg::rgba8(255, 255, 255)); // Font parameters //-----------------------int fontHeight = 60; // in Pixels in this case int fontWidth = 0; int iAngle = 0; bool bold = true; bool italic = true; const char* typeFace = "Times New Roman"; // I'm not sure how to deal with those sneaky WinGDI functions correctly, // so, please correct me if there's something wrong. // I'm not sure if I need to call ReleaseDC() for the screen. //---------------------------------HFONT font = ::CreateFont(fontHeight, // height of font fontWidth, // average character width iAngle, // angle of escapement iAngle, // base-line orientation angle bold ? 700 : 400, // font weight italic, // italic attribute option FALSE, // underline attribute option FALSE, // strikeout attribute option ANSI_CHARSET, // character set identifier OUT_DEFAULT_PRECIS, // output precision CLIP_DEFAULT_PRECIS, // clipping precision ANTIALIASED_QUALITY, // output quality FF_DONTCARE, // pitch and family typeFace); // typeface name if(font) { HDC dc = ::GetDC(0); if(dc) { HGDIOBJ old_font = ::SelectObject(dc, font); agg::tt_glyph gl; gl.font(dc, font); ren.color(agg::rgba8(0,0,128)); render_text(ras, ren, sl, gl, 10, 100, "Hello, World!");

http://www.antigrain.com/tips/win_glyph/win_glyph.agdoc.html[2. 11. 2010 20:37:18]

Anti-Grain Geometry - Using WinAPI to Render Text write_ppm(buffer, frame_width, frame_height, "agg_test.ppm"); ::SelectObject(dc, old_font); ::ReleaseDC(0, dc); } ::DeleteObject(font); } delete [] buffer; return 0; } And the result:

Beautiful, isn't it? In fact, it looks much better than the native text rendered in Windows. To compare the quality, run WordPad, type Hello, World! and change font to Times New Roman, set Bold, Italic and size of 39 points. Here's an enlarged fragment of a glyph to compare the quality.

Copyright 2002-2006 Maxim Shemanarev Web Design and Programming Maxim Shemanarev

http://www.antigrain.com/tips/win_glyph/win_glyph.agdoc.html[2. 11. 2010 20:37:18]

Anti-Grain Geometry - Using WinAPI to Render Text

http://www.antigrain.com/tips/win_glyph/win_glyph.agdoc.html[2. 11. 2010 20:37:18]

Anti-Grain Geometry - Compiling AGG under Microsoft eMbedded VC 4.0

Home/ Tips & Tricks/


News Docs Download Mailing List CVS

Compiling AGG under Microsoft eMbedded VC 4.0


A simple step-by-step tutorial
If you have a Microsoft eMbedded Visual C++ 4.0 (eVC4.0) installed you can develop applications based on AGG for PocketPCs that use Windows CE. I didn't have any problems compiling AGG under eVC4.0, it looks pretty much the same as building of Win32 API applications. Below there is a spep-by-step instruction how to create a simple WinCE application with AGG. 1. Create a new WCE Application , for example in agg2/examples/win32_ce/agg_test . Choose "A typical Hello World Application". 2. Add path to the AGG include directory: Project/Settings, set "Setting For:" to "All configurations". Select tab "C/C++", category "Preprocessor". Type path to the AGG include directory in the "Additional Include Directories". If you put the project to agg2/examples/win32_ce/agg_test , type ../../../include 3. Add necessary AGG source files to the project from agg2/src . You can actually add all of them (except the ones in the sub-directories). 4. Select all the AGG source files in the project ( agg_*.cpp , except agg_test.* ). 5. Go to Project/Settings, set "Setting For:" to "All configurations". Select tab "C/C++", category "Precompiled Headers". Choose option "Not using precompiled headers". 6. Replace the content of agg_test.cpp to the following (see the source in agg_test.cpp). Basically you need to replace the WM_PAINT event handler, add the AGG includes, and add the agg_draw() function. // agg_test.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "agg_test.h" #include <commctrl.h> #include #include #include #include #include #include #include "agg_rendering_buffer.h" "agg_curves.h" "agg_conv_stroke.h" "agg_rasterizer_scanline_aa.h" "agg_scanline_p.h" "agg_renderer_scanline.h" "agg_pixfmt_rgb555.h"

#define MAX_LOADSTRING 100 // Global Variables: HINSTANCE hInst; HWND hwndCB; // The current instance // The command bar handle

// Forward declarations of functions included in this code module: ATOM MyRegisterClass (HINSTANCE, LPTSTR); BOOL InitInstance (HINSTANCE, int); LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About (HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; HACCEL hAccelTable; // Perform application initialization:

http://www.antigrain.com/tips/win_ce/win_ce.agdoc.html[2. 11. 2010 20:37:22]

Anti-Grain Geometry - Compiling AGG under Microsoft eMbedded VC 4.0 if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_AGG_TEST); // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } // // FUNCTION: MyRegisterClass() // // PURPOSE: Registers the window class. // // COMMENTS: // // It is important to call this function so that the application // will get 'well formed' small icons associated with it. // ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass) { WNDCLASS wc; wc.style wc.lpfnWndProc wc.cbClsExtra wc.cbWndExtra wc.hInstance wc.hIcon wc.hCursor wc.hbrBackground wc.lpszMenuName wc.lpszClassName } // // FUNCTION: InitInstance(HANDLE, int) // // PURPOSE: Saves instance handle and creates main window // // COMMENTS: // // In this function, we save the instance handle in a global variable and // create and display the main program window. // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // The window class name hInst = hInstance; // Store instance handle in our global variable // Initialize global strings LoadString(hInstance, IDC_AGG_TEST, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance, szWindowClass); LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); if (hwndCB) CommandBar_Show(hwndCB, TRUE); return TRUE; } = = = = = = = = = = CS_HREDRAW | CS_VREDRAW; (WNDPROC) WndProc; 0; 0; hInstance; LoadIcon(hInstance, MAKEINTRESOURCE(IDI_AGG_TEST)); 0; (HBRUSH) GetStockObject(WHITE_BRUSH); 0; szWindowClass;

return RegisterClass(&wc);

void agg_draw(unsigned char* buf, unsigned w, unsigned h, int stride) { typedef agg::pixfmt_rgb555 pixfmt; //============================================================ // AGG lowest level code. agg::rendering_buffer rbuf;

http://www.antigrain.com/tips/win_ce/win_ce.agdoc.html[2. 11. 2010 20:37:22]

Anti-Grain Geometry - Compiling AGG under Microsoft eMbedded VC 4.0 rbuf.attach((unsigned char*)buf, w, h, stride); // Pixel format and basic primitives renderer pixfmt pixf(rbuf); agg::renderer_base<pixfmt> renb(pixf); renb.clear(agg::rgba8(255, 255, 255, 255)); // Scanline renderer for solid filling. agg::renderer_scanline_aa_solid<agg::renderer_base<pixfmt> > ren(renb); // Rasterizer & scanline agg::rasterizer_scanline_aa<> ras; agg::scanline_p8 sl; agg::curve4 curve; agg::conv_stroke<agg::curve4> poly(curve); unsigned i; srand(12365); for(i = 0; i < 100; i++) { poly.width(double(rand() % 3500 + 500) / 500.0); curve.init(rand() % w, rand() % h, rand() % w, rand() % h, rand() % w, rand() % h, rand() % w, rand() % h); ren.color(agg::rgba8(rand() & 0xFF, rand() & 0xFF, rand() & 0xFF, rand() & 0xFF)); ras.add_path(poly, 0); agg::render_scanlines(ras, sl, ren); } //============================================================ }

// // FUNCTION: WndProc(HWND, unsigned, WORD, LONG) // // PURPOSE: Processes messages for the main window. // // WM_COMMAND - process the application menu // WM_PAINT - Paint the main window // WM_DESTROY - post a quit message and return // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; int wmId, wmEvent; PAINTSTRUCT ps; // TCHAR szHello[MAX_LOADSTRING]; switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId) { case IDM_HELP_ABOUT: DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break; case IDM_FILE_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_CREATE: hwndCB = CommandBar_Create(hInst, hWnd, 1); CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0); CommandBar_AddAdornments(hwndCB, 0, 0); break; case WM_PAINT: { hdc = BeginPaint(hWnd, &ps); RECT rt; GetClientRect(hWnd, &rt); int width = rt.right - rt.left; int height = rt.bottom - rt.top;

http://www.antigrain.com/tips/win_ce/win_ce.agdoc.html[2. 11. 2010 20:37:22]

Anti-Grain Geometry - Compiling AGG under Microsoft eMbedded VC 4.0 //============================================================ //Creating compatible DC and a bitmap to render the image BITMAPINFO bmp_info; bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmp_info.bmiHeader.biWidth = width; bmp_info.bmiHeader.biHeight = height; bmp_info.bmiHeader.biPlanes = 1; bmp_info.bmiHeader.biBitCount = 16; bmp_info.bmiHeader.biCompression = BI_RGB; bmp_info.bmiHeader.biSizeImage = 0; bmp_info.bmiHeader.biXPelsPerMeter = 0; bmp_info.bmiHeader.biYPelsPerMeter = 0; bmp_info.bmiHeader.biClrUsed = 0; bmp_info.bmiHeader.biClrImportant = 0; HDC mem_dc = ::CreateCompatibleDC(hdc); void* buf = 0; HBITMAP bmp = ::CreateDIBSection( mem_dc, &bmp_info, DIB_RGB_COLORS, &buf, 0, 0 ); // Selecting the object before doing anything allows you // to use AGG together with native Windows GDI. HBITMAP temp = (HBITMAP)::SelectObject(mem_dc, bmp); // Calculate the aligned stride value for the 16-bit BMP. int stride = ((width * 2 + 3) >> 2) << 2; // Negate the stride value to have the Y-axis flipped agg_draw((unsigned char*)buf, width, height, -stride); //-----------------------------------------------------------// Display the image. If the image is B-G-R-A (32-bits per pixel) // one can use AlphaBlend instead of BitBlt. In case of AlphaBlend // one also should clear the image with zero alpha, i.e. rgba8(0,0,0,0) ::BitBlt( hdc, rt.left, rt.top, width, height, mem_dc, 0, 0, SRCCOPY ); // Free resources ::SelectObject(mem_dc, temp); ::DeleteObject(bmp); ::DeleteObject(mem_dc); EndPaint(hWnd, &ps); } break; case WM_DESTROY: CommandBar_Destroy(hwndCB); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // Mesage handler for the About box. LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { RECT rt, rt1; int DlgWidth, DlgHeight; // dialog width and height in pixel units int NewPosX, NewPosY; switch (message) { case WM_INITDIALOG: // trying to center the About dialog if (GetWindowRect(hDlg, &rt1)) { GetClientRect(GetParent(hDlg), &rt); DlgWidth = rt1.right - rt1.left; DlgHeight = rt1.bottom - rt1.top ; NewPosX = (rt.right - rt.left - DlgWidth)/2; NewPosY = (rt.bottom - rt.top - DlgHeight)/2; // if the About box is larger than the physical screen if (NewPosX < 0) NewPosX = 0; if (NewPosY < 0) NewPosY = 0; SetWindowPos(hDlg, 0, NewPosX, NewPosY, 0, 0, SWP_NOZORDER | SWP_NOSIZE);

http://www.antigrain.com/tips/win_ce/win_ce.agdoc.html[2. 11. 2010 20:37:22]

Anti-Grain Geometry - Compiling AGG under Microsoft eMbedded VC 4.0 } return TRUE; case WM_COMMAND: if ((LOWORD(wParam) == IDOK) || (LOWORD(wParam) == IDCANCEL)) { EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; } Download the whole project: (agg_test.zip). It's supposed to be placed in

agg2/examples/win32_ce/agg_test/. Create win32_ce and agg_test directories. If you


choose another directory, modify the Additional Include Directories path, and you will have to remove all the AGG source files from the project and add new ones from the actual AGG directory (don't forget to turn Precompiled Headers off). NOTE I'm not a pro in WinCE and embedded systems, but I presumed that there's an RGB555 pixel format is used. If it's not so, use another pixel format that fits the native display format. Change function agg_draw() and try other AGG stuff. Also note, that the whole scene is being drawn from scratch every time the WM_PAINT event comes. It's a good idea to cache the rendered image and just BitBlt() it if there were no changes. Here's a screenshot (click to see the enlarged version)

Copyright 2002-2006 Maxim Shemanarev Web Design and Programming Maxim Shemanarev

http://www.antigrain.com/tips/win_ce/win_ce.agdoc.html[2. 11. 2010 20:37:22]

Anti-Grain Geometry - Compiling AGG under Microsoft eMbedded VC 4.0

http://www.antigrain.com/tips/win_ce/win_ce.agdoc.html[2. 11. 2010 20:37:22]

Anti-Grain Geometry - Working with Gradients

Home/ Tips & Tricks/


News Docs Download Mailing List CVS

Working with Gradients


A Simple Step-by-Step Tutorial
This article will explain to you how to set up gradients and render them. We will use a simple command-line example that produces the result in the agg_test.ppm file. You can use, for example IrfanView (www.irfanview.com) to see the results. You will need to tell the compiler the AGG include directory and add three source files to the project or to the command line: agg_rasterizer_scanline_aa.cpp , agg_trans_affine.cpp , and agg_sqrt_tables.cpp. You can find the source file here: (gradients.cpp). #include #include #include #include #include #include #include #include #include #include <stdio.h> <string.h> "agg_pixfmt_rgb.h" "agg_renderer_base.h" "agg_renderer_scanline.h" "agg_scanline_u.h" "agg_rasterizer_scanline_aa.h" "agg_ellipse.h" "agg_span_gradient.h" "agg_span_interpolator_linear.h"

enum { frame_width = 320, frame_height = 200 }; // Writing the buffer to a .PPM file, assuming it has // RGB-structure, one byte per color component //-------------------------------------------------bool write_ppm(const unsigned char* buf, unsigned width, unsigned height, const char* file_name) { FILE* fd = fopen(file_name, "wb"); if(fd) { fprintf(fd, "P6 %d %d 255 ", width, height); fwrite(buf, 1, width * height * 3, fd); fclose(fd); return true; } return false; }

// A simple function to form the gradient color array // consisting of 3 colors, "begin", "middle", "end" //--------------------------------------------------template<class Array> void fill_color_array(Array& array, agg::rgba8 begin, agg::rgba8 middle, agg::rgba8 end) { unsigned i; unsigned half_size = array.size() / 2; for(i = 0; i < half_size; ++i) { array[i] = begin.gradient(middle, i / double(half_size)); } for(; i < array.size(); ++i) { array[i] = middle.gradient(end, (i - half_size) / double(half_size)); } }

int main() { unsigned char* buffer = new unsigned char[frame_width * frame_height * 3];

http://www.antigrain.com/tips/gradients_tutorial/gradients_tutorial.agdoc.html[2. 11. 2010 20:37:28]

Anti-Grain Geometry - Working with Gradients agg::rendering_buffer rbuf(buffer, frame_width, frame_height, -frame_width * 3); // Pixel format and basic renderers. //----------------typedef agg::pixfmt_rgb24 pixfmt_type; typedef agg::renderer_base<pixfmt_type> renderer_base_type; // The gradient color array typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; // Gradient shape function (linear, radial, custom, etc) //----------------typedef agg::gradient_x gradient_func_type; // Span interpolator. This object is used in all span generators // that operate with transformations during iterating of the spans, // for example, image transformers use the interpolator too. //----------------typedef agg::span_interpolator_linear<> interpolator_type; // Span allocator is an object that allocates memory for // the array of colors that will be used to render the // color spans. One object can be shared between different // span generators. //----------------typedef agg::span_allocator<agg::rgba8> span_allocator_type; // Finally, the gradient span generator working with the agg::rgba8 // color type. // The 4-th argument is the color function that should have // the [] operator returning the color in range of [0...255]. // In our case it will be a simple look-up table of 256 colors. //----------------typedef agg::span_gradient<agg::rgba8, interpolator_type, gradient_func_type, color_array_type, span_allocator_type> span_gradient_type; // The gradient scanline renderer type //----------------typedef agg::renderer_scanline_aa<renderer_base_type, span_gradient_type> renderer_gradient_type; // Common declarations (pixel format and basic renderer). //---------------pixfmt_type pixf(rbuf); renderer_base_type rbase(pixf); // The gradient objects declarations //---------------gradient_func_type gradient_func; // The gradient function agg::trans_affine gradient_mtx; // Affine transformer interpolator_type span_interpolator(gradient_mtx); // Span interpolator span_allocator_type span_allocator; // Span Allocator color_array_type color_array; // Gradient colors // Declare the gradient span itself. // The last two arguments are so called "d1" and "d2" // defining two distances in pixels, where the gradient starts // and where it ends. The actual meaning of "d1" and "d2" depands // on the gradient function. //---------------span_gradient_type span_gradient(span_allocator, span_interpolator, gradient_func, color_array, 0, 100); // The gradient renderer //---------------renderer_gradient_type ren_gradient(rbase, span_gradient); // The rasterizing/scanline stuff //---------------agg::rasterizer_scanline_aa<> ras; agg::scanline_u8 sl; // Finally we can draw a circle. //---------------rbase.clear(agg::rgba8(255, 255, 255)); fill_color_array(color_array,

http://www.antigrain.com/tips/gradients_tutorial/gradients_tutorial.agdoc.html[2. 11. 2010 20:37:28]

Anti-Grain Geometry - Working with Gradients agg::rgba8(0,50,50), agg::rgba8(240, 255, 100), agg::rgba8(80, 0, 0)); agg::ellipse ell(50, 50, 50, 50, 100); ras.add_path(ell); agg::render_scanlines(ras, sl, ren_gradient); write_ppm(buffer, frame_width, frame_height, "agg_test.ppm"); delete [] buffer; return 0; } It looks rather complex, especially the necessity to declare a lot of types and objects. But the complexity gives you freedom, for example, you can define your own gradient functions or even arbitrary distortions. The example renders a circle with linear gradient from (0,0) to (100,0). In AGG you can define an arbitrary color function, in our case it's a simple look-up table generated from three colors, start , middle , and end . Here is the result (the axes and text were added in Xara X):

It also can seem like an overkill for this simple task, but later you will see that it's not so.

The next step is one little modification. Modify the following: // Declare the gradient span itself. // The last two arguments are so called "d1" and "d2" // defining two distances in pixels, where the gradient starts // and where it ends. The actual meaning of "d1" and "d2" depands // on the gradient function. //---------------span_gradient_type span_gradient(span_allocator, span_interpolator, gradient_func, color_array, 50, 100); The result:

http://www.antigrain.com/tips/gradients_tutorial/gradients_tutorial.agdoc.html[2. 11. 2010 20:37:28]

Anti-Grain Geometry - Working with Gradients

It should explain those freaky d1 and d2 arguments. In fact, they determine the geometrical start and end of the gradient and their meaning depends on the gradient function.

Now change the gradient function: // Gradient shape function (linear, radial, custom, etc) //----------------typedef agg::gradient_circle gradient_func_type; Set d1 back to 0: // Declare the gradient span itself. // The last two arguments are so called "d1" and "d2" // defining two distances in pixels, where the gradient starts // and where it ends. The actual meaning of "d1" and "d2" depands // on the gradient function. //---------------span_gradient_type span_gradient(span_allocator, span_interpolator, gradient_func, color_array, 0, 100); And modify the circle: agg::ellipse ell(0, 0, 120, 120, 100); The result:

Modify d1 again: // // // // Declare the gradient span itself. The last two arguments are so called "d1" and "d2" defining two distances in pixels, where the gradient starts and where it ends. The actual meaning of "d1" and "d2" depands

http://www.antigrain.com/tips/gradients_tutorial/gradients_tutorial.agdoc.html[2. 11. 2010 20:37:28]

Anti-Grain Geometry - Working with Gradients // on the gradient function. //---------------span_gradient_type span_gradient(span_allocator, span_interpolator, gradient_func, color_array, 50, 100);

So that, in case of a radial gradient, d1 and d2 define the starting and ending radii.

By default the origin point for the gradients is (0,0). How to draw a gradient in some other place? The answer is to use affine transformations. Strictly speaking, the transformations are fully defined by the span interpolator. In our case we use span_interpolator_linear with an affine matrix. The linear interpolator allows you to speed up the calculations vastly, because we calculate the floating point coordinates only in the begin and end of the horizontal spans and then use a fast, integer, Bresenhamlike interpolation with Subpixel Accuracy. Add the following code somewhere before calling agg::render_scanlines(ras,

sl,

ren_gradient);
gradient_mtx *= agg::trans_affine_scaling(0.75, 1.2); gradient_mtx *= agg::trans_affine_rotation(-agg::pi/3.0); gradient_mtx *= agg::trans_affine_translation(100.0, 100.0); gradient_mtx.invert(); And modify the circle: agg::ellipse ell(100, 100, 120, 120, 100);

the affine matrix should be obvious except for some strange It's necessary because the gradient generator uses reverse transformations instead of direct ones. In other words it takes the destination point, applies the transformations and obtains the coordinates in the gradient. Note that the affine transformations allow you to turn a circular gradient into elliptical.

The

gradient_mtx.invert() .

code

of

initializing

of

http://www.antigrain.com/tips/gradients_tutorial/gradients_tutorial.agdoc.html[2. 11. 2010 20:37:28]

Anti-Grain Geometry - Working with Gradients Now it should be obvious how to define a linear gradient from some Point1 to Point2 . So, get back to the original code and add the following function: // Calculate the affine transformation matrix for the linear gradient // from (x1, y1) to (x2, y2). gradient_d2 is the "base" to scale the // gradient. Here d1 must be 0.0, and d2 must equal gradient_d2. //--------------------------------------------------------------void calc_linear_gradient_transform(double x1, double y1, double x2, double y2, agg::trans_affine& mtx, double gradient_d2 = 100.0) { double dx = x2 - x1; double dy = y2 - y1; mtx.reset(); mtx *= agg::trans_affine_scaling(sqrt(dx * dx + dy * dy) / gradient_d2); mtx *= agg::trans_affine_rotation(atan2(dy, dx)); mtx *= agg::trans_affine_translation(x1, y1); mtx.invert(); } Then modify the circle: agg::ellipse ell(100, 100, 80, 80, 100); And add the transformations: calc_linear_gradient_transform(50, 50, 150, 150, gradient_mtx);

Try to play with different parameters, transformations, and gradient functions: gradient_circle, gradient_x, gradient_y, gradient_diamond, gradient_xy, gradient_sqrt_xy, gradient_conic. Also look at the gradient functions and try to write your own. Actually, the set of the gradient functions in AGG is rather poor, it just demonstrates the possibilities. For example, repeating or reflecting gradients should be implemented in gradient functions (or you can write adaptors that will use the existing functions).
Copyright 2002-2006 Maxim Shemanarev Web Design and Programming Maxim Shemanarev

http://www.antigrain.com/tips/gradients_tutorial/gradients_tutorial.agdoc.html[2. 11. 2010 20:37:28]

Anti-Grain Geometry - Working with Gradients

http://www.antigrain.com/tips/gradients_tutorial/gradients_tutorial.agdoc.html[2. 11. 2010 20:37:28]

Anti-Grain Geometry - The Problem of Line Alignment

Home/ Tips & Tricks/


News Docs Download Mailing List CVS

The Problem of Line Alignment


The "half-a-pixel" problem when rendering lines
Anti-Aliasing is a tricky thing. If you decided you like AGG and it finally solves all your problems in 2D graphics, it's a mistake. Nothing of the kind. The more you worry about the quality the more problems there are exposed. Let us start with a simple rectangle.

Here we have a rectangle with exact integral coordinates (1,1,4,4). Everything looks fine, but to understand and see how the Anti-Aliasing and Subpixel Accuracy work let's shift it to 0.5 pixel by X and Y:

The pixels have intensities proportional to the area of the pixel covered by the rectangle. In practice it means that the rectangle looks blur. It's not a caprice, it's a necessity because only in this case we can preserve the visual area covered by the rectangle the same, regardless of its subpixel position. The initial rectangle covers 9 pixels. If we just round off the coordinates, the resulting rectangle can be drawn as 4 pixels and it can be drawn as 16 pixels, depending on the position and the rounding rules. So that, the blurness is much less evil than "jitter" because it allows you to keep the image much more consistent.

http://www.antigrain.com/tips/line_alignment/line_alignment.agdoc.html[2. 11. 2010 20:37:32]

Anti-Grain Geometry - The Problem of Line Alignment Now let's try to calculate an outline of one pixel width around this square:

This is an ideal case. In prcatice we cannot draw anything between pixels, so the result will look even more blur:

There are no fully covered pixels at all and this fact creates the problem of line alignment. Bad news is that there's no ideal solution of it, we'll have to sacrifice something. The good news is there are several partial solutions that can be satisfactory. First, let's try to add 0.5 to the coordinates of the outline. Remember, if we add 0.5 to the filled rectangle too, the ones without outlines will look blur (see above).

http://www.antigrain.com/tips/line_alignment/line_alignment.agdoc.html[2. 11. 2010 20:37:32]

Anti-Grain Geometry - The Problem of Line Alignment Looks perfect while the outline is 100% opaque. If we have a translucent boundary it will look worse:

The translucency can be implicit, for example, if we draw a line of 0.5 pixel width, it's simulated with translucency! It will look better if we shift both, the fill and its outline.

But remember, it will look worse if it's not outlined. Still, The first solution is to shift everything to 0.5 pixel, which can be appropriate when you have outlines in all cases. The second solution is to shift only outlines, keeping the filled polygons as they are. In this case you must be sure you always have the outline of at least 1 pixel width. That's not a good restriction. You can do even better, shifting only those polygons that have an outline (stroke). But in this case you can have some inconsistency between polygons with and without strokes. The shifting transformer is very simple: namespace agg { class trans_shift { public: trans_shift() : m_shift(0.0) {} trans_shift(double s) : m_shift(s) {} void shift(double s) { m_shift = s; } double shift() const { return m_shift; } void transform(double* x, double* y) const { *x += m_shift; *y += m_shift; } private: double m_shift; }; } And its use is simple too:

http://www.antigrain.com/tips/line_alignment/line_alignment.agdoc.html[2. 11. 2010 20:37:32]

Anti-Grain Geometry - The Problem of Line Alignment agg::trans_shift ts(0.5); agg::conv_transform<source_class, agg::trans_shift> shift(source, ts); That is, it's included into the pipeline as yet another transformer. If you use the affine transformer (most probably you will), you can do without this additional converter. Just add the following, after the matrix is formed: mtx *= agg::trans_affine_translate(0.5, 0.5); In this case there will be no additional performance fee. Of course, you will have to worry about when and where to add this shift (see cases above). There is one more solution and it can be even better. Nobody says that we need to apply the same shift to all coordinates. In case of our rectangle there can be inner or outer outline:

You can achive this with using conv_contour, see also Demo conv_contour.cpp. But there are some problems too. First of all, the insideness becomes important, while conv_stroke doesn't care about it. So that, you should preserve or detect the orientation of the contours, not to mention that self-intersecting polygons don't have a univocal orientation, they can have only a prevaling orientation. The second problem is where to apply this transformation. It should be definitely done before stroke converter. But there is a contradiction with the succeeding affine transformations. Take the zoom operation, for example. If you want the line widths to be consistent with the transformations, you have to use the affine transformer after the outline is calculated. If it's done before, you can change the stroke width respectively, but in this case you breake the integrity if you have different scaling coefficients by X and Y.

http://www.antigrain.com/tips/line_alignment/line_alignment.agdoc.html[2. 11. 2010 20:37:32]

Anti-Grain Geometry - The Problem of Line Alignment

If you are absolutely sure you will never use different scaling coefficients by X and Y, you can transform paths before calculating the outline. In other words, the sequence of the conversions is important and it can be contadictive. Besides, you have to decide if you only need to correct the 0.5 problem or to have the true inner or outer stroke. The conclusion is that there's no ideal solution. But the whole idea of Anti-Grain Geometry is that it's you who chooses the neccessary pipelines and solve your problem-orinted tasks. Thare are no other APIs that allow you to have this flexibility.
Copyright 2002-2006 Maxim Shemanarev Web Design and Programming Maxim Shemanarev

http://www.antigrain.com/tips/line_alignment/line_alignment.agdoc.html[2. 11. 2010 20:37:32]

Anti-Grain Geometry - The Problem of Line Alignment

http://www.antigrain.com/tips/line_alignment/line_alignment.agdoc.html[2. 11. 2010 20:37:32]

http://www.antigrain.com/tips/win_ce/screenshot.png[2. 11. 2010 20:37:38]