1 #include <math.h>
2 #include <stdio.h>
3 #include <time.h>
4 #include <stdlib.h>
5 #include "agg_rendering_buffer.h"
6 #include "agg_trans_viewport.h"
7 #include "agg_path_storage.h"
8 #include "agg_conv_transform.h"
9 #include "agg_conv_curve.h"
10 #include "agg_conv_stroke.h"
11 #include "agg_gsv_text.h"
12 #include "agg_scanline_u.h"
13 #include "agg_scanline_bin.h"
14 #include "agg_renderer_scanline.h"
15 #include "agg_rasterizer_scanline_aa.h"
16 #include "agg_rasterizer_compound_aa.h"
17 #include "agg_span_allocator.h"
18 #include "agg_gamma_lut.h"
19 #include "agg_pixfmt_rgba.h"
20 #include "agg_bounding_rect.h"
21 #include "agg_color_gray.h"
22 #include "platform/agg_platform_support.h"
23
24
25
26 enum { flip_y = false };
27
28 typedef agg::pixfmt_bgra32_pre pixfmt;
29
30
31
32 namespace agg
33 {
34 struct path_style
35 {
36 unsigned path_id;
37 int left_fill;
38 int right_fill;
39 int line;
40 };
41
42 class compound_shape
43 {
44 public:
~compound_shape()45 ~compound_shape()
46 {
47 if(m_fd)
48 {
49 fclose(m_fd);
50 }
51 }
52
compound_shape()53 compound_shape() :
54 m_path(),
55 m_affine(),
56 m_curve(m_path),
57 m_trans(m_curve, m_affine),
58 m_styles()
59 {}
60
open(const char * fname)61 bool open(const char* fname)
62 {
63 m_fd = fopen(fname, "r");
64 return m_fd != 0;
65 }
66
read_next()67 bool read_next()
68 {
69 m_path.remove_all();
70 m_styles.remove_all();
71 const char space[] = " \t\n\r";
72 double ax, ay, cx, cy;
73 if(m_fd)
74 {
75 char buf[1024];
76 char* ts;
77
78 for(;;)
79 {
80 if(fgets(buf, 1022, m_fd) == 0) return false;
81 if(buf[0] == '=') break;
82 }
83
84 while(fgets(buf, 1022, m_fd))
85 {
86 if(buf[0] == '!') break;
87 if(buf[0] == 'P')
88 {
89 // BeginPath
90 path_style style;
91 style.path_id = m_path.start_new_path();
92 ts = strtok(buf, space); // Path;
93 ts = strtok(0, space); // left_style
94 style.left_fill = atoi(ts);
95 ts = strtok(0, space); // right_style
96 style.right_fill = atoi(ts);
97 ts = strtok(0, space); // line_style
98 style.line = atoi(ts);
99 ts = strtok(0, space); // ax
100 ax = atof(ts);
101 ts = strtok(0, space); // ay
102 ay = atof(ts);
103 m_path.move_to(ax, ay);
104 m_styles.add(style);
105 }
106
107
108 if(buf[0] == 'C')
109 {
110 ts = strtok(buf, space); // Curve;
111 ts = strtok(0, space); // cx
112 cx = atof(ts);
113 ts = strtok(0, space); // cy
114 cy = atof(ts);
115 ts = strtok(0, space); // ax
116 ax = atof(ts);
117 ts = strtok(0, space); // ay
118 ay = atof(ts);
119 m_path.curve3(cx, cy, ax, ay);
120 }
121
122 if(buf[0] == 'L')
123 {
124 ts = strtok(buf, space); // Line;
125 ts = strtok(0, space); // ax
126 ax = atof(ts);
127 ts = strtok(0, space); // ay
128 ay = atof(ts);
129 m_path.line_to(ax, ay);
130 }
131
132
133 if(buf[0] == '<')
134 {
135 // EndPath
136 }
137 }
138 return true;
139 }
140 return false;
141 }
142
143
operator [](unsigned i) const144 unsigned operator [] (unsigned i) const
145 {
146 return m_styles[i].path_id;
147 }
148
paths() const149 unsigned paths() const { return m_styles.size(); }
style(unsigned i) const150 const path_style& style(unsigned i) const
151 {
152 return m_styles[i];
153 }
154
rewind(unsigned path_id)155 void rewind(unsigned path_id)
156 {
157 m_trans.rewind(path_id);
158 }
159
vertex(double * x,double * y)160 unsigned vertex(double* x, double* y)
161 {
162 return m_trans.vertex(x, y);
163 }
164
scale() const165 double scale() const
166 {
167 return m_affine.scale();
168 }
169
scale(double w,double h)170 void scale(double w, double h)
171 {
172 m_affine.reset();
173 double x1, y1, x2, y2;
174 bounding_rect(m_path, *this, 0, m_styles.size(),
175 &x1, &y1, &x2, &y2);
176 if(x1 < x2 && y1 < y2)
177 {
178 trans_viewport vp;
179 vp.preserve_aspect_ratio(0.5, 0.5, aspect_ratio_meet);
180 vp.world_viewport(x1, y1, x2, y2);
181 vp.device_viewport(0, 0, w, h);
182 m_affine = vp.to_affine();
183 }
184 m_curve.approximation_scale(m_affine.scale());
185 }
186
approximation_scale(double s)187 void approximation_scale(double s)
188 {
189 m_curve.approximation_scale(m_affine.scale() * s);
190 }
191
hit_test(double x,double y,double r)192 int hit_test(double x, double y, double r)
193 {
194 m_affine.inverse_transform(&x, &y);
195 r /= m_affine.scale();
196 unsigned i;
197 for(i = 0; i < m_path.total_vertices(); i++)
198 {
199 double vx, vy;
200 unsigned cmd = m_path.vertex(i, &vx, &vy);
201 if(is_vertex(cmd))
202 {
203 if(calc_distance(x, y, vx, vy) <= r)
204 {
205 return i;
206 }
207 }
208 }
209 return -1;
210 }
211
modify_vertex(unsigned i,double x,double y)212 void modify_vertex(unsigned i, double x, double y)
213 {
214 m_affine.inverse_transform(&x, &y);
215 m_path.modify_vertex(i, x, y);
216 }
217
218 private:
219 path_storage m_path;
220 trans_affine m_affine;
221 conv_curve<path_storage> m_curve;
222 conv_transform<conv_curve<path_storage> > m_trans;
223 pod_bvector<path_style> m_styles;
224 double m_x1, m_y1, m_x2, m_y2;
225
226 FILE* m_fd;
227 };
228
229
230
231 // Testing class, color provider and span generator
232 //-------------------------------------------------
233 class test_styles
234 {
235 public:
test_styles(const rgba8 * solid_colors,const rgba8 * gradient)236 test_styles(const rgba8* solid_colors,
237 const rgba8* gradient) :
238 m_solid_colors(solid_colors),
239 m_gradient(gradient)
240 {}
241
242 // Suppose that style=1 is a gradient
243 //---------------------------------------------
is_solid(unsigned style) const244 bool is_solid(unsigned style) const
245 {
246 return true;//style != 1;
247 }
248
249 // Just returns a color
250 //---------------------------------------------
color(unsigned style) const251 const rgba8& color(unsigned style) const
252 {
253 return m_solid_colors[style];
254 }
255
256 // Generate span. In our test case only one style (style=1)
257 // can be a span generator, so that, parameter "style"
258 // isn't used here.
259 //---------------------------------------------
generate_span(rgba8 * span,int x,int y,unsigned len,unsigned style)260 void generate_span(rgba8* span, int x, int y, unsigned len, unsigned style)
261 {
262 memcpy(span, m_gradient + x, sizeof(rgba8) * len);
263 }
264
265 private:
266 const rgba8* m_solid_colors;
267 const rgba8* m_gradient;
268 };
269
270
271
272
273 }
274
275
276
277
278
279
280 class the_application : public agg::platform_support
281 {
282
283 public:
284 agg::compound_shape m_shape;
285 agg::rgba8 m_colors[100];
286 agg::trans_affine m_scale;
287 agg::gamma_lut<> m_gamma;
288 agg::pod_array<agg::rgba8> m_gradient;
289 int m_point_idx;
290 int m_hit_x;
291 int m_hit_y;
292
the_application(agg::pix_format_e format,bool flip_y)293 the_application(agg::pix_format_e format, bool flip_y) :
294 agg::platform_support(format, flip_y),
295 m_point_idx(-1),
296 m_hit_x(-1),
297 m_hit_y(-1)
298 {
299 m_gamma.gamma(2.0);
300
301 for(unsigned i = 0; i < 100; i++)
302 {
303 m_colors[i] = agg::rgba8(
304 (rand() & 0xFF),
305 (rand() & 0xFF),
306 (rand() & 0xFF),
307 230);
308
309 m_colors[i].apply_gamma_dir(m_gamma);
310 m_colors[i].premultiply();
311 }
312 }
313
314
315
open(const char * fname)316 bool open(const char* fname)
317 {
318 return m_shape.open(full_file_name(fname));
319 }
320
read_next()321 void read_next()
322 {
323 m_shape.read_next();
324 m_shape.scale(width(), height());
325 }
326
on_draw()327 virtual void on_draw()
328 {
329 typedef agg::renderer_base<pixfmt> renderer_base;
330 typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_scanline;
331 typedef agg::scanline_u8 scanline;
332
333 pixfmt pixf(rbuf_window());
334 renderer_base ren_base(pixf);
335 ren_base.clear(agg::rgba(1.0, 1.0, 0.95));
336 renderer_scanline ren(ren_base);
337
338 unsigned i;
339 unsigned w = unsigned(width());
340 m_gradient.resize(w);
341 agg::rgba8 c1(255, 0, 0, 180);
342 agg::rgba8 c2(0, 0, 255, 180);
343 for(i = 0; i < w; i++)
344 {
345 m_gradient[i] = c1.gradient(c2, i / width());
346 m_gradient[i].premultiply();
347 }
348
349 agg::rasterizer_scanline_aa<agg::rasterizer_sl_clip_dbl> ras;
350 agg::rasterizer_compound_aa<agg::rasterizer_sl_clip_dbl> rasc;
351 agg::scanline_u8 sl;
352 agg::scanline_bin sl_bin;
353 agg::conv_transform<agg::compound_shape> shape(m_shape, m_scale);
354 agg::conv_stroke<agg::conv_transform<agg::compound_shape> > stroke(shape);
355
356 agg::test_styles style_handler(m_colors, m_gradient.data());
357 agg::span_allocator<agg::rgba8> alloc;
358
359 m_shape.approximation_scale(m_scale.scale());
360
361 // Fill shape
362 //----------------------
363 rasc.clip_box(0, 0, width(), height());
364 rasc.reset();
365 //rasc.filling_rule(agg::fill_even_odd);
366 start_timer();
367 for(i = 0; i < m_shape.paths(); i++)
368 {
369
370 if(m_shape.style(i).left_fill >= 0 ||
371 m_shape.style(i).right_fill >= 0)
372 {
373 rasc.styles(m_shape.style(i).left_fill,
374 m_shape.style(i).right_fill);
375 rasc.add_path(shape, m_shape.style(i).path_id);
376 }
377 }
378 agg::render_scanlines_compound(rasc, sl, sl_bin, ren_base, alloc, style_handler);
379 double tfill = elapsed_time();
380
381 // Hit-test test
382 bool draw_strokes = true;
383 if(m_hit_x >= 0 && m_hit_y >= 0)
384 {
385 if(rasc.hit_test(m_hit_x, m_hit_y))
386 {
387 draw_strokes = false;
388 }
389 }
390
391 // Draw strokes
392 //----------------------
393 start_timer();
394 if(draw_strokes)
395 {
396 ras.clip_box(0, 0, width(), height());
397 stroke.width(sqrt(m_scale.scale()));
398 stroke.line_join(agg::round_join);
399 stroke.line_cap(agg::round_cap);
400 for(i = 0; i < m_shape.paths(); i++)
401 {
402 ras.reset();
403 if(m_shape.style(i).line >= 0)
404 {
405 ras.add_path(stroke, m_shape.style(i).path_id);
406 ren.color(agg::rgba8(0,0,0, 128));
407 agg::render_scanlines(ras, sl, ren);
408 }
409 }
410 }
411 double tstroke = elapsed_time();
412
413
414 char buf[256];
415 agg::gsv_text t;
416 t.size(8.0);
417 t.flip(true);
418
419 agg::conv_stroke<agg::gsv_text> ts(t);
420 ts.width(1.6);
421 ts.line_cap(agg::round_cap);
422
423 sprintf(buf, "Fill=%.2fms (%dFPS) Stroke=%.2fms (%dFPS) Total=%.2fms (%dFPS)\n\n"
424 "Space: Next Shape\n\n"
425 "+/- : ZoomIn/ZoomOut (with respect to the mouse pointer)",
426 tfill, int(1000.0 / tfill),
427 tstroke, int(1000.0 / tstroke),
428 tfill+tstroke, int(1000.0 / (tfill+tstroke)));
429
430 t.start_point(10.0, 20.0);
431 t.text(buf);
432
433 ras.add_path(ts);
434 ren.color(agg::rgba(0,0,0));
435 agg::render_scanlines(ras, sl, ren);
436
437 if(m_gamma.gamma() != 1.0)
438 {
439 pixf.apply_gamma_inv(m_gamma);
440 }
441 }
442
443
on_key(int x,int y,unsigned key,unsigned flags)444 virtual void on_key(int x, int y, unsigned key, unsigned flags)
445 {
446 if(key == ' ')
447 {
448 m_shape.read_next();
449 m_shape.scale(width(), height());
450 force_redraw();
451 }
452
453 if(key == '+' || key == agg::key_kp_plus)
454 {
455 m_scale *= agg::trans_affine_translation(-x, -y);
456 m_scale *= agg::trans_affine_scaling(1.1);
457 m_scale *= agg::trans_affine_translation(x, y);
458 force_redraw();
459 }
460
461 if(key == '-' || key == agg::key_kp_minus)
462 {
463 m_scale *= agg::trans_affine_translation(-x, -y);
464 m_scale *= agg::trans_affine_scaling(1/1.1);
465 m_scale *= agg::trans_affine_translation(x, y);
466 force_redraw();
467 }
468
469 if(key == agg::key_left)
470 {
471 m_scale *= agg::trans_affine_translation(-x, -y);
472 m_scale *= agg::trans_affine_rotation(-agg::pi / 20.0);
473 m_scale *= agg::trans_affine_translation(x, y);
474 force_redraw();
475 }
476
477 if(key == agg::key_right)
478 {
479 m_scale *= agg::trans_affine_translation(-x, -y);
480 m_scale *= agg::trans_affine_rotation(agg::pi / 20.0);
481 m_scale *= agg::trans_affine_translation(x, y);
482 force_redraw();
483 }
484 }
485
on_mouse_move(int x,int y,unsigned flags)486 void on_mouse_move(int x, int y, unsigned flags)
487 {
488 if((flags & 3) == 0)
489 {
490 on_mouse_button_up(x, y, flags);
491 }
492 else
493 {
494 if(m_point_idx >= 0)
495 {
496 double xd = x;
497 double yd = y;
498 m_scale.inverse_transform(&xd, &yd);
499 m_shape.modify_vertex(m_point_idx, xd, yd);
500 force_redraw();
501 }
502 }
503 }
504
on_mouse_button_down(int x,int y,unsigned flags)505 void on_mouse_button_down(int x, int y, unsigned flags)
506 {
507 if(flags & 1)
508 {
509 double xd = x;
510 double yd = y;
511 double r = 4.0 / m_scale.scale();
512 m_scale.inverse_transform(&xd, &yd);
513 m_point_idx = m_shape.hit_test(xd, yd, r);
514 force_redraw();
515 }
516 if(flags & 2)
517 {
518 m_hit_x = x;
519 m_hit_y = y;
520 force_redraw();
521 }
522 }
523
on_mouse_button_up(int x,int y,unsigned flags)524 void on_mouse_button_up(int x, int y, unsigned flags)
525 {
526 m_point_idx = -1;
527 m_hit_x = -1;
528 m_hit_y = -1;
529 force_redraw();
530 }
531
532
533 };
534
535
536
agg_main(int argc,char * argv[])537 int agg_main(int argc, char* argv[])
538 {
539 the_application app(agg::pix_format_bgra32, flip_y);
540 app.caption("AGG Example - Flash Rasterizer");
541 const char* fname = "shapes.txt";
542 if(argc > 1) fname = argv[1];
543 if(!app.open(fname))
544 {
545 char buf[256];
546 if(strcmp(fname, "shapes.txt") == 0)
547 {
548 sprintf(buf, "File not found: %s. Download http://www.antigrain.com/%s\n"
549 "or copy it from another directory if available.",
550 fname, fname);
551 }
552 else
553 {
554 sprintf(buf, "File not found: %s", fname);
555 }
556 app.message(buf);
557 return 1;
558 }
559
560 if(app.init(655, 520, agg::window_resize))
561 {
562 app.read_next();
563 return app.run();
564 }
565 return 1;
566 }
567
568
569
570
571
572