#include <math.h> #include <stdio.h> #include "agg_basics.h" #include "agg_rendering_buffer.h" #include "agg_rasterizer_scanline_aa.h" #include "agg_conv_curve.h" #include "agg_conv_contour.h" #include "agg_conv_stroke.h" #include "agg_scanline_p.h" #include "agg_renderer_scanline.h" #include "agg_pixfmt_rgb.h" #include "agg_pixfmt_rgba.h" #include "agg_pixfmt_gray.h" #include "agg_bounding_rect.h" #include "agg_trans_perspective.h" #include "agg_blur.h" #include "ctrl/agg_slider_ctrl.h" #include "ctrl/agg_rbox_ctrl.h" #include "ctrl/agg_cbox_ctrl.h" #include "ctrl/agg_polygon_ctrl.h" #include "platform/agg_platform_support.h" enum flip_y_e { flip_y = true }; class the_application : public agg::platform_support { agg::rbox_ctrl<agg::rgba8> m_method; agg::slider_ctrl<agg::rgba8> m_radius; agg::polygon_ctrl<agg::rgba8> m_shadow_ctrl; agg::cbox_ctrl<agg::rgba8> m_channel_r; agg::cbox_ctrl<agg::rgba8> m_channel_g; agg::cbox_ctrl<agg::rgba8> m_channel_b; agg::path_storage m_path; typedef agg::conv_curve<agg::path_storage> shape_type; shape_type m_shape; agg::rasterizer_scanline_aa<> m_ras; agg::scanline_p8 m_sl; agg::rendering_buffer m_rbuf2; agg::stack_blur <agg::rgba8, agg::stack_blur_calc_rgb<> > m_stack_blur; agg::recursive_blur<agg::rgba8, agg::recursive_blur_calc_rgb<> > m_recursive_blur; agg::rect_d m_shape_bounds; public: the_application(agg::pix_format_e format, bool flip_y) : agg::platform_support(format, flip_y), m_method (10.0, 10.0, 130.0, 70.0, !flip_y), m_radius (130 + 10.0, 10.0 + 4.0, 130 + 300.0, 10.0 + 8.0 + 4.0, !flip_y), m_shadow_ctrl(4), m_channel_r (10.0, 80.0, "Red", !flip_y), m_channel_g (10.0, 95.0, "Green", !flip_y), m_channel_b (10.0, 110.0, "Blue", !flip_y), m_shape(m_path) { add_ctrl(m_method); m_method.text_size(8); m_method.add_item("Stack Blur"); m_method.add_item("Recursive Blur"); m_method.add_item("Channels"); m_method.cur_item(0); add_ctrl(m_radius); m_radius.range(0.0, 40.0); m_radius.value(15.0); m_radius.label("Blur Radius=%1.2f"); add_ctrl(m_shadow_ctrl); add_ctrl(m_channel_r); add_ctrl(m_channel_g); add_ctrl(m_channel_b); m_channel_g.status(true); m_path.remove_all(); m_path.move_to(28.47, 6.45); m_path.curve3(21.58, 1.12, 19.82, 0.29); m_path.curve3(17.19, -0.93, 14.21, -0.93); m_path.curve3(9.57, -0.93, 6.57, 2.25); m_path.curve3(3.56, 5.42, 3.56, 10.60); m_path.curve3(3.56, 13.87, 5.03, 16.26); m_path.curve3(7.03, 19.58, 11.99, 22.51); m_path.curve3(16.94, 25.44, 28.47, 29.64); m_path.line_to(28.47, 31.40); m_path.curve3(28.47, 38.09, 26.34, 40.58); m_path.curve3(24.22, 43.07, 20.17, 43.07); m_path.curve3(17.09, 43.07, 15.28, 41.41); m_path.curve3(13.43, 39.75, 13.43, 37.60); m_path.line_to(13.53, 34.77); m_path.curve3(13.53, 32.52, 12.38, 31.30); m_path.curve3(11.23, 30.08, 9.38, 30.08); m_path.curve3(7.57, 30.08, 6.42, 31.35); m_path.curve3(5.27, 32.62, 5.27, 34.81); m_path.curve3(5.27, 39.01, 9.57, 42.53); m_path.curve3(13.87, 46.04, 21.63, 46.04); m_path.curve3(27.59, 46.04, 31.40, 44.04); m_path.curve3(34.28, 42.53, 35.64, 39.31); m_path.curve3(36.52, 37.21, 36.52, 30.71); m_path.line_to(36.52, 15.53); m_path.curve3(36.52, 9.13, 36.77, 7.69); m_path.curve3(37.01, 6.25, 37.57, 5.76); m_path.curve3(38.13, 5.27, 38.87, 5.27); m_path.curve3(39.65, 5.27, 40.23, 5.62); m_path.curve3(41.26, 6.25, 44.19, 9.18); m_path.line_to(44.19, 6.45); m_path.curve3(38.72, -0.88, 33.74, -0.88); m_path.curve3(31.35, -0.88, 29.93, 0.78); m_path.curve3(28.52, 2.44, 28.47, 6.45); m_path.close_polygon(); m_path.move_to(28.47, 9.62); m_path.line_to(28.47, 26.66); m_path.curve3(21.09, 23.73, 18.95, 22.51); m_path.curve3(15.09, 20.36, 13.43, 18.02); m_path.curve3(11.77, 15.67, 11.77, 12.89); m_path.curve3(11.77, 9.38, 13.87, 7.06); m_path.curve3(15.97, 4.74, 18.70, 4.74); m_path.curve3(22.41, 4.74, 28.47, 9.62); m_path.close_polygon(); agg::trans_affine shape_mtx; shape_mtx *= agg::trans_affine_scaling(4.0); shape_mtx *= agg::trans_affine_translation(150, 100); m_path.transform(shape_mtx); agg::bounding_rect_single(m_shape, 0, &m_shape_bounds.x1, &m_shape_bounds.y1, &m_shape_bounds.x2, &m_shape_bounds.y2); m_shadow_ctrl.xn(0) = m_shape_bounds.x1; m_shadow_ctrl.yn(0) = m_shape_bounds.y1; m_shadow_ctrl.xn(1) = m_shape_bounds.x2; m_shadow_ctrl.yn(1) = m_shape_bounds.y1; m_shadow_ctrl.xn(2) = m_shape_bounds.x2; m_shadow_ctrl.yn(2) = m_shape_bounds.y2; m_shadow_ctrl.xn(3) = m_shape_bounds.x1; m_shadow_ctrl.yn(3) = m_shape_bounds.y2; m_shadow_ctrl.line_color(agg::rgba(0, 0.3, 0.5, 0.3)); } virtual void on_draw() { typedef agg::renderer_base<agg::pixfmt_bgr24> ren_base; agg::pixfmt_bgr24 pixf(rbuf_window()); ren_base renb(pixf); renb.clear(agg::rgba(1, 1, 1)); m_ras.clip_box(0,0, width(), height()); agg::trans_perspective shadow_persp(m_shape_bounds.x1, m_shape_bounds.y1, m_shape_bounds.x2, m_shape_bounds.y2, m_shadow_ctrl.polygon()); agg::conv_transform<shape_type, agg::trans_perspective> shadow_trans(m_shape, shadow_persp); // Render shadow m_ras.add_path(shadow_trans); agg::render_scanlines_aa_solid(m_ras, m_sl, renb, agg::rgba(0.2,0.3,0)); // Calculate the bounding box and extend it by the blur radius agg::rect_d bbox; agg::bounding_rect_single(shadow_trans, 0, &bbox.x1, &bbox.y1, &bbox.x2, &bbox.y2); bbox.x1 -= m_radius.value(); bbox.y1 -= m_radius.value(); bbox.x2 += m_radius.value(); bbox.y2 += m_radius.value(); if(m_method.cur_item() == 1) { // The recursive blur method represents the true Gussian Blur, // with theoretically infinite kernel. The restricted window size // results in extra influence of edge pixels. It's impossible to // solve correctly, but extending the right and top areas to another // radius value produces fair result. //------------------ bbox.x2 += m_radius.value(); bbox.y2 += m_radius.value(); } start_timer(); if(m_method.cur_item() != 2) { // Create a new pixel renderer and attach it to the main one as a child image. // It returns true if the attachment suceeded. It fails if the rectangle // (bbox) is fully clipped. //------------------ agg::pixfmt_bgr24 pixf2(m_rbuf2); if(pixf2.attach(pixf, int(bbox.x1), int(bbox.y1), int(bbox.x2), int(bbox.y2))) { // Blur it if(m_method.cur_item() == 0) { // More general method, but 30-40% slower. //------------------ //m_stack_blur.blur(pixf2, agg::uround(m_radius.value())); // Faster, but bore specific. // Works only for 8 bits per channel and only with radii <= 254. //------------------ agg::stack_blur_rgb24(pixf2, agg::uround(m_radius.value()), agg::uround(m_radius.value())); } else { // True Gaussian Blur, 3-5 times slower than Stack Blur, // but still constant time of radius. Very sensitive // to precision, doubles are must here. //------------------ m_recursive_blur.blur(pixf2, m_radius.value()); } } } else { // Blur separate channels //------------------ if(m_channel_r.status()) { typedef agg::pixfmt_alpha_blend_gray< agg::blender_gray8, agg::rendering_buffer, 3, 2> pixfmt_gray8r; pixfmt_gray8r pixf2r(m_rbuf2); if(pixf2r.attach(pixf, int(bbox.x1), int(bbox.y1), int(bbox.x2), int(bbox.y2))) { agg::stack_blur_gray8(pixf2r, agg::uround(m_radius.value()), agg::uround(m_radius.value())); } } if(m_channel_g.status()) { typedef agg::pixfmt_alpha_blend_gray< agg::blender_gray8, agg::rendering_buffer, 3, 1> pixfmt_gray8g; pixfmt_gray8g pixf2g(m_rbuf2); if(pixf2g.attach(pixf, int(bbox.x1), int(bbox.y1), int(bbox.x2), int(bbox.y2))) { agg::stack_blur_gray8(pixf2g, agg::uround(m_radius.value()), agg::uround(m_radius.value())); } } if(m_channel_b.status()) { typedef agg::pixfmt_alpha_blend_gray< agg::blender_gray8, agg::rendering_buffer, 3, 0> pixfmt_gray8b; pixfmt_gray8b pixf2b(m_rbuf2); if(pixf2b.attach(pixf, int(bbox.x1), int(bbox.y1), int(bbox.x2), int(bbox.y2))) { agg::stack_blur_gray8(pixf2b, agg::uround(m_radius.value()), agg::uround(m_radius.value())); } } } double tm = elapsed_time(); agg::render_ctrl(m_ras, m_sl, renb, m_shadow_ctrl); // Render the shape itself //------------------ m_ras.add_path(m_shape); agg::render_scanlines_aa_solid(m_ras, m_sl, renb, agg::rgba(0.6,0.9,0.7, 0.8)); char buf[64]; agg::gsv_text t; t.size(10.0); agg::conv_stroke<agg::gsv_text> st(t); st.width(1.5); sprintf(buf, "%3.2f ms", tm); t.start_point(140.0, 30.0); t.text(buf); m_ras.add_path(st); agg::render_scanlines_aa_solid(m_ras, m_sl, renb, agg::rgba(0,0,0)); agg::render_ctrl(m_ras, m_sl, renb, m_method); agg::render_ctrl(m_ras, m_sl, renb, m_radius); agg::render_ctrl(m_ras, m_sl, renb, m_channel_r); agg::render_ctrl(m_ras, m_sl, renb, m_channel_g); agg::render_ctrl(m_ras, m_sl, renb, m_channel_b); } }; int agg_main(int argc, char* argv[]) { the_application app(agg::pix_format_bgr24, flip_y); app.caption("AGG Example. Gaussian and Stack Blur"); if(app.init(440, 330, 0)) { return app.run(); } return 1; } |