agg/examples/flash_rasterizer.cpp

562 lines
15 KiB
C++

#include <math.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include "agg_rendering_buffer.h"
#include "agg_trans_viewport.h"
#include "agg_path_storage.h"
#include "agg_conv_transform.h"
#include "agg_conv_curve.h"
#include "agg_conv_stroke.h"
#include "agg_gsv_text.h"
#include "agg_scanline_u.h"
#include "agg_scanline_bin.h"
#include "agg_renderer_scanline.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_rasterizer_compound_aa.h"
#include "agg_span_allocator.h"
#include "agg_pixfmt_rgba.h"
#include "agg_bounding_rect.h"
#include "agg_color_gray.h"
#include "platform/agg_platform_support.h"
#define AGG_BGRA32
//#define AGG_BGRA128
#include "pixel_formats.h"
enum { flip_y = false };
namespace agg
{
struct path_style
{
unsigned path_id;
int left_fill;
int right_fill;
int line;
};
class compound_shape
{
public:
~compound_shape()
{
if(m_fd)
{
fclose(m_fd);
}
}
compound_shape() :
m_path(),
m_affine(),
m_curve(m_path),
m_trans(m_curve, m_affine),
m_styles()
{}
bool open(const char* fname)
{
m_fd = fopen(fname, "r");
return m_fd != 0;
}
bool read_next()
{
m_path.remove_all();
m_styles.remove_all();
const char space[] = " \t\n\r";
double ax, ay, cx, cy;
if(m_fd)
{
char buf[1024];
char* ts;
for(;;)
{
if(fgets(buf, 1022, m_fd) == 0) return false;
if(buf[0] == '=') break;
}
while(fgets(buf, 1022, m_fd))
{
if(buf[0] == '!') break;
if(buf[0] == 'P')
{
// BeginPath
path_style style;
style.path_id = m_path.start_new_path();
ts = strtok(buf, space); // Path;
ts = strtok(0, space); // left_style
style.left_fill = atoi(ts);
ts = strtok(0, space); // right_style
style.right_fill = atoi(ts);
ts = strtok(0, space); // line_style
style.line = atoi(ts);
ts = strtok(0, space); // ax
ax = atof(ts);
ts = strtok(0, space); // ay
ay = atof(ts);
m_path.move_to(ax, ay);
m_styles.add(style);
}
if(buf[0] == 'C')
{
ts = strtok(buf, space); // Curve;
ts = strtok(0, space); // cx
cx = atof(ts);
ts = strtok(0, space); // cy
cy = atof(ts);
ts = strtok(0, space); // ax
ax = atof(ts);
ts = strtok(0, space); // ay
ay = atof(ts);
m_path.curve3(cx, cy, ax, ay);
}
if(buf[0] == 'L')
{
ts = strtok(buf, space); // Line;
ts = strtok(0, space); // ax
ax = atof(ts);
ts = strtok(0, space); // ay
ay = atof(ts);
m_path.line_to(ax, ay);
}
if(buf[0] == '<')
{
// EndPath
}
}
return true;
}
return false;
}
unsigned operator [] (unsigned i) const
{
return m_styles[i].path_id;
}
unsigned paths() const { return m_styles.size(); }
const path_style& style(unsigned i) const
{
return m_styles[i];
}
void rewind(unsigned path_id)
{
m_trans.rewind(path_id);
}
unsigned vertex(double* x, double* y)
{
return m_trans.vertex(x, y);
}
double scale() const
{
return m_affine.scale();
}
void scale(double w, double h)
{
m_affine.reset();
double x1, y1, x2, y2;
bounding_rect(m_path, *this, 0, m_styles.size(),
&x1, &y1, &x2, &y2);
if(x1 < x2 && y1 < y2)
{
trans_viewport vp;
vp.preserve_aspect_ratio(0.5, 0.5, aspect_ratio_meet);
vp.world_viewport(x1, y1, x2, y2);
vp.device_viewport(0, 0, w, h);
m_affine = vp.to_affine();
}
m_curve.approximation_scale(m_affine.scale());
}
void approximation_scale(double s)
{
m_curve.approximation_scale(m_affine.scale() * s);
}
int hit_test(double x, double y, double r)
{
m_affine.inverse_transform(&x, &y);
r /= m_affine.scale();
unsigned i;
for(i = 0; i < m_path.total_vertices(); i++)
{
double vx, vy;
unsigned cmd = m_path.vertex(i, &vx, &vy);
if(is_vertex(cmd))
{
if(calc_distance(x, y, vx, vy) <= r)
{
return i;
}
}
}
return -1;
}
void modify_vertex(unsigned i, double x, double y)
{
m_affine.inverse_transform(&x, &y);
m_path.modify_vertex(i, x, y);
}
private:
path_storage m_path;
trans_affine m_affine;
conv_curve<path_storage> m_curve;
conv_transform<conv_curve<path_storage> > m_trans;
pod_bvector<path_style> m_styles;
double m_x1, m_y1, m_x2, m_y2;
FILE* m_fd;
};
// Testing class, color provider and span generator
//-------------------------------------------------
class test_styles
{
public:
test_styles(const color_type* solid_colors,
const color_type* gradient) :
m_solid_colors(solid_colors),
m_gradient(gradient)
{}
// Suppose that style=1 is a gradient
//---------------------------------------------
bool is_solid(unsigned style) const
{
return true;//style != 1;
}
// Just returns a color
//---------------------------------------------
const color_type& color(unsigned style) const
{
return m_solid_colors[style];
}
// Generate span. In our test case only one style (style=1)
// can be a span generator, so that, parameter "style"
// isn't used here.
//---------------------------------------------
void generate_span(color_type* span, int x, int y, unsigned len, unsigned style)
{
memcpy(span, m_gradient + x, sizeof(color_type) * len);
}
private:
const color_type* m_solid_colors;
const color_type* m_gradient;
};
}
class the_application : public agg::platform_support
{
public:
agg::compound_shape m_shape;
color_type m_colors[100];
agg::trans_affine m_scale;
agg::pod_array<color_type> m_gradient;
int m_point_idx;
int m_hit_x;
int m_hit_y;
the_application(agg::pix_format_e format, bool flip_y) :
agg::platform_support(format, flip_y),
m_point_idx(-1),
m_hit_x(-1),
m_hit_y(-1)
{
for(unsigned i = 0; i < 100; i++)
{
m_colors[i] = agg::srgba8(
(rand() & 0xFF),
(rand() & 0xFF),
(rand() & 0xFF),
230);
m_colors[i].premultiply();
}
}
bool open(const char* fname)
{
return m_shape.open(full_file_name(fname));
}
void read_next()
{
m_shape.read_next();
m_shape.scale(width(), height());
}
virtual void on_draw()
{
typedef agg::renderer_base<pixfmt_pre> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_scanline;
typedef agg::scanline_u8 scanline;
pixfmt_pre pixf(rbuf_window());
renderer_base ren_base(pixf);
ren_base.clear(agg::rgba(1.0, 1.0, 0.95));
renderer_scanline ren(ren_base);
unsigned i;
unsigned w = unsigned(width());
m_gradient.resize(w);
agg::srgba8 c1(255, 0, 0, 180);
agg::srgba8 c2(0, 0, 255, 180);
for(i = 0; i < w; i++)
{
m_gradient[i] = c1.gradient(c2, i / width());
m_gradient[i].premultiply();
}
agg::rasterizer_scanline_aa<agg::rasterizer_sl_clip_dbl> ras;
agg::rasterizer_compound_aa<agg::rasterizer_sl_clip_dbl> rasc;
agg::scanline_u8 sl;
agg::scanline_bin sl_bin;
agg::conv_transform<agg::compound_shape> shape(m_shape, m_scale);
agg::conv_stroke<agg::conv_transform<agg::compound_shape> > stroke(shape);
agg::test_styles style_handler(m_colors, m_gradient.data());
agg::span_allocator<color_type> alloc;
m_shape.approximation_scale(m_scale.scale());
// Fill shape
//----------------------
rasc.clip_box(0, 0, width(), height());
rasc.reset();
//rasc.filling_rule(agg::fill_even_odd);
start_timer();
for(i = 0; i < m_shape.paths(); i++)
{
if(m_shape.style(i).left_fill >= 0 ||
m_shape.style(i).right_fill >= 0)
{
rasc.styles(m_shape.style(i).left_fill,
m_shape.style(i).right_fill);
rasc.add_path(shape, m_shape.style(i).path_id);
}
}
agg::render_scanlines_compound(rasc, sl, sl_bin, ren_base, alloc, style_handler);
double tfill = elapsed_time();
// Hit-test test
bool draw_strokes = true;
if(m_hit_x >= 0 && m_hit_y >= 0)
{
if(rasc.hit_test(m_hit_x, m_hit_y))
{
draw_strokes = false;
}
}
// Draw strokes
//----------------------
start_timer();
if(draw_strokes)
{
ras.clip_box(0, 0, width(), height());
stroke.width(sqrt(m_scale.scale()));
stroke.line_join(agg::round_join);
stroke.line_cap(agg::round_cap);
for(i = 0; i < m_shape.paths(); i++)
{
ras.reset();
if(m_shape.style(i).line >= 0)
{
ras.add_path(stroke, m_shape.style(i).path_id);
ren.color(agg::srgba8(0,0,0, 128));
agg::render_scanlines(ras, sl, ren);
}
}
}
double tstroke = elapsed_time();
char buf[256];
agg::gsv_text t;
t.size(8.0);
t.flip(true);
agg::conv_stroke<agg::gsv_text> ts(t);
ts.width(1.6);
ts.line_cap(agg::round_cap);
sprintf(buf, "Fill=%.2fms (%dFPS) Stroke=%.2fms (%dFPS) Total=%.2fms (%dFPS)\n\n"
"Space: Next Shape\n\n"
"+/- : ZoomIn/ZoomOut (with respect to the mouse pointer)",
tfill, int(1000.0 / tfill),
tstroke, int(1000.0 / tstroke),
tfill+tstroke, int(1000.0 / (tfill+tstroke)));
t.start_point(10.0, 20.0);
t.text(buf);
ras.add_path(ts);
ren.color(agg::rgba(0,0,0));
agg::render_scanlines(ras, sl, ren);
}
virtual void on_key(int x, int y, unsigned key, unsigned flags)
{
if(key == ' ')
{
m_shape.read_next();
m_shape.scale(width(), height());
force_redraw();
}
if(key == '+' || key == agg::key_kp_plus)
{
m_scale *= agg::trans_affine_translation(-x, -y);
m_scale *= agg::trans_affine_scaling(1.1);
m_scale *= agg::trans_affine_translation(x, y);
force_redraw();
}
if(key == '-' || key == agg::key_kp_minus)
{
m_scale *= agg::trans_affine_translation(-x, -y);
m_scale *= agg::trans_affine_scaling(1/1.1);
m_scale *= agg::trans_affine_translation(x, y);
force_redraw();
}
if(key == agg::key_left)
{
m_scale *= agg::trans_affine_translation(-x, -y);
m_scale *= agg::trans_affine_rotation(-agg::pi / 20.0);
m_scale *= agg::trans_affine_translation(x, y);
force_redraw();
}
if(key == agg::key_right)
{
m_scale *= agg::trans_affine_translation(-x, -y);
m_scale *= agg::trans_affine_rotation(agg::pi / 20.0);
m_scale *= agg::trans_affine_translation(x, y);
force_redraw();
}
}
void on_mouse_move(int x, int y, unsigned flags)
{
if((flags & 3) == 0)
{
on_mouse_button_up(x, y, flags);
}
else
{
if(m_point_idx >= 0)
{
double xd = x;
double yd = y;
m_scale.inverse_transform(&xd, &yd);
m_shape.modify_vertex(m_point_idx, xd, yd);
force_redraw();
}
}
}
void on_mouse_button_down(int x, int y, unsigned flags)
{
if(flags & 1)
{
double xd = x;
double yd = y;
double r = 4.0 / m_scale.scale();
m_scale.inverse_transform(&xd, &yd);
m_point_idx = m_shape.hit_test(xd, yd, r);
force_redraw();
}
if(flags & 2)
{
m_hit_x = x;
m_hit_y = y;
force_redraw();
}
}
void on_mouse_button_up(int x, int y, unsigned flags)
{
m_point_idx = -1;
m_hit_x = -1;
m_hit_y = -1;
force_redraw();
}
};
int agg_main(int argc, char* argv[])
{
the_application app(pix_format, flip_y);
app.caption("AGG Example - Flash Rasterizer");
const char* fname = "shapes.txt";
if(argc > 1) fname = argv[1];
if(!app.open(fname))
{
char buf[256];
if(strcmp(fname, "shapes.txt") == 0)
{
sprintf(buf, "File not found: %s%s. Download http://www.antigrain.com/%s%s\n"
"or copy it from the ../art directory.",
fname, fname);
}
else
{
sprintf(buf, "File not found: %s", fname);
}
app.message(buf);
return 1;
}
if(app.init(655, 520, agg::window_resize))
{
app.read_next();
return app.run();
}
return 1;
}