1 //
2 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 //   Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18 
19 #ifdef HAVE_CONFIG_H
20 #include "gnashconfig.h"
21 #endif
22 
23 #include <sys/time.h>
24 #include <cstring>
25 #include <cmath>
26 #include <iostream>
27 #include <iterator>
28 #include <functional>
29 #include <boost/utility.hpp>
30 
31 #include "log.h"
32 #include "RGBA.h"
33 #include "GnashImage.h"
34 #include "GnashNumeric.h"
35 #include "FillStyle.h"
36 #include "LineStyle.h"
37 #include "Transform.h"
38 #include "log.h"
39 #include "utility.h"
40 #include "Range2d.h"
41 #include "SWFCxForm.h"
42 #include "openvg/OpenVGRenderer.h"
43 #include "openvg/OpenVGBitmap.h"
44 #include "openvg/OpenVGStyle.h"
45 #include "SWFMatrix.h"
46 #include "swf/ShapeRecord.h"
47 #include "CachedBitmap.h"
48 
49 #include <VG/vgu.h>
50 #ifdef HAVE_VG_EXT_H
51 # include <VG/ext.h>
52 #else
53 # ifdef HAVE_VG_VGEXT_H
54 #  include <VG/vgext.h>
55 # endif
56 #endif
57 #include <VG/openvg.h>
58 #define GNASH_IMAGE_QUALITY     VG_IMAGE_QUALITY_FASTER
59 #define GNASH_RENDER_QUALITY    VG_RENDERING_QUALITY_FASTER
60 
61 #define  MAX_POINTS (4096)
62 
63 /// \file Renderer_ovg.cpp
64 /// \brief The OpenVG renderer and related code.
65 ///
66 
67 static const int TwipsPerInch = 1440;
68 
69 namespace gnash {
70 
71 typedef std::vector<Path> PathVec;
72 typedef std::vector<geometry::Range2d<int> > ClipBounds;
73 
74 namespace renderer {
75 
76 namespace openvg {
77 
78 /// Transforms the current OpenVG SWFMatrix using the given SWFMatrix.
79 /// When it goes out of scope, the SWFMatrix will be reset to what it
80 /// was before the new SWFMatrix was applied.
81 class eglScopeMatrix : public boost::noncopyable
82 {
83 public:
eglScopeMatrix(const SWFMatrix & m)84     eglScopeMatrix(const SWFMatrix& m)
85         {
86             // GNASH_REPORT_FUNCTION;
87 
88             vgGetMatrix(_orig_mat);
89             // Renderer_ovg::printVGMatrix(_orig_mat);
90 
91             float mat[9];
92             memset(mat, 0, sizeof(mat));
93             mat[0] = m.a() / 65536.0f;
94             mat[1] = m.b() / 65536.0f;
95             mat[3] = m.c() / 65536.0f;
96             mat[4] = m.d() / 65536.0f;
97             mat[6] = m.tx();
98             mat[7] = m.ty();
99             vgMultMatrix(mat);
100             // Renderer_ovg::printVGMatrix(mat);
101         }
102 
~eglScopeMatrix()103     ~eglScopeMatrix()
104         {
105             vgLoadMatrix(_orig_mat);
106         }
107 private:
108     VGfloat _orig_mat[9];
109 };
110 
111 /// @note
112 /// A VGpath is constructed from a series of appended path
113 /// segments. When drawing shapes from flash, we start each path by
114 /// moving to a known location. Then segments are appended, and then
115 /// the path is closed. This is also used for fills.
116 
117 #define MAX_SEG  (256)
118 
119 /// Start a VGPath by moving to a specified location
120 ///
121 /// @param path The VGPath to start
122 /// @returns nothing
123 inline void
startpath(VGPath path,const int x,const int y)124 startpath(VGPath path, const int x, const int y)
125 {
126     VGubyte     gseg[1];
127     VGfloat     gdata[2];
128 
129     gseg[0] = VG_MOVE_TO;
130     gdata[0] = x;
131     gdata[1] = y;
132     vgAppendPathData (path, 1, gseg, gdata);
133 }
134 
135 /// Close the VGPath started by startpath()
136 ///
137 /// @param path The VGPath to close
138 /// @returns nothing
139 inline void
closepath(VGPath path)140 closepath(VGPath path)
141 {
142     VGubyte     gseg[1];
143     VGfloat     gdata[2];
144 
145     gseg[0] = VG_CLOSE_PATH;
146     vgAppendPathData (path, 1, gseg, gdata);
147 }
148 
149 /// Add a series of edges to the existing path created by startpath()
150 ///
151 /// @param path The VGPath to append segments to
152 /// @param edges The segments to append to the path
153 /// @param anchor_x The X coordinate to start from
154 /// @param anchor_y The Y coordinate to start from
155 /// @returns nothing
156 inline void
preparepath(VGPath path,const std::vector<Edge> & edges,const float & anchor_x,const float & anchor_y)157 preparepath(VGPath path, const std::vector<Edge>& edges,
158                         const float& anchor_x, const float& anchor_y)
159 {
160     VGubyte     gseg[MAX_SEG];
161     VGfloat     gdata[MAX_SEG*3*2];
162     int         scount = 0;
163     int         dcount = 0;
164 
165     point start(anchor_x, anchor_y);
166     point anchor(anchor_x, anchor_y);
167 
168     for (std::vector<Edge>::const_iterator it = edges.begin(), end = edges.end();
169          it != end; ++it) {
170         const Edge& the_edge = *it;
171 
172         point target(the_edge.ap.x, the_edge.ap.y);
173 
174         if (the_edge.straight()) {
175             gseg[scount++]  = VG_LINE_TO;
176             gdata[dcount++] = target.x;
177             gdata[dcount++] = target.y;
178         } else {
179             gseg[scount++]  = VG_QUAD_TO;
180             gdata[dcount++] = the_edge.cp.x;
181             gdata[dcount++] = the_edge.cp.y;
182             gdata[dcount++] = target.x;
183             gdata[dcount++] = target.y;
184         }
185         if (scount >= MAX_SEG-2) {
186             vgAppendPathData(path, scount, gseg, gdata);
187             scount = 0;
188             dcount = 0;
189         }
190         anchor = target;
191     }
192 
193     if (scount > 0)
194         vgAppendPathData (path, scount, gseg, gdata);
195 }
196 
197 template<typename C, typename T, typename R, typename A>
198 void
for_each(C & container,R (T::* pmf)(const A &),const A & arg)199 for_each(C& container, R (T::*pmf)(const A&),const A& arg)
200 {
201     std::for_each(container.begin(), container.end(),
202                   std::bind(pmf, _1, std::ref(arg)));
203 }
204 
Renderer_ovg()205 Renderer_ovg::Renderer_ovg()
206     : _display_width(0.0),
207       _display_height(0.0),
208       _drawing_mask(false),
209 #ifdef OPENVG_VERSION_1_1
210       _mask_layer(VG_INVALID_HANDLE),
211 #endif
212       _fillpaint(VG_INVALID_HANDLE),
213       _strokepaint(VG_INVALID_HANDLE),
214       _aspect_ratio(0.75)       // 4:3 aspect ratio
215 {
216     // GNASH_REPORT_FUNCTION;
217 }
218 
Renderer_ovg(renderer::GnashDevice::dtype_t)219 Renderer_ovg::Renderer_ovg(renderer::GnashDevice::dtype_t /* dtype */)
220     : _display_width(0.0),
221       _display_height(0.0),
222       _drawing_mask(false),
223 #ifdef OPENVG_VERSION_1_1
224       _mask_layer(VG_INVALID_HANDLE),
225 #endif
226       _fillpaint(VG_INVALID_HANDLE),
227       _strokepaint(VG_INVALID_HANDLE),
228       _aspect_ratio(0.75)       // 4:3 aspect ratio
229 {
230     // GNASH_REPORT_FUNCTION;
231 
232     set_scale(1.0f, 1.0f);
233 
234 #if 0
235     _fillpaint = vgCreatePaint();
236 
237     _strokepaint = vgCreatePaint();
238 
239     // this paint object is used for solid, gradient, and pattern fills.
240     vgSetPaint (_fillpaint,   VG_FILL_PATH);
241 
242     // this pain object is used for paths
243     vgSetPaint (_strokepaint, VG_STROKE_PATH);
244 #endif
245 }
246 
247 void
init(float x,float y)248 Renderer_ovg::init(float x, float y)
249 {
250     // GNASH_REPORT_FUNCTION;
251 
252     _display_width = x;
253     _display_height = y;
254 
255     // this paint object is used for solid, gradient, and pattern fills.
256     _fillpaint = vgCreatePaint();
257     vgSetPaint (_fillpaint, VG_FILL_PATH);
258 
259     // this pain object is used for paths
260     _strokepaint = vgCreatePaint();
261     vgSetPaint (_strokepaint, VG_STROKE_PATH);
262 
263     // Turn on alpha blending.
264     vgSeti (VG_BLEND_MODE, VG_BLEND_SRC_OVER);
265 
266     vgSeti(VG_RENDERING_QUALITY, GNASH_RENDER_QUALITY);
267     vgSetf(VG_STROKE_LINE_WIDTH, 20.0f);
268 
269     VGfloat clearColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
270     vgSetfv( VG_CLEAR_COLOR, 4, clearColor );
271 
272 #ifdef OPENVG_VERSION_1_1
273     _mask_layer = vgCreateMaskLayer(x, y);
274 #endif
275 
276     log_debug(_("VG Vendor is %s, VG Version is %s, VG Renderer is %s"),
277               vgGetString(VG_VENDOR), vgGetString(VG_VERSION),
278               vgGetString(VG_RENDERER));
279     log_debug(_("VG Extensions are: "), vgGetString(VG_EXTENSIONS));
280     printVGParams();
281 
282     // vgSeti(VG_SCISSORING, VG_FALSE);
283     vgClear(0, 0, x*2, y*2);
284 }
285 
~Renderer_ovg()286 Renderer_ovg::~Renderer_ovg()
287 {
288     // GNASH_REPORT_FUNCTION;
289 
290     vgDestroyPaint(_fillpaint);
291     vgDestroyPaint(_strokepaint);
292 #ifdef OPENVG_VERSION_1_1
293     vgDestroyMaskLayer(_mask_layer);
294 #endif
295 }
296 
297 // Given an image, returns a pointer to a CachedBitmap class
298 // that can later be passed to fill_styleX_bitmap(), to set a
299 // bitmap fill style. We only cache the GnashImage here, as a
300 // VGImage can't be created yet until the renderer is initialized.
301 CachedBitmap *
createCachedBitmap(std::unique_ptr<image::GnashImage> im)302 Renderer_ovg::createCachedBitmap(std::unique_ptr<image::GnashImage> im)
303 {
304     // GNASH_REPORT_FUNCTION;
305 
306     CachedBitmap *cbinfo = reinterpret_cast<CachedBitmap *>(new OpenVGBitmap(im.release(),
307                                                                              _fillpaint));
308     return cbinfo;
309 }
310 
311 // Since we store drawing operations in display lists, we take special care
312 // to store video frame operations in their own display list, lest they be
313 // anti-aliased with the rest of the drawing. Since display lists cannot be
314 // concatenated this means we'll add up with several display lists for normal
315 // drawing operations.
316 void
drawVideoFrame(image::GnashImage *,const SWFMatrix *,const SWFRect *,bool)317 Renderer_ovg::drawVideoFrame(image::GnashImage* /* frame */, const SWFMatrix* /* m */,
318                              const SWFRect* /* bounds */, bool /*smooth*/)
319 {
320     log_unimpl(_("drawVideoFrame"));
321 }
322 
323 void
world_to_pixel(int & x,int & y,float world_x,float world_y) const324 Renderer_ovg::world_to_pixel(int& x, int& y, float world_x, float world_y) const
325 {
326 //    GNASH_REPORT_FUNCTION;
327 
328     // negative pixels seems ok here... we don't
329     // clip to valid range, use world_to_pixel(rect&)
330     // and Intersect() against valid range instead.
331     point p(world_x, world_y);
332     stage_matrix.transform(p);
333     x = (int)p.x;
334     y = (int)p.y;
335 }
336 
337 geometry::Range2d<int>
world_to_pixel(const SWFRect & wb) const338 Renderer_ovg::world_to_pixel(const SWFRect& wb) const
339 {
340 //    GNASH_REPORT_FUNCTION;
341 
342     using namespace gnash::geometry;
343 
344     if ( wb.is_null() ) return Range2d<int>(nullRange);
345     if ( wb.is_world() ) return Range2d<int>(worldRange);
346 
347     int xmin, ymin, xmax, ymax;
348 
349     world_to_pixel(xmin, ymin, wb.get_x_min(), wb.get_y_min());
350     world_to_pixel(xmax, ymax, wb.get_x_max(), wb.get_y_max());
351 
352     return Range2d<int>(xmin, ymin, xmax, ymax);
353 }
354 
355 geometry::Range2d<int>
world_to_pixel(const geometry::Range2d<float> & wb) const356 Renderer_ovg::world_to_pixel(const geometry::Range2d<float>& wb) const
357 {
358     // GNASH_REPORT_FUNCTION;
359 
360     if (wb.isNull() || wb.isWorld()) return wb;
361 
362     int xmin, ymin, xmax, ymax;
363 
364     world_to_pixel(xmin, ymin, wb.getMinX(), wb.getMinY());
365     world_to_pixel(xmax, ymax, wb.getMaxX(), wb.getMaxY());
366 
367     return geometry::Range2d<int>(xmin, ymin, xmax, ymax);
368 }
369 
370 point
pixel_to_world(int x,int y) const371 Renderer_ovg::pixel_to_world(int x, int y) const
372 {
373     // GNASH_REPORT_FUNCTION;
374 
375     point p(x, y);
376     SWFMatrix mat = stage_matrix;
377     mat.invert().transform(p);
378     return p;
379 }
380 
381 /// Setup the renderer to display by setting the Matrix for scaling,
382 /// shearing, and transformations.
383 ///
384 /// @param width - stage width
385 /// @param height - stage height
386 /// @param x0 - minimum frame size in X dimension in twips
387 /// @param x1 - maximum frame size in X dimension in twips
388 /// @param y0 - minimum frame size in Y dimension in twips
389 /// @param y1 - maximum frame size in Y dimension in twips
390 void
begin_display(gnash::rgba const &,int width,int height,float x0,float x1,float y0,float y1)391 Renderer_ovg::begin_display(gnash::rgba const&, int width, int height,
392                             float x0, float x1, float y0, float y1)
393 {
394     // GNASH_REPORT_FUNCTION;
395 
396     // Disable masking
397     vgSeti (VG_MASKING, VG_FALSE);
398 
399     VGfloat mat[9];
400     memset(mat, 0, sizeof(mat));
401     // sx and sy define scaling in the x and y directions, respectively;
402     // shx and shy define shearing in the x and y directions, respectively;
403     // tx and ty define translation in the x and y directions, respectively.
404 
405     // Flash internally calculates anything that uses pixels with
406     // twips (or 1/20 of a pixel). Sprites, movie clips and any other
407     // object on the stage are positioned with twips. As a result, the
408     // coordinates of (for example) sprites are always multiples of
409     // 0.05 (i.e. 1/20).
410     mat[0] = (VGfloat)width / VGfloat(x1 - x0);  // scale sx = 0.05
411     mat[1] = 0; // shx
412     mat[3] = 0; // shy
413     mat[4] = -((VGfloat)height / VGfloat(y1 - y0)); // scale sy = -0.05
414     mat[6] = 0;   // shift tx in pixels
415     mat[7] = height;   // shift ty in pixels
416 
417     vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
418     // The default values after vgLoadIdentity() are:
419     //          [ 1 0 0 ]
420     //       M =| 0 1 0 |
421     //          [ 0 0 1 ]
422     vgLoadIdentity();
423 
424     // An affine transformation maps a point (x, y) (represented using
425     // homogeneous coordinates as the column vector [x, y, 1]T) into the
426     // point (x*sx + y*shx + tx, x*shy + y*sy + ty) using matrix multiplication:
427     // [ sx shx tx ] [ x ]   [ x∗sx + y∗shx + tx ]
428     // | shy sy ty |.[ y | = | x∗shy + y∗sy + ty |
429     // [   0  0  1 ] [ 1 ]   [            1      ]
430     //
431     // If not VG_MATRIX_IMAGE_USER_TO_SURFACE, w0, w1, and w2 are ignored.
432     vgLoadMatrix (mat);
433 
434     // vgSeti(VG_SCISSORING, VG_FALSE);
435     vgClear(0, 0, _display_width, _display_height);
436 }
437 
438 void
end_display()439 Renderer_ovg::end_display()
440 {
441     // GNASH_REPORT_FUNCTION;
442 }
443 
444 /// Draw a line-strip directly, using a thin, solid line.
445 //
446 /// Can be used to draw empty boxes and cursors.
447 void
drawLine(const std::vector<point> & coords,const rgba & fill,const SWFMatrix & mat)448 Renderer_ovg::drawLine(const std::vector<point>& coords, const rgba& fill,
449                        const SWFMatrix& mat)
450 {
451     // GNASH_REPORT_FUNCTION;
452 
453     VGubyte     gseg[MAX_SEG];
454     VGfloat     gdata[MAX_SEG*3*2];
455     int         scount = 0;
456     int         dcount = 0;
457 
458     if (coords.empty()) return;
459 
460     eglScopeMatrix scope_mat(mat);
461 
462     VGfloat color[] = {
463         fill.m_r / 255.0f,
464         fill.m_g / 255.0f,
465         fill.m_b / 255.0f,
466         1.0f
467     };
468     VGPath      vg_path;
469     vg_path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
470                             VG_PATH_DATATYPE_F,
471                             1, 0, 0, 0,
472                             VG_PATH_CAPABILITY_ALL);
473     vgSetf (VG_FILL_RULE, VG_EVEN_ODD );
474     vgSetParameteri (_strokepaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
475     vgSetParameterfv (_strokepaint, VG_PAINT_COLOR, 4, color);
476 
477     std::vector<point>::const_iterator  it = coords.begin();
478     std::vector<point>::const_iterator end = coords.end();
479 
480     gseg[scount++] = VG_MOVE_TO;
481     gdata[dcount++] = (float)(*it).x;
482     gdata[dcount++] = (float)(*it).y;
483     ++it;
484 
485     for (; it != end; ++it) {
486         gseg[scount++] = VG_LINE_TO;
487         gdata[dcount++] = (float)(*it).x;
488         gdata[dcount++] = (float)(*it).y;
489         if (scount >= MAX_SEG-1) {
490             vgAppendPathData(vg_path, scount, gseg, gdata);
491             scount = 0;
492             dcount = 0;
493         }
494     }
495 
496     if (scount > 0) {
497         vgAppendPathData(vg_path, scount, gseg, gdata);
498 
499     }
500 
501     vgSetf (VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND);
502     vgSetf (VG_STROKE_CAP_STYLE, VG_CAP_ROUND);
503     vgSetf (VG_STROKE_LINE_WIDTH, 20.0f);
504 
505     vgDrawPath (vg_path, VG_STROKE_PATH);
506     vgDestroyPath(vg_path);
507 }
508 
509 void
draw_poly(const std::vector<point> & corners,const rgba & fill,const rgba &,const SWFMatrix & mat,bool)510 Renderer_ovg::draw_poly(const std::vector<point>& corners,
511                         const rgba& fill, const rgba& /* outline */,
512                         const SWFMatrix& mat, bool /* masked */)
513 {
514     // GNASH_REPORT_FUNCTION;
515 
516     VGubyte     gseg[MAX_SEG];
517     VGfloat     gdata[MAX_SEG*3*2];
518     int         scount = 0;
519     int         dcount = 0;
520 
521     if (corners.empty()) {
522         return;
523     }
524 
525     eglScopeMatrix scope_mat(mat);
526 
527     VGfloat color[] = {
528         fill.m_r / 255.0f,
529         fill.m_g / 255.0f,
530         fill.m_b / 255.0f,
531         fill.m_a / 255.0f
532     };
533     VGPath      vg_path;
534     vg_path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
535                             VG_PATH_DATATYPE_F,
536                             1, 0, 0, 0,
537                             VG_PATH_CAPABILITY_ALL);
538     vgSetf (VG_FILL_RULE, VG_NON_ZERO );
539 
540     vgSetParameteri (_fillpaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
541     vgSetParameterfv (_fillpaint, VG_PAINT_COLOR, 4, color);
542 
543     const point *ptr = &corners.front();
544     gseg[scount++] = VG_MOVE_TO;
545     gdata[dcount++] = (float)ptr->x;
546     gdata[dcount++] = (float)ptr->y;
547     ptr++;
548 
549     for (size_t i = 1; i < corners.size(); i++) {
550         gseg[scount++] = VG_LINE_TO;
551         gdata[dcount++] = (float)ptr->x;
552         gdata[dcount++] = (float)ptr->y;
553         ptr++;
554         if (scount >= MAX_SEG-2) {
555             vgAppendPathData(vg_path, scount, gseg, gdata);
556             scount = 0;
557             dcount = 0;
558         }
559     }
560     gseg[scount++] = VG_CLOSE_PATH;
561     vgAppendPathData (vg_path, scount, gseg, gdata);
562 
563     vgDrawPath (vg_path, VG_FILL_PATH);
564     vgDestroyPath(vg_path);
565 }
566 
567 void
set_antialiased(bool)568 Renderer_ovg::set_antialiased(bool /* enable */)
569 {
570     log_unimpl(_("set_antialiased"));
571 }
572 
573 void
begin_submit_mask()574 Renderer_ovg::begin_submit_mask()
575 {
576     // GNASH_REPORT_FUNCTION;
577 
578     PathVec mask;
579     _masks.push_back(mask);
580     _drawing_mask = true;
581 }
582 
583 void
end_submit_mask()584 Renderer_ovg::end_submit_mask()
585 {
586     // GNASH_REPORT_FUNCTION;
587 
588     // If masking is disabled, rhen we can't use it
589     if (_drawing_mask == true) {
590         _drawing_mask = false;
591         apply_mask();
592     }
593 }
594 
595 /// Apply the current mask; nesting is supported.
596 ///
597 /// This method marks the stencil buffer by incrementing every stencil pixel
598 /// by one every time a solid from one of the current masks is drawn. When
599 /// all the mask solids are drawn, we change the stencil operation to permit
600 /// only drawing where all masks have drawn, in other words, where all masks
601 /// intersect, or in even other words, where the stencil pixel buffer equals
602 /// the number of masks.
603 void
apply_mask()604 Renderer_ovg::apply_mask()
605 {
606     // GNASH_REPORT_FUNCTION;
607 
608     if (_masks.empty()) {
609         return;
610     }
611 
612     float mat[9];
613     float omat[9];
614 
615     vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
616     vgLoadIdentity();
617     vgGetMatrix (omat);         // get the current matrix
618 
619     memset(mat, 0, sizeof(mat));
620     mat[0] =  stage_matrix.get_x_scale(); // scale sx
621     mat[1] =  0.0f; // shx
622     mat[3] =  0.0f; // shy
623     mat[4] =  -stage_matrix.get_x_scale(); // scale sy
624     mat[6] =  0;    // shift tx
625     mat[7] =  _display_height;   // shift ty
626     vgLoadMatrix(mat);
627     // Renderer_ovg::printVGMatrix(mat);
628 
629 #ifdef OPENVG_VERSION_1_1
630     vgMask(_mask_layer, VG_FILL_MASK, 0, 0, _display_width, _display_height);
631 #endif
632 // Call add_paths for each mask.
633     std::for_each(_masks.begin(), _masks.end(),
634                   std::bind(&Renderer_ovg::add_paths, this, _1));
635     vgSeti(VG_MASKING, VG_TRUE);
636 
637     vgLoadMatrix (omat);        // restore the current matrix
638 }
639 
640 void
disable_mask()641 Renderer_ovg::disable_mask()
642 {
643     // GNASH_REPORT_FUNCTION;
644 
645     // if (vgGeti(VG_MASKING) == VG_TRUE) {
646         _masks.pop_back();
647 
648         if (_masks.empty()) {
649             vgSeti (VG_MASKING, VG_FALSE);
650         } else {
651             apply_mask();
652         }
653     // }
654 }
655 
656 void
add_paths(const PathVec & path_vec)657 Renderer_ovg::add_paths(const PathVec& path_vec)
658 {
659     // GNASH_REPORT_FUNCTION;
660 
661     SWFCxForm dummy_cx;
662 
663     FillStyle coloring = FillStyle(SolidFill(rgba(0, 255, 0, 255)));
664 
665     draw_submask(path_vec, SWFMatrix(), dummy_cx, coloring);
666 }
667 
668 Path
reverse_path(const Path & cur_path)669 Renderer_ovg::reverse_path(const Path& cur_path)
670 {
671     // GNASH_REPORT_FUNCTION;
672 
673     const Edge& cur_end = cur_path.m_edges.back();
674 
675     float prev_cx = cur_end.cp.x;
676     float prev_cy = cur_end.cp.y;
677 
678     Path newpath(cur_end.ap.x, cur_end.ap.y, cur_path.m_fill1, cur_path.m_fill0, cur_path.m_line, cur_path.m_new_shape);
679 
680     float prev_ax = cur_end.ap.x;
681     float prev_ay = cur_end.ap.y;
682 
683     for (std::vector<Edge>::const_reverse_iterator it = cur_path.m_edges.rbegin()+1, end = cur_path.m_edges.rend(); it != end; ++it) {
684         const Edge& cur_edge = *it;
685 
686         if (prev_ax == prev_cx && prev_ay == prev_cy) {
687             prev_cx = cur_edge.ap.x;
688             prev_cy = cur_edge.ap.y;
689         }
690 
691         Edge newedge(prev_cx, prev_cy, cur_edge.ap.x, cur_edge.ap.y);
692 
693         newpath.m_edges.push_back(newedge);
694 
695         prev_cx = cur_edge.cp.x;
696         prev_cy = cur_edge.cp.y;
697         prev_ax = cur_edge.ap.x;
698         prev_ay = cur_edge.ap.y;
699 
700     }
701 
702     Edge newlastedge(prev_cx, prev_cy, cur_path.ap.x, cur_path.ap.y);
703     newpath.m_edges.push_back(newlastedge);
704 
705     return newpath;
706 }
707 
708 const Path *
find_connecting_path(const Path & to_connect,std::list<const Path * > path_refs)709 Renderer_ovg::find_connecting_path(const Path& to_connect,
710                                    std::list<const Path*> path_refs)
711 {
712     // GNASH_REPORT_FUNCTION;
713 
714     float target_x = to_connect.m_edges.back().ap.x;
715     float target_y = to_connect.m_edges.back().ap.y;
716 
717     if (target_x == to_connect.ap.x &&
718         target_y == to_connect.ap.y) {
719         return NULL;
720     }
721 
722     for (std::list<const Path*>::const_iterator it = path_refs.begin(),
723              end = path_refs.end(); it != end; ++it) {
724         const Path* cur_path = *it;
725 
726         if (cur_path == &to_connect) {
727             continue;
728         }
729 
730         if (cur_path->ap.x == target_x && cur_path->ap.y == target_y) {
731             if (cur_path->m_fill1 != to_connect.m_fill1) {
732                 continue;
733             }
734             return cur_path;
735         }
736     }
737 
738     return NULL;
739 }
740 
741 PathVec
normalize_paths(const PathVec & paths)742 Renderer_ovg::normalize_paths(const PathVec &paths)
743 {
744     // GNASH_REPORT_FUNCTION;
745 
746     PathVec normalized;
747 
748     for (PathVec::const_iterator it = paths.begin(), end = paths.end();
749          it != end; ++it) {
750         const Path& cur_path = *it;
751 
752         if (cur_path.m_edges.empty()) {
753             continue;
754 
755         } else if (cur_path.m_fill0 && cur_path.m_fill1) {
756 
757             // Two fill styles; duplicate and then reverse the left-filled one.
758             normalized.push_back(cur_path);
759             normalized.back().m_fill0 = 0;
760 
761             Path newpath = reverse_path(cur_path);
762             newpath.m_fill0 = 0;
763 
764             normalized.push_back(newpath);
765 
766         } else if (cur_path.m_fill0) {
767             // Left fill style.
768             Path newpath = reverse_path(cur_path);
769             newpath.m_fill0 = 0;
770 
771             normalized.push_back(newpath);
772         } else if (cur_path.m_fill1) {
773             // Right fill style.
774             normalized.push_back(cur_path);
775         } else {
776             // No fill styles; copy without modifying.
777             normalized.push_back(cur_path);
778         }
779 
780     }
781 
782     return normalized;
783 }
784 
785 
786 /// Analyzes a set of paths to detect real presence of fills and/or outlines
787 /// TODO: This should be something the character tells us and should be
788 /// cached.
789 void
analyze_paths(const PathVec & paths,bool & have_shape,bool & have_outline)790 Renderer_ovg::analyze_paths(const PathVec &paths, bool& have_shape,
791                             bool& have_outline)
792 {
793     // GNASH_REPORT_FUNCTION;
794 
795     have_shape = false;
796     have_outline = false;
797 
798     int pcount = paths.size();
799 
800     for (int pno= 0; pno<pcount; pno++) {
801 
802         const Path &the_path = paths[pno];
803 
804         // If a left or right fill is set, then this is an outline
805         if ((the_path.m_fill0 > 0) || (the_path.m_fill1 > 0)) {
806             have_shape = true;
807             if (have_outline) return; // have both
808         }
809 
810         // If a line is set, then it's a shape. A path can be both
811         if (the_path.m_line > 0) {
812             have_outline = true;
813             if (have_shape) return; // have both
814         }
815     }
816 }
817 
818 bool
apply_line_style(const LineStyle & style,const SWFCxForm & cx,const SWFMatrix & mat)819 Renderer_ovg::apply_line_style(const LineStyle& style, const SWFCxForm& cx,
820                                const SWFMatrix& mat)
821 {
822     // GNASH_REPORT_FUNCTION;
823 
824     bool rv = true;
825 
826     switch(style.joinStyle()) {
827       case JOIN_ROUND:
828           vgSetf (VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND);
829           break;
830       case JOIN_BEVEL:
831           vgSetf (VG_STROKE_JOIN_STYLE, VG_JOIN_BEVEL);
832           break;
833       case JOIN_MITER:
834           vgSetf (VG_STROKE_JOIN_STYLE, VG_JOIN_MITER);
835           break;
836       default:
837           log_unimpl(_("join style"));
838     }
839 
840     switch(style.startCapStyle()) {
841       case CAP_ROUND:
842           vgSetf (VG_STROKE_CAP_STYLE, VG_CAP_ROUND);
843           break;
844       case CAP_NONE:
845           vgSetf (VG_STROKE_CAP_STYLE, VG_CAP_BUTT);
846           break;
847       case CAP_SQUARE:
848           vgSetf (VG_STROKE_CAP_STYLE, VG_CAP_SQUARE);
849           break;
850       default:
851           log_unimpl(_("cap style"));
852     }
853 
854     vgSetf (VG_STROKE_MITER_LIMIT, style.miterLimitFactor());
855 
856     float width = style.getThickness();
857 
858     if (!width) {
859         vgSetf(VG_STROKE_LINE_WIDTH, 20.0f);
860         rv = false; // Don't draw rounded lines.
861     } else if ( (!style.scaleThicknessVertically()) && (!style.scaleThicknessHorizontally()) ) {
862         vgSetf(VG_STROKE_LINE_WIDTH, width);
863     } else {
864         if ( (!style.scaleThicknessVertically()) || (!style.scaleThicknessHorizontally()) ) {
865             LOG_ONCE( log_unimpl(_("Unidirectionally scaled strokes in OGL renderer")) );
866         }
867 
868         float stroke_scale = fabsf(mat.get_x_scale()) + fabsf(mat.get_y_scale());
869         stroke_scale /= 2.0f;
870         stroke_scale *= (stage_matrix.get_x_scale() + stage_matrix.get_y_scale()) / 2.0f;
871         width *= stroke_scale;
872 
873         if (width < 20.0f) {
874             width = 20.0f;
875         }
876 
877         vgSetf(VG_STROKE_LINE_WIDTH, width);
878     }
879 
880     rgba c = cx.transform(style.get_color());
881     VGfloat color[] = {
882         c.m_r / 255.0f,
883         c.m_g / 255.0f,
884         c.m_b / 255.0f,
885         c.m_a / 255.0f
886     };
887 
888 
889     vgSetParameteri (_strokepaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
890     vgSetParameterfv (_strokepaint, VG_PAINT_COLOR, 4, color);
891 
892     return rv;
893 }
894 
895 typedef std::vector<const Path*> PathPtrVec;
896 
897 void
draw_outlines(const PathVec & path_vec,const SWFMatrix & mat,const SWFCxForm & cx,const std::vector<LineStyle> & line_styles)898 Renderer_ovg::draw_outlines(const PathVec& path_vec, const SWFMatrix& mat,
899                             const SWFCxForm& cx, const std::vector<LineStyle>& line_styles)
900 {
901     // GNASH_REPORT_FUNCTION;
902 
903     for (PathVec::const_iterator it = path_vec.begin(), end = path_vec.end();
904          it != end; ++it) {
905 
906         const Path& cur_path = *it;
907 
908         if (!cur_path.m_line) {
909             continue;
910         }
911 
912         VGPath      vpath;
913         vpath = vgCreatePath (VG_PATH_FORMAT_STANDARD,
914                               VG_PATH_DATATYPE_F,
915                               1, 0, 0, 0,
916                               VG_PATH_CAPABILITY_ALL);
917         vgSetf (VG_FILL_RULE, VG_EVEN_ODD );
918         startpath(vpath, cur_path.ap.x, cur_path.ap.y);
919         preparepath(vpath, cur_path.m_edges, cur_path.ap.x,
920                     cur_path.ap.y);
921 
922         apply_line_style(line_styles[cur_path.m_line-1], cx, mat);
923         vgDrawPath (vpath, VG_STROKE_PATH);
924         vgDestroyPath(vpath);
925     }
926 }
927 
928 std::list<PathPtrVec>
get_contours(const PathPtrVec & paths)929 Renderer_ovg::get_contours(const PathPtrVec &paths)
930 {
931     // GNASH_REPORT_FUNCTION;
932 
933     std::list<const Path*> path_refs;
934     std::list<PathPtrVec> contours;
935 
936     for (PathPtrVec::const_iterator it = paths.begin(), end = paths.end();
937          it != end; ++it) {
938         const Path* cur_path = *it;
939         path_refs.push_back(cur_path);
940     }
941 
942     for (std::list<const Path*>::const_iterator it = path_refs.begin(), end = path_refs.end();
943          it != end; ++it) {
944         const Path* cur_path = *it;
945 
946         if (cur_path->m_edges.empty()) {
947             continue;
948         }
949 
950         if (!cur_path->m_fill0 && !cur_path->m_fill1) {
951             continue;
952         }
953 
954         PathPtrVec contour;
955 
956         contour.push_back(cur_path);
957 
958         const Path* connector = find_connecting_path(*cur_path, path_refs);
959 
960         while (connector) {
961             contour.push_back(connector);
962 
963             const Path* tmp = connector;
964             connector = find_connecting_path(*connector, std::list<const Path*>(std::next(it), end));
965 
966             // make sure we don't iterate over the connecting path in the for loop.
967             path_refs.remove(tmp);
968 
969         }
970 
971         contours.push_back(contour);
972     }
973 
974     return contours;
975 }
976 
977 void
draw_mask(const PathVec & path_vec)978 Renderer_ovg::draw_mask(const PathVec& path_vec)
979 {
980     // GNASH_REPORT_FUNCTION;
981 
982     if (_drawing_mask == true) {
983         for (PathVec::const_iterator it = path_vec.begin(), end = path_vec.end();
984              it != end; ++it) {
985             const Path& cur_path = *it;
986 
987             if (cur_path.m_fill0 || cur_path.m_fill1) {
988                 _masks.back().push_back(cur_path);
989                 _masks.back().back().m_line = 0;
990             }
991         }
992     }
993 }
994 
995 PathPtrVec
paths_by_style(const PathVec & path_vec,unsigned int style)996 Renderer_ovg::paths_by_style(const PathVec& path_vec, unsigned int style)
997 {
998     // GNASH_REPORT_FUNCTION;
999 
1000     PathPtrVec paths;
1001     for (PathVec::const_iterator it = path_vec.begin(), end = path_vec.end();
1002          it != end; ++it) {
1003         const Path& cur_path = *it;
1004 
1005         if (cur_path.m_fill0 == style) {
1006             paths.push_back(&cur_path);
1007         }
1008 
1009         if (cur_path.m_fill1 == style) {
1010             paths.push_back(&cur_path);
1011         }
1012 
1013     }
1014     return paths;
1015 }
1016 
1017 
1018 std::vector<PathVec::const_iterator>
find_subshapes(const PathVec & path_vec)1019 Renderer_ovg::find_subshapes(const PathVec& path_vec)
1020 {
1021     // GNASH_REPORT_FUNCTION;
1022 
1023     std::vector<PathVec::const_iterator> subshapes;
1024 
1025     PathVec::const_iterator it = path_vec.begin(),
1026         end = path_vec.end();
1027 
1028     subshapes.push_back(it);
1029     ++it;
1030 
1031     for (;it != end; ++it) {
1032         const Path& cur_path = *it;
1033 
1034         if (cur_path.m_new_shape) {
1035             subshapes.push_back(it);
1036         }
1037     }
1038 
1039     if (subshapes.back() != end) {
1040         subshapes.push_back(end);
1041     }
1042 
1043     return subshapes;
1044 }
1045 
1046 /// Takes a path and translates it using the given SWFMatrix.
1047 void
apply_matrix_to_paths(std::vector<Path> & paths,const SWFMatrix & mat)1048 Renderer_ovg::apply_matrix_to_paths(std::vector<Path>& paths, const SWFMatrix& mat)
1049 {
1050     // GNASH_REPORT_FUNCTION;
1051 
1052     std::for_each(paths.begin(), paths.end(),
1053                   std::bind(&Path::transform, _1, std::ref(mat)));
1054 }
1055 
1056 void
draw_subshape(const PathVec & path_vec,const SWFMatrix & mat,const SWFCxForm & cx,const std::vector<FillStyle> & fill_styles,const std::vector<LineStyle> & line_styles)1057 Renderer_ovg::draw_subshape(const PathVec& path_vec,
1058                             const SWFMatrix& mat,
1059                             const SWFCxForm& cx,
1060                             const std::vector<FillStyle>& fill_styles,
1061                             const std::vector<LineStyle>& line_styles)
1062 {
1063     // GNASH_REPORT_FUNCTION;
1064 
1065     PathVec normalized = normalize_paths(path_vec);
1066 
1067     for (size_t i = 0; i < fill_styles.size(); ++i) {
1068         PathPtrVec paths = paths_by_style(normalized, i+1);
1069 
1070         if (!paths.size()) {
1071             continue;
1072         }
1073 
1074         std::list<PathPtrVec> contours = get_contours(paths);
1075 
1076         VGPath      vg_path;
1077         vg_path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
1078                                 VG_PATH_DATATYPE_F,
1079                                 1, 0, 0, 0,
1080                                 VG_PATH_CAPABILITY_ALL);
1081         vgSetf (VG_FILL_RULE, VG_EVEN_ODD );
1082 
1083         for (std::list<PathPtrVec>::const_iterator iter = contours.begin(),
1084                  final = contours.end(); iter != final; ++iter) {
1085             const PathPtrVec& refs = *iter;
1086 
1087             startpath(vg_path, (*(refs[0])).ap.x, (*(refs[0])).ap.y);
1088 
1089             // Create a Linear or Radial gradient
1090             // All positions are specified in twips, which are 20 to the
1091             // pixel.
1092             const StyleHandler st(cx, _fillpaint,
1093                                   (*(refs[0])).ap.x/20, (*(refs[0])).ap.y/20);
1094             boost::apply_visitor(st, fill_styles[i].fill);
1095 
1096             for (PathPtrVec::const_iterator it = refs.begin(), end = refs.end();
1097                  it != end; ++it) {
1098                 const Path& cur_path = *(*it);
1099                 if (!cur_path.m_fill0 && !cur_path.m_fill1)
1100                     continue;
1101                 preparepath(vg_path, cur_path.m_edges, cur_path.ap.x,
1102                             cur_path.ap.y);
1103             }
1104             closepath(vg_path);
1105         }
1106         vgDrawPath (vg_path, VG_FILL_PATH);
1107         vgDestroyPath(vg_path);
1108     }
1109 
1110     draw_outlines(normalized, mat, cx, line_styles);
1111 }
1112 
1113 void
draw_submask(const PathVec & path_vec,const SWFMatrix &,const SWFCxForm &,const FillStyle &)1114 Renderer_ovg::draw_submask(const PathVec& path_vec,
1115                            const SWFMatrix& /* mat */,
1116                            const SWFCxForm& /* cx */,
1117                             const FillStyle& /* f_style */)
1118 {
1119     // GNASH_REPORT_FUNCTION;
1120 
1121     PathVec normalized = normalize_paths(path_vec);
1122 
1123     PathPtrVec paths = paths_by_style(normalized, 1);
1124 
1125     std::list<PathPtrVec> contours = get_contours(paths);
1126 
1127     VGfloat color[] = {1.0f, 1.0f, 1.0f, 1.0f};
1128 
1129     vgSetParameteri (_fillpaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
1130     vgSetParameterfv (_fillpaint, VG_PAINT_COLOR, 4, color);
1131 
1132 
1133     VGPath      vg_path;
1134     vg_path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
1135                             VG_PATH_DATATYPE_F,
1136                             1, 0, 0, 0,
1137                             VG_PATH_CAPABILITY_ALL);
1138     vgSetf (VG_FILL_RULE, VG_EVEN_ODD );
1139 
1140     for (std::list<PathPtrVec>::const_iterator iter = contours.begin(),
1141              final = contours.end(); iter != final; ++iter) {
1142         const PathPtrVec& refs = *iter;
1143         startpath(vg_path, (*(refs[0])).ap.x, (*(refs[0])).ap.y);
1144         for (PathPtrVec::const_iterator it = refs.begin(), end = refs.end();
1145              it != end; ++it) {
1146             const Path& cur_path = *(*it);
1147             preparepath(vg_path, cur_path.m_edges, cur_path.ap.x,
1148                         cur_path.ap.y);
1149         }
1150         closepath(vg_path);
1151     }
1152 
1153 #ifdef OPENVG_VERSION_1_1
1154     vgRenderToMask(vg_path, VG_FILL_PATH, VG_INTERSECT_MASK);
1155 #endif
1156     vgDestroyPath(vg_path);
1157 }
1158 
1159 // Drawing procedure:
1160 // 1. Separate paths by subshape.
1161 // 2. Separate subshapes by fill style.
1162 // 3. For every subshape/fill style combo:
1163 //  a. Separate contours: find closed shapes by connecting ends.
1164 //  b. Apply fill style.
1165 //  c. Feed the contours in the tesselator. (Render.)
1166 //  d. Draw outlines for every path in the subshape with a line style.
1167 void
drawShape(gnash::SWF::ShapeRecord const & shape,gnash::Transform const & xform)1168 Renderer_ovg::drawShape(gnash::SWF::ShapeRecord const &shape,
1169                         gnash::Transform const& xform)
1170 {
1171     // GNASH_REPORT_FUNCTION;
1172 
1173     const PathVec& path_vec = shape.paths();
1174 
1175     if (!path_vec.size()) {
1176         // No paths. Nothing to draw...
1177         return;
1178     }
1179     if (_drawing_mask) {
1180         PathVec scaled_path_vec = path_vec;
1181         apply_matrix_to_paths(scaled_path_vec, xform.matrix);
1182         draw_mask(scaled_path_vec);
1183         return;
1184     }
1185 
1186     bool have_shape, have_outline;
1187 
1188     analyze_paths(path_vec, have_shape, have_outline);
1189 
1190     if (!have_shape && !have_outline) {
1191         return;
1192     }
1193 
1194     eglScopeMatrix scope_mat(xform.matrix);
1195 
1196     std::vector<PathVec::const_iterator> subshapes = find_subshapes(path_vec);
1197 
1198     const std::vector<FillStyle>& fill_styles = shape.fillStyles();
1199     const std::vector<LineStyle>& line_styles = shape.lineStyles();
1200 
1201     for (size_t i = 0; i < subshapes.size()-1; ++i) {
1202         PathVec subshape_paths;
1203 
1204         if (subshapes[i] != subshapes[i+1]) {
1205             subshape_paths = PathVec(subshapes[i], subshapes[i+1]);
1206         } else {
1207             subshape_paths.push_back(*subshapes[i]);
1208         }
1209 
1210         draw_subshape(subshape_paths, xform.matrix, xform.colorTransform,
1211                       fill_styles, line_styles);
1212     }
1213 }
1214 
1215 void
drawGlyph(const SWF::ShapeRecord & rec,const rgba & c,const SWFMatrix & mat)1216 Renderer_ovg::drawGlyph(const SWF::ShapeRecord& rec, const rgba& c,
1217                         const SWFMatrix& mat)
1218 {
1219     // GNASH_REPORT_FUNCTION;
1220 
1221     if (_drawing_mask) {
1222         abort();
1223     }
1224 
1225     if (rec.getBounds().is_null()) {
1226         return;
1227     }
1228 
1229     SWFCxForm dummy_cx;
1230     std::vector<FillStyle> glyph_fs;
1231 
1232     FillStyle coloring = FillStyle(SolidFill(c));
1233 
1234     glyph_fs.push_back(coloring);
1235 
1236     std::vector<LineStyle> dummy_ls;
1237 
1238     eglScopeMatrix scope_mat(mat);
1239 
1240     draw_subshape(rec.paths(), mat, dummy_cx, glyph_fs, dummy_ls);
1241 
1242 }
1243 
1244 void
set_scale(float xscale,float yscale)1245 Renderer_ovg::set_scale(float xscale, float yscale)
1246 {
1247     // GNASH_REPORT_FUNCTION;
1248 
1249     _xscale = xscale;
1250     _yscale = yscale;
1251     stage_matrix.set_identity();
1252     stage_matrix.set_scale(xscale/20.0f, yscale/20.0f);
1253 }
1254 
1255 void
set_invalidated_regions(const InvalidatedRanges &)1256 Renderer_ovg::set_invalidated_regions(const InvalidatedRanges& /* ranges */)
1257 {
1258     // GNASH_REPORT_FUNCTION;
1259 
1260     // do nothing obviously. This method is required by the base class though,
1261     // so something has to be here.
1262 }
1263 
1264 DSOEXPORT Renderer *
create_handler(const char *)1265 create_handler(const char */* pixelformat */)
1266 {
1267     // GNASH_REPORT_FUNCTION;
1268 
1269     Renderer_ovg *renderer = new Renderer_ovg;
1270     return renderer;
1271 }
1272 
1273 
1274 // These methods are only for debugging and development
1275 void
printVGParams()1276 Renderer_ovg::printVGParams()
1277 {
1278     // vgGetVectorSize();
1279 
1280     std::string str;
1281     switch(vgGeti(VG_MATRIX_MODE)) {
1282       case VG_MATRIX_PATH_USER_TO_SURFACE:
1283           str = "VG_MATRIX_PATH_USER_TO_SURFACE";
1284           break;
1285       case VG_MATRIX_IMAGE_USER_TO_SURFACE:
1286           str = "VG_MATRIX_IMAGE_USER_TO_SURFACE";
1287           break;
1288       case VG_MATRIX_FILL_PAINT_TO_USER:
1289           str = "VG_MATRIX_FILL_PAINT_TO_USER";
1290           break;
1291       case VG_MATRIX_STROKE_PAINT_TO_USER:
1292           str = "VG_MATRIX_STROKE_PAINT_TO_USER";
1293           break;
1294 #ifdef VG_MATRIX_MODE_FORCE_SIZE
1295       case VG_MATRIX_MODE_FORCE_SIZE:
1296           str = "VG_MATRIX_MODE_FORCE_SIZE";
1297           break;
1298 #endif
1299       default:
1300           log_error(_("unsupported VG_MATRIX_MODE!"));
1301     }
1302     log_debug(_("VG_MATRIX_MODE is %s"), str);
1303     str.clear();
1304 
1305     switch(vgGeti(VG_FILL_RULE)) {
1306       case VG_EVEN_ODD:
1307           str = "VG_EVEN_ODD";
1308           break;
1309       case VG_NON_ZERO:
1310           str = "VG_NON_ZERO";
1311           break;
1312       default:
1313           log_error(_("unsupported VG_FILL_RULE!"));
1314     }
1315     log_debug(_("VG_FILL_RULE is %s"), str);
1316     str.clear();
1317 
1318     switch(vgGeti(VG_IMAGE_QUALITY)) {
1319       case VG_IMAGE_QUALITY_NONANTIALIASED:
1320           str = "VG_IMAGE_QUALITY_NONANTIALIASED";
1321           break;
1322       case VG_IMAGE_QUALITY_FASTER:
1323           str = "VG_IMAGE_QUALITY_FASTER";
1324           break;
1325       case VG_IMAGE_QUALITY_BETTER:
1326           str = "VG_IMAGE_QUALITY_BETTER";
1327           break;
1328 #ifdef VG_MATRIX_MODE_FORCE_SIZE
1329       case VG_IMAGE_QUALITY_FORCE_SIZE:
1330           str = "VG_IMAGE_QUALITY_FORCE_SIZE";
1331           break;
1332 #endif
1333       default:
1334           log_error(_("unsupported VG_IMAGE_QUALITY!"));
1335     }
1336     log_debug(_("VG_IMAGE_QUALITY is %s"), str);
1337     str.clear();
1338 
1339     switch(vgGeti(VG_RENDERING_QUALITY)) {
1340       case VG_RENDERING_QUALITY_NONANTIALIASED:
1341           str = "VG_RENDERING_QUALITY_NONANTIALIASED";
1342           break;
1343       case VG_RENDERING_QUALITY_FASTER:
1344           str = "VG_RENDERING_QUALITY_FASTER";
1345           break;
1346       case VG_RENDERING_QUALITY_BETTER:
1347           str = "VG_RENDERING_QUALITY_BETTER";
1348           break;
1349 #ifdef VG_MATRIX_MODE_FORCE_SIZE
1350       case VG_RENDERING_QUALITY_FORCE_SIZE:
1351           str = "VG_RENDERING_QUALITY_FORCE_SIZE";
1352           break;
1353 #endif
1354       default:
1355           log_error(_("unsupported VG_RENDERING_QUALITY!"));
1356     }
1357     log_debug(_("VG_RENDERING_QUALITY is %s"), str);
1358     str.clear();
1359 
1360     switch(vgGeti(VG_BLEND_MODE)) {
1361       case VG_BLEND_SRC:
1362           str = "VG_BLEND_SRC";
1363           break;
1364       case VG_BLEND_SRC_OVER:
1365           str = "VG_BLEND_SRC_OVER";
1366           break;
1367       case VG_BLEND_DST_OVER:
1368           str = "VG_BLEND_DST_OVER";
1369           break;
1370       case VG_BLEND_SRC_IN:
1371           str = "VG_BLEND_SRC_IN";
1372           break;
1373       case VG_BLEND_DST_IN:
1374           str = "VG_BLEND_DST_IN";
1375           break;
1376       case VG_BLEND_MULTIPLY:
1377           str = "VG_BLEND_MULTIPLY";
1378           break;
1379       case VG_BLEND_SCREEN:
1380           str = "VG_BLEND_SCREEN";
1381           break;
1382       case VG_BLEND_DARKEN:
1383           str = "VG_BLEND_DARKEN";
1384           break;
1385       case VG_BLEND_LIGHTEN:
1386           str = "VG_BLEND_LIGHTEN";
1387           break;
1388       case VG_BLEND_ADDITIVE:
1389           str = "VG_BLEND_ADDITIVE";
1390           break;
1391       default:
1392           log_error(_("unsupported VG_BLEND_MODE!"));
1393     }
1394     log_debug(_("VG_BLEND_MODE is %s"), str);
1395     str.clear();
1396 
1397     switch(vgGeti(VG_IMAGE_MODE)) {
1398       case VG_DRAW_IMAGE_NORMAL:
1399           str = "VG_DRAW_IMAGE_MULTIPLY";
1400           break;
1401       case VG_DRAW_IMAGE_MULTIPLY:
1402           str = "VG_DRAW_IMAGE_MULTIPLY";
1403           break;
1404       case VG_DRAW_IMAGE_STENCIL:
1405           str = "VG_DRAW_IMAGE_STENCIL";
1406           break;
1407 #ifdef VG_MATRIX_MODE_FORCE_SIZE
1408       case VG_IMAGE_MODE_FORCE_SIZE:
1409           str = "VG_IMAGE_MODE_FORCE_SIZE";
1410           break;
1411 #endif
1412       default:
1413           log_error(_("unsupported VG_IMAGE_MODE!"));
1414     }
1415     log_debug(_("VG_IMAGE_MODE is %s"), str);
1416     str.clear();
1417 
1418     log_debug(_("VG_STROKE_LINE_WIDTH is %d"), vgGeti(VG_STROKE_LINE_WIDTH));
1419     str.clear();
1420 
1421     switch(vgGeti(VG_STROKE_CAP_STYLE)) {
1422       case VG_CAP_BUTT:
1423           str = "VG_CAP_BUTT";
1424           break;
1425       case VG_CAP_ROUND:
1426           str = "VG_CAP_ROUND";
1427           break;
1428       case VG_CAP_SQUARE:
1429           str = "VG_CAP_SQUARE";
1430           break;
1431 #ifdef VG_MATRIX_MODE_FORCE_SIZE
1432       case VG_CAP_STYLE_FORCE_SIZE:
1433           str = "VG_CAP_STYLE_FORCE_SIZE";
1434           break;
1435 #endif
1436       default:
1437           log_error(_("unsupported VG_STROKE_CAP_STYLE!"));
1438     }
1439     log_debug(_("VG_STROKE_CAP_STYLE is %s"), str);
1440     str.clear();
1441 
1442     switch(vgGeti(VG_STROKE_JOIN_STYLE)) {
1443       case VG_JOIN_MITER:
1444           str = "VG_JOIN_MITER";
1445           break;
1446       case VG_JOIN_ROUND:
1447           str = "VG_JOIN_ROUND";
1448           break;
1449       case VG_JOIN_BEVEL:
1450           str = "VG_JOIN_BEVEL";
1451           break;
1452 #ifdef VG_MATRIX_MODE_FORCE_SIZE
1453       case VG_JOIN_STYLE_FORCE_SIZE:
1454           str = "VG_JOIN_STYLE_FORCE_SIZE";
1455           break;
1456 #endif
1457       default:
1458           log_error(_("unsupported VG_STROKE_JOIN_STYLE!"));
1459     }
1460     log_debug(_("VG_STROKE_JOIN_STYLE is %s"), str);
1461     str.clear();
1462 
1463     log_debug(_("VG_STROKE_MITER_LIMIT is %d"), vgGeti(VG_STROKE_MITER_LIMIT));
1464     log_debug(_("VG_MASKING is %d"), vgGeti(VG_MASKING));
1465     log_debug(_("VG_SCISSORING is %d"), vgGeti(VG_SCISSORING));
1466     str.clear();
1467 
1468     switch(vgGeti(VG_PIXEL_LAYOUT)) {
1469       case VG_PIXEL_LAYOUT_UNKNOWN:
1470           str = "VG_PIXEL_LAYOUT_UNKNOWN";
1471           break;
1472       case VG_PIXEL_LAYOUT_RGB_VERTICAL:
1473           str = "VG_PIXEL_LAYOUT_RGB_VERTICAL";
1474           break;
1475       case VG_PIXEL_LAYOUT_BGR_VERTICAL:
1476           str = "VG_PIXEL_LAYOUT_BGR_VERTICAL";
1477           break;
1478       case VG_PIXEL_LAYOUT_RGB_HORIZONTAL:
1479           str = "VG_PIXEL_LAYOUT_RGB_HORIZONTAL";
1480           break;
1481       case VG_PIXEL_LAYOUT_BGR_HORIZONTAL:
1482           str = "VG_PIXEL_LAYOUT_BGR_HORIZONTAL";
1483           break;
1484 #ifdef VG_MATRIX_MODE_FORCE_SIZE
1485       case VG_PIXEL_LAYOUT_FORCE_SIZE:
1486           str = "VG_PIXEL_LAYOUT_FORCE_SIZE";
1487           break;
1488 #endif
1489       default:
1490           log_error(_("unsupported VG_PIXEL_LAYOUT!"));
1491     }
1492     log_debug(_("VG_PIXEL_LAYOUT is %s"), str);
1493 
1494     log_debug(_("VG_STROKE_DASH_PHASE_RESET is %s"),
1495               (vgGeti(VG_STROKE_DASH_PHASE_RESET) == true) ? "true" : "false");
1496     log_debug(_("VG_FILTER_FORMAT_LINEAR is %s"),
1497               (vgGeti(VG_FILTER_FORMAT_LINEAR) == true) ? "true" : "false");
1498     log_debug(_("VG_FILTER_FORMAT_PREMULTIPLIED is %s"),
1499               (vgGeti(VG_FILTER_FORMAT_PREMULTIPLIED) == true) ? "true" : "false");
1500     str.clear();
1501 
1502     VGint value = vgGeti(VG_FILTER_CHANNEL_MASK);
1503     if (value & VG_RED) {
1504         str += " VG_RED";
1505     }
1506     if (value & VG_GREEN) {
1507         str += " VG_GREEN";
1508     }
1509     if (value & VG_BLUE) {
1510         str += " VG_BLUE";
1511     }
1512     if (value & VG_ALPHA) {
1513         str += " VG_ALPHA";
1514     }
1515     log_debug(_("VG_FILTER_CHANNEL_MASK is %s"), str);
1516 
1517     log_debug(_("VG_MAX_IMAGE_WIDTH is %d"), vgGeti(VG_MAX_IMAGE_WIDTH));
1518     log_debug(_("VG_MAX_IMAGE_HEIGHT is %d"), vgGeti(VG_MAX_IMAGE_HEIGHT));
1519     log_debug(_("VG_MAX_IMAGE_PIXELS is %d"), vgGeti(VG_MAX_IMAGE_PIXELS));
1520     log_debug(_("VG_MAX_IMAGE_BYTES is %d"), vgGeti(VG_MAX_IMAGE_BYTES));
1521 
1522 }
1523 
1524 void
printVGPath(VGPath path)1525 Renderer_ovg::printVGPath(VGPath path)
1526 {
1527     log_debug(_("VG_PATH_FORMAT is %d"), vgGetParameteri(path, VG_PATH_FORMAT));
1528     log_debug(_("VG_PATH_DATATYPE is %d"), vgGetParameteri(path, VG_PATH_DATATYPE));
1529     log_debug(_("VG_PATH_CAPABILITY_APPEND_TO is %d"), vgGetParameteri(path, VG_PATH_CAPABILITY_APPEND_TO));
1530     log_debug(_("VG_PATH_SCALE is %g"), vgGetParameteri(path, VG_PATH_SCALE));
1531     log_debug(_("VG_PATH_BIA is %g"), vgGetParameteri(path, VG_PATH_BIAS));
1532 
1533     log_debug(_("VG_PATH_NUM_SEGMENTS is %d"), vgGetParameteri(path, VG_PATH_NUM_SEGMENTS));
1534     log_debug(_("VG_PATH_NUM_COORDS is %d"), vgGetParameteri(path, VG_PATH_NUM_COORDS));
1535 }
1536 
1537 // Print an OpenVG matric, which is 3 x 3. Elements 2 and 5 are
1538 // ignored, as they are the w0 and w1 paramaters.
1539 // It looks like this: { sx, shy, w0, shx, sy, w1, tx, ty, w2 }
1540 void
printVGMatrix(VGfloat * mat)1541 Renderer_ovg::printVGMatrix(VGfloat *mat)
1542 {
1543     std::cerr << "sx, shx, tx: " << mat[0] << ", " << mat[1]<< ", "
1544               << std::fixed << mat[3] << std::endl;
1545     std::cerr << "sy, shy, ty: " << mat[4]<< ", " << mat[6] << ", "
1546               << std::scientific << mat[7] << std::endl;
1547 }
1548 
1549 void
printVGMatrix(const SWFMatrix & mat)1550 Renderer_ovg::printVGMatrix(const SWFMatrix &mat)
1551 {
1552     std::cerr << "a, shx, tx: " << mat.a() << ", " << mat.b() << ", " << mat.tx() << std::endl;
1553     std::cerr << "sy, shy, ty: " << mat.c() << ", " << mat.d() << ", " << mat.ty() << std::endl;
1554 }
1555 
1556 Renderer *
startInternalRender(gnash::image::GnashImage &)1557 Renderer_ovg::startInternalRender(gnash::image::GnashImage&)
1558 {
1559     // GNASH_REPORT_FUNCTION;
1560 
1561     return 0;
1562 }
1563 
1564 void
endInternalRender()1565 Renderer_ovg::endInternalRender()
1566 {
1567     // GNASH_REPORT_FUNCTION;
1568 }
1569 
1570 void
drawVideoFrame(gnash::image::GnashImage *,gnash::Transform const &,gnash::SWFRect const *,bool)1571 Renderer_ovg::drawVideoFrame(gnash::image::GnashImage*, gnash::Transform const&, gnash::SWFRect const*, bool)
1572 {
1573     GNASH_REPORT_FUNCTION;
1574 }
1575 
1576 unsigned int
getBitsPerPixel()1577 Renderer_ovg::getBitsPerPixel()
1578 {
1579     return 0;
1580 }
1581 
1582 const char *
getErrorString(VGErrorCode error)1583 Renderer_ovg::getErrorString(VGErrorCode error)
1584 {
1585     switch (error) {
1586     case VG_NO_ERROR:
1587         return "No Error";
1588         break;
1589     case VG_BAD_HANDLE_ERROR:
1590         return "Bad Handle";
1591         break;
1592     case VG_ILLEGAL_ARGUMENT_ERROR:
1593         return "Illegal Argument";
1594         break;
1595     case VG_OUT_OF_MEMORY_ERROR:
1596         return "Our Of Memory";
1597         break;
1598     case VG_PATH_CAPABILITY_ERROR:
1599         return "Path Capability";
1600         break;
1601     case VG_UNSUPPORTED_IMAGE_FORMAT_ERROR:
1602         return "Unsupported Image Format";
1603         break;
1604     case VG_UNSUPPORTED_PATH_FORMAT_ERROR:
1605         return "Unsupported Path Format";
1606         break;
1607     case VG_IMAGE_IN_USE_ERROR:
1608         return "VG Image In Use";
1609        break;
1610     case VG_NO_CONTEXT_ERROR:
1611         return "No COntext";
1612         break;
1613 #ifdef VG_ERROR_CODE_FORCE_SIZE
1614     case VG_ERROR_CODE_FORCE_SIZE:
1615         return "Code Force Size";
1616         break;
1617 #endif
1618     default:
1619         return "Unknown error";
1620         break;
1621     }
1622 }
1623 
1624 } // namespace gnash::renderer::gles1
1625 } // namespace gnash::renderer
1626 } // namespace gnash
1627 
1628 // local Variables:
1629 // mode: C++
1630 // indent-tabs-mode: nil
1631 // End:
1632