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