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 // Original version by Udo Giacomozzi and Hannes Mayr,
20 // INDUNET GmbH (www.indunet.it)
21 
22 
23 /// A Renderer that uses the Anti-Grain Geometry Toolkit (antigrain.com)
24 /// and renders directly to a buffer (for example to the framebuffer). This
25 /// backend is *completely* independent of any hardware. It can be used for
26 /// rendering to the Linux FrameBuffer device, or be blitted inside a
27 /// window (regardless of what operating system). It should also be no problem
28 /// to render into a file...
29 /// This file uses *very* heavily templates and is optimized mainly for speed,
30 /// meaning that the compiler generates very, very, very much specialized
31 /// code. That's good for speed but bloats up the resulting machine code.
32 
33 /*
34 
35 Status
36 ------
37 
38   outlines:
39     solid             COMPLETE
40     patterns          don't exist (they're converted at compile time by Flash!)
41     widths            COMPLETE
42     colors, alpha     COMPLETE
43     cap styles        DONE, but end cap style is ignored
44     join styles       COMPLETE
45     no-close flag     COMPLETE
46 
47 
48   fills:
49     solid fills       COMPLETE
50     linear gradients  COMPLETE
51     radial gradients  COMPLETE
52     focal gradients   COMPLETE
53     ext. spread modes COMPLETE
54     linear RGB mode   COMPLETE
55     bitmaps, tiled    COMPLETE
56     bitmaps, clipped  COMPLETE
57     bitmaps, smooth   COMPLETE
58     bitmaps, hard     COMPLETE
59     color xform       COMPLETE
60 
61   fonts               COMPLETE
62 
63   masks               COMPLETE
64 
65   caching             NONE IMPLEMENTED
66 
67   video               COMPLETE
68 
69   Currently the renderer should be able to render everything correctly.
70 
71 
72 What could and should be /optimized/
73 ------------------------------------
74 
75   - EASY: Do not even start rendering shapes that are abviously out of the
76     invalidated bounds!
77 
78   - The alpha mask buffers (masks) are allocated and freed for each mask which
79     results in many large-size buffer allocations during a second. Maybe this
80     should be optimized.
81 
82   - Matrix-transformed paths (generated before drawing a shape) should be cached
83     and re-used to avoid recalculation of the same coordinates.
84 
85   - Characters (or sprites) may be cached as bitmaps with alpha channel (RGBA).
86     The mechanism could be automatically activated when the same character is
87     being rendered the 3rd or 5th time in a row with the same transformations
88     (regardless of the instance itself!). It's not a good idea to always
89     render into a bitmap buffer because this eats up memory and adds an
90     additional pass in rendering (blitting the bitmap buffer). This may be
91     tricky to implement anyway.
92 
93   - Masks are a very good candidate for bitmap caching as they do not change
94     that often. With other words, the alpha mask should not be discarded after
95     rendering and should be reused if possible.
96 
97   - there are also a few TODO comments in the code!
98 
99 
100 AGG resources
101 -------------
102   http://www.antigrain.com/
103   http://haiku-os.org/node/86
104 
105 */
106 
107 #ifdef HAVE_CONFIG_H
108 #include "gnashconfig.h"
109 #endif
110 
111 #include "Renderer_agg.h"
112 
113 #include <vector>
114 #include <cmath>
115 #include <math.h> // We use round()!
116 #include <climits>
117 #include <functional>
118 
119 #pragma GCC diagnostic push
120 #pragma GCC diagnostic ignored "-Wunused-parameter"
121 #pragma GCC diagnostic ignored "-Wsign-compare"
122 #include <agg_rendering_buffer.h>
123 #include <agg_renderer_base.h>
124 #include <agg_pixfmt_gray.h>
125 #include <agg_pixfmt_rgb_packed.h>
126 #include <agg_color_rgba.h>
127 #include <agg_color_gray.h>
128 #include <agg_ellipse.h>
129 #include <agg_conv_transform.h>
130 #include <agg_trans_affine.h>
131 #include <agg_scanline_u.h>
132 #include <agg_scanline_bin.h>
133 #include <agg_scanline_p.h>
134 #include <agg_renderer_scanline.h>
135 #include <agg_rasterizer_scanline_aa.h>
136 #include <agg_rasterizer_compound_aa.h>
137 #include <agg_span_allocator.h>
138 #include <agg_path_storage.h>
139 #include <agg_conv_curve.h>
140 #include <agg_conv_stroke.h>
141 #include <agg_renderer_primitives.h>
142 #include <agg_image_accessors.h>
143 #include <agg_alpha_mask_u8.h>
144 #pragma GCC diagnostic pop
145 
146 #include "Renderer_agg_style.h"
147 
148 #include "GnashEnums.h"
149 #include "CachedBitmap.h"
150 #include "RGBA.h"
151 #include "GnashImage.h"
152 #include "log.h"
153 #include "Range2d.h"
154 #include "swf/ShapeRecord.h"
155 #include "GnashNumeric.h"
156 #include "SWFCxForm.h"
157 #include "FillStyle.h"
158 #include "Transform.h"
159 #include "IOChannel.h"
160 
161 #ifdef HAVE_VA_VA_H
162 #include "GnashVaapiImage.h"
163 #include "GnashVaapiImageProxy.h"
164 #endif
165 
166 #include "Renderer_agg_bitmap.h"
167 
168 // Print a debugging warning when rendering of a whole character
169 // is skipped
170 //#define GNASH_WARN_WHOLE_CHARACTER_SKIP
171 
172 
173 #include <boost/numeric/conversion/converter.hpp>
174 #include <boost/ptr_container/ptr_vector.hpp>
175 
176 namespace gnash {
177 
178 namespace {
179 
180 class AlphaMask;
181 
182 typedef std::vector<agg::path_storage> AggPaths;
183 typedef std::vector<geometry::Range2d<int> > ClipBounds;
184 typedef boost::ptr_vector<AlphaMask> AlphaMasks;
185 typedef std::vector<Path> GnashPaths;
186 
187 // Note: this is here in case ::round doesn't exist. However, it's not
188 // advisable to check using ifdefs (as previously), because ::round is
189 // generally a function not a macro!
190 template<typename T>
round(T t)191 T round(T t)
192 {
193     return ::round(t);
194 }
195 
196 template <class Rasterizer>
applyClipBox(Rasterizer & ras,const geometry::Range2d<int> & bounds)197 inline void applyClipBox(Rasterizer& ras, const geometry::Range2d<int>& bounds)
198 {
199     assert(bounds.isFinite());
200     ras.clip_box(static_cast<double>(bounds.getMinX()),
201             static_cast<double>(bounds.getMinY()),
202             static_cast<double>(bounds.getMaxX() + 1),
203             static_cast<double>(bounds.getMaxY() + 1)
204             );
205 }
206 
207 /// Analyzes a set of paths to detect real presence of fills and/or outlines
208 /// TODO: This should be something the character tells us and should be
209 /// cached.
210 void
analyzePaths(const GnashPaths & paths,bool & have_shape,bool & have_outline)211 analyzePaths(const GnashPaths &paths, bool& have_shape,
212     bool& have_outline)
213 {
214 
215     have_shape = false;
216     have_outline = false;
217 
218     const int pcount = paths.size();
219 
220     for (int pno=0; pno<pcount; ++pno) {
221 
222         const Path &the_path = paths[pno];
223 
224         if ((the_path.m_fill0 > 0) || (the_path.m_fill1 > 0)) {
225             have_shape = true;
226             if (have_outline) return; // have both
227         }
228 
229         if (the_path.m_line > 0) {
230             have_outline = true;
231             if (have_shape) return; // have both
232         }
233     }
234 }
235 
236 class EdgeToPath
237 {
238 
239 public:
EdgeToPath(AggPaths::value_type & path,double shift=0)240     EdgeToPath(AggPaths::value_type& path, double shift = 0)
241         :
242         _path(path),
243         _shift(shift)
244     {}
245 
operator ()(const Edge & edge)246     void operator()(const Edge& edge)
247     {
248         if (edge.straight()) {
249             _path.line_to(twipsToPixels(edge.ap.x) + _shift,
250                           twipsToPixels(edge.ap.y) + _shift);
251         }
252         else {
253             _path.curve3(twipsToPixels(edge.cp.x) + _shift,
254                      twipsToPixels(edge.cp.y) + _shift,
255                      twipsToPixels(edge.ap.x) + _shift,
256                      twipsToPixels(edge.ap.y) + _shift);
257         }
258     }
259 
260 private:
261     agg::path_storage& _path;
262     const double _shift;
263 };
264 
265 /// In-place transformation of Gnash paths to AGG paths.
266 class GnashToAggPath
267 {
268 public:
269 
GnashToAggPath(AggPaths & dest,double shift=0)270     GnashToAggPath(AggPaths& dest, double shift = 0)
271         :
272         _dest(dest),
273         _it(_dest.begin()),
274         _shift(shift)
275     {
276     }
277 
operator ()(const Path & in)278     void operator()(const Path& in)
279     {
280         agg::path_storage& p = *_it;
281 
282         p.move_to(twipsToPixels(in.ap.x) + _shift,
283                   twipsToPixels(in.ap.y) + _shift);
284 
285         std::for_each(in.m_edges.begin(), in.m_edges.end(),
286                 EdgeToPath(p, _shift));
287         ++_it;
288     }
289 
290 private:
291     AggPaths& _dest;
292     AggPaths::iterator _it;
293     const double _shift;
294 
295 };
296 
297 
298 /// Transposes Gnash paths to AGG paths, which can be used for both outlines
299 /// and shapes. Subshapes are ignored (ie. all paths are converted). Converts
300 /// TWIPS to pixels on the fly.
301 inline void
buildPaths(AggPaths & dest,const GnashPaths & paths)302 buildPaths(AggPaths& dest, const GnashPaths& paths)
303 {
304     dest.resize(paths.size());
305     std::for_each(paths.begin(), paths.end(), GnashToAggPath(dest, 0.05));
306 }
307 
308 // --- ALPHA MASK BUFFER CONTAINER ---------------------------------------------
309 // How masks are implemented: A mask is basically a full alpha buffer. Each
310 // pixel in the alpha buffer defines the fraction of color values that are
311 // copied to the main buffer. The alpha mask buffer has 256 alpha levels per
312 // pixel, which is good as it allows anti-aliased masks. A full size buffer
313 // is allocated for each mask even if the invalidated bounds may be much
314 // smaller. The advantage of this is that the alpha mask adaptor does not need
315 // to do any clipping which results in better performance.
316 // Masks can be nested, which means the intersection of all masks should be
317 // visible (logical AND). To allow this we hold a stack of alpha masks and the
318 // topmost mask is used itself as a mask to draw any new mask. When rendering
319 // visible shapes only the topmost mask must be used and when a mask should not
320 // be used anymore it's simply discarded so that the next mask becomes active
321 // again.
322 // To be exact, Flash is a bit restrictive regarding to what can be a mask
323 // (dynamic text, shapes, ...) but our renderer can build a mask from
324 // anything we can draw otherwise (except lines, which are excluded
325 // explicitly).
326 
327 class AlphaMask
328 {
329 
330     typedef agg::renderer_base<agg::pixfmt_gray8> Renderer;
331     typedef agg::alpha_mask_gray8 Mask;
332 
333 public:
334 
AlphaMask(int width,int height)335     AlphaMask(int width, int height)
336         :
337         _rbuf(nullptr, width, height, width),
338         _pixf(_rbuf),
339         _rbase(_pixf),
340         _amask(_rbuf),
341         _buffer(new std::uint8_t[width * height]())
342     {
343         _rbuf.attach(_buffer.get(), width, height, width);
344     }
345 
clear(const geometry::Range2d<int> & region)346     void clear(const geometry::Range2d<int>& region)
347     {
348         if (region.isNull()) return;
349         assert(region.isFinite());
350 
351         const agg::gray8 black(0);
352 
353         // region can't be world as it should be intersected with
354         // the visible SWFRect
355         assert(!region.isWorld());
356 
357         unsigned int left = region.getMinX();
358         unsigned int width = region.width() + 1;
359 
360         const unsigned int max_y = region.getMaxY();
361         for (unsigned int y=region.getMinY(); y <= max_y; ++y)
362         {
363              _pixf.copy_hline(left, y, width, black);
364         }
365     }
366 
get_rbase()367     Renderer& get_rbase() {
368         return _rbase;
369     }
370 
getMask() const371     const Mask& getMask() const {
372         return _amask;
373     }
374 
375 private:
376 
377     // agg class to access the buffer
378     agg::rendering_buffer _rbuf;
379 
380     // pixel access
381     agg::pixfmt_gray8 _pixf;
382 
383     // renderer base
384     Renderer _rbase;
385 
386     // alpha mask
387     Mask _amask;
388 
389     // in-memory buffer
390     std::unique_ptr<std::uint8_t[]> _buffer;
391 
392 };
393 
394 /// Class for rendering lines.
395 template<typename PixelFormat>
396 class LineRenderer
397 {
398 public:
399     typedef agg::renderer_base<PixelFormat> BaseRenderer;
400     typedef agg::renderer_scanline_aa_solid<BaseRenderer> Renderer;
401     typedef agg::rasterizer_scanline_aa<> Rasterizer;
402     typedef agg::conv_stroke<agg::path_storage> Stroke;
403 
LineRenderer(const ClipBounds & clipbounds,BaseRenderer & baseRenderer)404     LineRenderer(const ClipBounds& clipbounds, BaseRenderer& baseRenderer)
405         :
406         _clipbounds(clipbounds),
407         _renderer(baseRenderer)
408     {}
409 
410     template<typename ScanLine>
render(ScanLine & sl,Stroke & stroke,const rgba & color)411     void render(ScanLine& sl, Stroke& stroke, const rgba& color)
412     {
413         for (const auto& bounds : _clipbounds) {
414 
415             applyClipBox<Rasterizer> (_ras, bounds);
416 
417             // The vectorial pipeline
418             _ras.add_path(stroke);
419 
420             // Set the color and render the scanlines
421             _renderer.color(agg::rgba8_pre(color.m_r, color.m_g,
422                         color.m_b, color.m_a));
423 
424             agg::render_scanlines(_ras, sl, _renderer);
425 
426         }
427     }
428 
429 private:
430 
431     const ClipBounds& _clipbounds;
432     Rasterizer _ras;
433     Renderer _renderer;
434 
435 };
436 
437 /// Class for rendering empty video frames.
438 //
439 /// Templated functions are used to allow using different types,
440 /// particularly for high and low quality rendering.
441 //
442 /// At present, this is bound to the renderer's ClipBounds. In future it
443 /// may be useful to pass BlendMode, Cxform, and custom clipbounds as well
444 /// as a caller-provided rendering buffer. This also applies to the
445 /// rest of the renderer API.
446 //
447 /// @param PixelFormat The format to render to.
448 template <typename PixelFormat>
449 class EmptyVideoRenderer
450 {
451 
452 public:
453 
454     /// Render the pixels using this renderer
455     typedef typename agg::renderer_base<PixelFormat> Renderer;
456     typedef agg::rasterizer_scanline_aa<> Rasterizer;
457 
EmptyVideoRenderer(const ClipBounds & clipbounds)458     EmptyVideoRenderer(const ClipBounds& clipbounds)
459         :
460         _clipbounds(clipbounds)
461     {}
462 
render(agg::path_storage & path,Renderer & rbase,const AlphaMasks & masks)463     void render(agg::path_storage& path, Renderer& rbase,
464         const AlphaMasks& masks)
465     {
466         if (masks.empty()) {
467             // No mask active
468             agg::scanline_p8 sl;
469             renderScanlines(path, rbase, sl);
470         }
471         else {
472             // Untested.
473             typedef agg::scanline_u8_am<agg::alpha_mask_gray8> Scanline;
474             Scanline sl(masks.back().getMask());
475             renderScanlines(path, rbase, sl);
476         }
477     }
478 
479 private:
480 
481     template<typename Scanline>
renderScanlines(agg::path_storage & path,Renderer & rbase,Scanline & sl)482     void renderScanlines(agg::path_storage& path, Renderer& rbase,
483             Scanline& sl)
484     {
485         Rasterizer ras;
486         agg::renderer_scanline_aa_solid<Renderer> ren_sl(rbase);
487 
488         for (const auto& cb : _clipbounds)
489         {
490             applyClipBox<Rasterizer>(ras, cb);
491             ras.add_path(path);
492 
493             const agg::rgba8 col(agg::argb8_packed(0));
494             ren_sl.color(col);
495 
496             // we don't want premultiplied alpha (so, don't use blend functions)
497             //agg::render_scanlines(ras, sl, ren_sl);
498             if (ras.rewind_scanlines()) {
499                 sl.reset(ras.min_x(), ras.max_x());
500                 ren_sl.prepare();
501                 while (ras.sweep_scanline(sl)) {
502                     //ren_sl.render(sl);
503                     const int y = sl.y();
504                     unsigned int num_spans = sl.num_spans();
505                     typename Scanline::const_iterator span = sl.begin();
506 
507                     for (;;) {
508                         const int x = span->x;
509                         assert(span->len > 0); // XXX: check span->len < 0 case!
510                         if (span->len > 0)
511                             rbase.copy_hline(x, y, (unsigned)span->len, col);
512                         else
513                             rbase.copy_hline(x, y,
514                                              (unsigned)(x - span->len - 1),
515                                              col);
516                         if (--num_spans == 0)
517                             break;
518                         ++span;
519                     }
520                 }
521             }
522         }
523     }
524 
525     const ClipBounds& _clipbounds;
526 };
527 
528 /// Class for rendering video frames.
529 //
530 /// Templated functions are used to allow using different types,
531 /// particularly for high and low quality rendering.
532 //
533 /// At present, this is bound to the renderer's ClipBounds. In future it
534 /// may be useful to pass BlendMode, Cxform, and custom clipbounds as well
535 /// as a caller-provided rendering buffer. This also applies to the
536 /// rest of the renderer API.
537 //
538 /// @param SourceFormat     The format of the video frame to be rendered
539 /// @param PixelFormat      The format to render to.
540 template <typename PixelFormat, typename SourceFormat = agg::pixfmt_rgb24_pre>
541 class VideoRenderer
542 {
543 
544 public:
545 
546     /// Fixed types for video frame rendering.
547 
548     /// Render the pixels using this renderer
549     typedef typename agg::renderer_base<PixelFormat> Renderer;
550     typedef agg::span_interpolator_linear<> Interpolator;
551     typedef agg::span_allocator<agg::rgba8> SpanAllocator;
552     typedef agg::rasterizer_scanline_aa<> Rasterizer;
553 
554     // cloning image accessor is used to avoid disturbing pixels at
555     // the edges for rotated video.
556     typedef agg::image_accessor_clone<SourceFormat> Accessor;
557 
558     /// Types used for different quality.
559     //
560     /// This (affects scaling) is only presently used when smoothing is
561     /// requested in high quality.
562     typedef agg::span_image_filter_rgb_nn<Accessor, Interpolator>
563         LowQualityFilter;
564 
565     typedef agg::span_image_filter_rgb_bilinear<Accessor, Interpolator>
566         HighQualityFilter;
567 
568     typedef agg::trans_affine Matrix;
569 
VideoRenderer(const ClipBounds & clipbounds,image::GnashImage & frame,Matrix & mat,Quality quality,bool smooth)570     VideoRenderer(const ClipBounds& clipbounds, image::GnashImage& frame,
571             Matrix& mat, Quality quality, bool smooth)
572         :
573         _buf(frame.begin(), frame.width(), frame.height(),
574                 frame.stride()),
575         _pixf(_buf),
576         _accessor(_pixf),
577         _interpolator(mat),
578         _clipbounds(clipbounds),
579         _quality(quality),
580         _smoothing(smooth)
581     {}
582 
render(agg::path_storage & path,Renderer & rbase,const AlphaMasks & masks)583     void render(agg::path_storage& path, Renderer& rbase,
584             const AlphaMasks& masks)
585     {
586         switch (_quality)
587         {
588             case QUALITY_BEST:
589             case QUALITY_HIGH:
590                 if (_smoothing) {
591                     renderFrame<HighQualityFilter>(path, rbase, masks);
592                 }
593                 else renderFrame<LowQualityFilter>(path, rbase, masks);
594                 break;
595             case QUALITY_MEDIUM:
596             case QUALITY_LOW:
597                 // FIXME: Should this be still lower quality?
598                 renderFrame<LowQualityFilter>(path, rbase, masks);
599                 break;
600         }
601     }
602 
603 private:
604 
605     /// Render a frame with or without alpha masks active.
606     template<typename SpanGenerator>
renderFrame(agg::path_storage & path,Renderer & rbase,const AlphaMasks & masks)607     void renderFrame(agg::path_storage& path, Renderer& rbase,
608             const AlphaMasks& masks)
609     {
610         SpanGenerator sg(_accessor, _interpolator);
611         if (masks.empty()) {
612             // No mask active
613             agg::scanline_u8 sl;
614             renderScanlines(path, rbase, sl, sg);
615         }
616         else {
617             // Untested.
618             typedef agg::scanline_u8_am<agg::alpha_mask_gray8> Scanline;
619             Scanline sl(masks.back().getMask());
620             renderScanlines(path, rbase, sl, sg);
621         }
622     }
623 
624     template<typename Scanline, typename SpanGenerator>
renderScanlines(agg::path_storage & path,Renderer & rbase,Scanline & sl,SpanGenerator & sg)625     void renderScanlines(agg::path_storage& path, Renderer& rbase,
626             Scanline& sl, SpanGenerator& sg)
627     {
628         Rasterizer _ras;
629         for (const auto& cb : _clipbounds)
630         {
631             applyClipBox<Rasterizer> (_ras, cb);
632 
633             _ras.add_path(path);
634 
635             agg::render_scanlines_aa(_ras, sl, rbase, _sa, sg);
636         }
637     }
638 
639     // rendering buffer is used to access the frame pixels here
640     agg::rendering_buffer _buf;
641 
642     const SourceFormat _pixf;
643 
644     Accessor _accessor;
645 
646     Interpolator _interpolator;
647 
648     SpanAllocator _sa;
649 
650     const ClipBounds& _clipbounds;
651 
652     /// Quality of renderering
653     const Quality _quality;
654 
655     /// Whether smoothing is required.
656     bool _smoothing;
657 };
658 
659 
660 
661 }
662 
663 
664 // --- RENDER HANDLER ----------------------------------------------------------
665 // The class is implemented using templates so that it supports any kind of
666 // pixel format. LUT (look up tables) are not supported, however.
667 
668 // Real AGG handler
669 template <class PixelFormat>
670 class Renderer_agg : public Renderer_agg_base
671 {
672 
673 public:
674 
description() const675     std::string description() const {
676         // TODO: make an effort to express pixel format
677         return "AGG";
678     }
679 
680     // Given an image, returns a pointer to a bitmap_info class
681     // that can later be passed to FillStyleX_bitmap(), to set a
682     // bitmap fill style.
createCachedBitmap(std::unique_ptr<image::GnashImage> im)683     gnash::CachedBitmap* createCachedBitmap(std::unique_ptr<image::GnashImage> im)
684     {
685         return new agg_bitmap_info(std::move(im));
686     }
687 
renderToImage(std::unique_ptr<IOChannel> io,FileType type,int quality) const688     virtual void renderToImage(std::unique_ptr<IOChannel> io,
689             FileType type, int quality) const
690     {
691         image::ImageRGBA im(xres, yres);
692         for (int x = 0; x < xres; ++x) {
693             for (int y = 0; y < yres; ++y) {
694                 typename PixelFormat::color_type t = m_pixf->pixel(x, y);
695                 im.setPixel(x, y, t.r, t.g, t.b, t.a);
696             }
697         }
698 
699         image::Output::writeImageData(type, std::move(io), im, quality);
700     }
701 
702     template<typename SourceFormat, typename Matrix>
renderVideo(image::GnashImage & frame,Matrix & img_mtx,agg::path_storage path,bool smooth)703     void renderVideo(image::GnashImage& frame, Matrix& img_mtx,
704             agg::path_storage path, bool smooth)
705     {
706 
707         // renderer base for the stage buffer (not the frame image!)
708         renderer_base& rbase = *m_rbase;
709 
710         VideoRenderer<PixelFormat, SourceFormat> vr(_clipbounds, frame,
711                 img_mtx, _quality, smooth);
712 
713         // If smoothing is requested and _quality is set to HIGH or BEST,
714         // use high-quality interpolation.
715         vr.render(path, rbase, _alphaMasks);
716     }
717 
renderEmptyVideo(agg::path_storage path)718     void renderEmptyVideo(agg::path_storage path)
719     {
720         // renderer base for the stage buffer (not the frame image!)
721         renderer_base& rbase = *m_rbase;
722 
723         EmptyVideoRenderer<PixelFormat> vr(_clipbounds);
724         vr.render(path, rbase, _alphaMasks);
725     }
726 
drawVideoFrame(image::GnashImage * frame,const Transform & xform,const SWFRect * bounds,bool smooth)727     void drawVideoFrame(image::GnashImage* frame, const Transform& xform,
728         const SWFRect* bounds, bool smooth)
729     {
730 
731         // NOTE: Assuming that the source image is RGB 8:8:8
732         // TODO: keep heavy instances alive accross frames for performance!
733         // TODO: Maybe implement specialization for 1:1 scaled videos
734         SWFMatrix mat = stage_matrix;
735         mat.concatenate(xform.matrix);
736 
737         // compute video scaling relative to video object size
738         double vscaleX = bounds->width() /
739             static_cast<double>(frame->width());
740 
741         double vscaleY = bounds->height() /
742             static_cast<double>(frame->height());
743 
744         // convert Gnash SWFMatrix to AGG SWFMatrix and scale down to
745         // pixel coordinates while we're at it
746         agg::trans_affine mtx(mat.a() / 65536.0, mat.b() / 65536.0,
747             mat.c() / 65536.0, mat.d() / 65536.0, mat.tx(), mat.ty());
748 
749         // invert SWFMatrix since this is used for the image source
750         mtx.invert();
751 
752         // Apply video scale
753         mtx *= agg::trans_affine_scaling(1.0 / vscaleX, 1.0 / vscaleY);
754 
755         // make a path for the video outline
756         point a, b, c, d;
757         mat.transform(&a, point(bounds->get_x_min(), bounds->get_y_min()));
758         mat.transform(&b, point(bounds->get_x_max(), bounds->get_y_min()));
759         mat.transform(&c, point(bounds->get_x_max(), bounds->get_y_max()));
760         mat.transform(&d, point(bounds->get_x_min(), bounds->get_y_max()));
761 
762         agg::path_storage path;
763         path.move_to(a.x, a.y);
764         path.line_to(b.x, b.y);
765         path.line_to(c.x, c.y);
766         path.line_to(d.x, d.y);
767         path.line_to(a.x, a.y);
768 
769 #ifdef HAVE_VA_VA_H
770         if (frame->location() == image::GNASH_IMAGE_GPU) {
771             RenderImage image;
772             image.reset(new GnashVaapiImageProxy(
773                             static_cast<GnashVaapiImage *>(frame),
774                             a.x, a.y, c.x - a.x, c.y - a.y));
775             _render_images.push_back(image);
776 
777             // clear video region with transparent color
778             renderEmptyVideo(path);
779             return;
780         }
781 #endif
782 
783         switch (frame->type())
784         {
785             case image::TYPE_RGBA:
786                 renderVideo<agg::pixfmt_rgba32_pre>(*frame, mtx, path, smooth);
787                 break;
788             case image::TYPE_RGB:
789                 renderVideo<agg::pixfmt_rgb24_pre>(*frame, mtx, path, smooth);
790                 break;
791             default:
792                 log_error(_("Can't render this type of frame"));
793                 break;
794         }
795 
796     }
797 
798   // Constructor
Renderer_agg(int bits_per_pixel)799   Renderer_agg(int bits_per_pixel)
800       :
801       xres(1),
802       yres(1),
803       bpp(bits_per_pixel),
804       scale_set(false),
805       m_drawing_mask(false)
806   {
807     // TODO: we really don't want to set the scale here as the core should
808     // tell us the right values before rendering anything. However this is
809     // currently difficult to implement. Removing the next call will
810     // lead to an assertion failure in begin_display() because we check
811     // whether the scale is known there.
812     set_scale(1.0f, 1.0f);
813   }
814 
815   /// Initializes the rendering buffer. The memory pointed by "mem" is not
816   /// owned by the renderer and init_buffer() may be called multiple times
817   /// when the buffer size changes, for example. However, bits_per_pixel must
818   /// remain the same.
819   /// rowstride is the size, in bytes, of one row.
820   /// This method *must* be called prior to any other method of the class!
init_buffer(unsigned char * mem,int,int x,int y,int rowstride)821   void init_buffer(unsigned char *mem, int /*size*/, int x, int y, int rowstride)
822   {
823         assert(x > 0);
824         assert(y > 0);
825 
826     xres    = x;
827     yres    = y;
828 
829     m_rbuf.attach(mem, xres, yres, rowstride);
830 
831     // allocate pixel format accessor and renderer_base
832     m_pixf.reset(new PixelFormat(m_rbuf));
833     m_rbase.reset(new renderer_base(*m_pixf));
834 
835     // by default allow drawing everywhere
836     set_invalidated_region_world();
837   }
838 
839 
begin_display(const gnash::rgba & bg,int,int,float,float,float,float)840   void begin_display(const gnash::rgba& bg,
841       int /*viewport_width*/, int /*viewport_height*/,
842       float /*x0*/, float /*x1*/, float /*y0*/, float /*y1*/)
843   {
844     assert(m_pixf.get());
845 
846     assert(scale_set);
847 
848     // Render images list is cleared here because the GUI may want
849     // them for display after ::end_display()
850     _render_images.clear();
851 
852     // clear the stage using the background color
853     if ( ! _clipbounds.empty() )
854     {
855         const agg::rgba8& col = agg::rgba8_pre(bg.m_r, bg.m_g, bg.m_b, bg.m_a);
856         for (const auto& bounds : _clipbounds)
857         {
858             clear_framebuffer(bounds, col);
859         }
860     }
861 
862     // reset status variables
863     m_drawing_mask = false;
864   }
865 
866 
startInternalRender(image::GnashImage & im)867     virtual Renderer* startInternalRender(image::GnashImage& im) {
868 
869         std::unique_ptr<Renderer_agg_base> in;
870 
871         switch (im.type()) {
872             case image::TYPE_RGB:
873                 in.reset(new Renderer_agg<typename RGB::PixelFormat>(24));
874                 break;
875             case image::TYPE_RGBA:
876                 in.reset(new Renderer_agg<typename RGBA::PixelFormat>(32));
877                 break;
878             default:
879                 std::abort();
880         }
881 
882         const size_t width = im.width();
883         const size_t height = im.height();
884         const size_t stride = width * (im.type() == image::TYPE_RGBA ? 4 : 3);
885 
886         in->init_buffer(im.begin(), width * height, width, height, stride);
887         _external.reset(in.release());
888         return _external.get();
889     }
890 
endInternalRender()891     virtual void endInternalRender() {
892         _external.reset();
893     }
894 
895     // renderer_base.clear() does no clipping which clears the
896     // whole framebuffer even if we update just a small portion
897     // of the screen. The result would be still correct, but slower.
898     // This function clears only a certain portion of the screen, while /not/
899     // being notably slower for a fullscreen clear.
clear_framebuffer(const geometry::Range2d<int> & region,const agg::rgba8 & color)900     void clear_framebuffer(const geometry::Range2d<int>& region,
901         const agg::rgba8& color)
902     {
903         assert(region.isFinite());
904 
905         // add 1 to width since we have still to draw a pixel when
906         // getMinX==getMaxX
907         unsigned int width=region.width()+1;
908 
909         // <Udo> Note: We don't need to check for width/height anymore because
910         // Range2d will take care that getMinX <= getMaxX and it's okay when
911         // region.width()==0 because in that case getMinX==getMaxX and we have
912         // still a pixel to draw.
913 
914         const unsigned int left=region.getMinX();
915 
916         for (unsigned int y=region.getMinY(), maxy=region.getMaxY();
917             y<=maxy; ++y) {
918               m_pixf->copy_hline(left, y, width, color);
919         }
920     }
921 
922     // Clean up after rendering a frame.
end_display()923     void end_display()
924     {
925         if (m_drawing_mask) {
926             log_debug("Warning: rendering ended while drawing a mask");
927         }
928 
929         while (! _alphaMasks.empty()) {
930             log_debug("Warning: rendering ended while masks "
931                         "were still active");
932             disable_mask();
933         }
934     }
935 
936     // Draw the line strip formed by the sequence of points.
drawLine(const std::vector<point> & coords,const rgba & color,const SWFMatrix & line_mat)937     void drawLine(const std::vector<point>& coords, const rgba& color,
938             const SWFMatrix& line_mat)
939     {
940 
941         assert(m_pixf.get());
942 
943         if (_clipbounds.empty()) return;
944         if (coords.empty()) return;
945 
946         SWFMatrix mat = stage_matrix;
947         mat.concatenate(line_mat);
948 
949         LineRenderer<PixelFormat> lr(_clipbounds, *m_rbase);
950 
951         // -- create path --
952         agg::path_storage path;
953 
954         typename LineRenderer<PixelFormat>::Stroke stroke(path);
955         stroke.width(1);
956         stroke.line_cap(agg::round_cap);
957         stroke.line_join(agg::round_join);
958 
959         typedef std::vector<point> Points;
960 
961         // We've asserted that it has at least one element.
962         Points::const_iterator i = coords.begin();
963 
964         point pnt;
965 
966         mat.transform(&pnt, *i);
967         path.move_to(pnt.x, pnt.y);
968 
969         ++i;
970 
971         for (const Points::const_iterator e = coords.end(); i != e; ++i)
972         {
973             mat.transform(&pnt, *i);
974             path.line_to(pnt.x, pnt.y);
975         }
976 
977         if (_alphaMasks.empty()) {
978             // No mask active
979             agg::scanline_p8 sl;
980             lr.render(sl, stroke, color);
981         }
982         else {
983             // Mask is active!
984             typedef agg::scanline_u8_am<agg::alpha_mask_gray8> sl_type;
985             sl_type sl(_alphaMasks.back().getMask());
986             lr.render(sl, stroke, color);
987         }
988 
989     }
990 
991 
begin_submit_mask()992     void begin_submit_mask()
993     {
994         // Set flag so that rendering of shapes is simplified (only solid fill)
995         m_drawing_mask = true;
996 
997         _alphaMasks.push_back(new AlphaMask(xres, yres));
998         AlphaMask& new_mask = _alphaMasks.back();
999 
1000         for (const auto& bounds : _clipbounds ) {
1001             new_mask.clear(bounds);
1002         }
1003 
1004     }
1005 
end_submit_mask()1006     void end_submit_mask()
1007     {
1008         m_drawing_mask = false;
1009     }
1010 
disable_mask()1011     void disable_mask()
1012     {
1013         assert(!_alphaMasks.empty());
1014         _alphaMasks.pop_back();
1015     }
1016 
1017 
drawGlyph(const SWF::ShapeRecord & shape,const rgba & color,const SWFMatrix & mat)1018   void drawGlyph(const SWF::ShapeRecord& shape, const rgba& color,
1019           const SWFMatrix& mat)
1020   {
1021     if (shape.subshapes().empty()) return;
1022     assert(shape.subshapes().size() == 1);
1023 
1024     // select relevant clipping bounds
1025     if (shape.getBounds().is_null()) {
1026         return;
1027     }
1028     select_clipbounds(shape.getBounds(), mat);
1029 
1030     if (_clipbounds_selected.empty()) return;
1031 
1032     GnashPaths paths;
1033     apply_matrix_to_path(shape.subshapes().front().paths(), paths, mat);
1034 
1035     // If it's a mask, we don't need the rest.
1036     if (m_drawing_mask) {
1037       draw_mask_shape(paths, false);
1038       return;
1039     }
1040 
1041     // convert gnash paths to agg paths.
1042     AggPaths agg_paths;
1043     buildPaths(agg_paths, paths);
1044 
1045     std::vector<FillStyle> v(1, FillStyle(SolidFill(color)));
1046 
1047     // prepare style handler
1048     StyleHandler sh;
1049     build_agg_styles(sh, v, mat, SWFCxForm());
1050 
1051     draw_shape(paths, agg_paths, sh, false);
1052 
1053     // NOTE: Do not use even-odd filling rule for glyphs!
1054 
1055     // clear clipping ranges to ease debugging
1056     _clipbounds_selected.clear();
1057   }
1058 
1059 
1060   /// Fills _clipbounds_selected with pointers to _clipbounds members who
1061   /// intersect with the given character (transformed by mat). This avoids
1062   /// rendering of characters outside a particular clipping range.
1063   /// "_clipbounds_selected" is used by draw_shape() and draw_outline() and
1064   /// *must* be initialized prior to using those function.
select_clipbounds(const SWFRect & objectBounds,const SWFMatrix & source_mat)1065   void select_clipbounds(const SWFRect& objectBounds, const SWFMatrix& source_mat)
1066   {
1067 
1068     SWFMatrix mat = stage_matrix;
1069     mat.concatenate(source_mat);
1070 
1071     _clipbounds_selected.clear();
1072     _clipbounds_selected.reserve(_clipbounds.size());
1073 
1074     if (objectBounds.is_null()) {
1075       log_debug("Warning: select_clipbounds encountered a character "
1076                   "definition with null bounds");
1077       return;
1078     }
1079 
1080     SWFRect bounds;
1081     bounds.set_null();
1082     bounds.expand_to_transformed_rect(mat, objectBounds);
1083 
1084     assert(bounds.getRange().isFinite());
1085 
1086     for (const auto& clip : _clipbounds) {
1087 
1088       if (clip.intersects(bounds.getRange()))
1089         _clipbounds_selected.push_back(&clip);
1090 
1091     }
1092   }
1093 
select_all_clipbounds()1094   void select_all_clipbounds() {
1095 
1096     if (_clipbounds_selected.size() == _clipbounds.size()) return;
1097 
1098     _clipbounds_selected.clear();
1099     _clipbounds_selected.reserve(_clipbounds.size());
1100 
1101     for (const auto& clip : _clipbounds)
1102     {
1103       _clipbounds_selected.push_back(&clip);
1104     }
1105   }
1106 
drawShape(const SWF::ShapeRecord & shape,const Transform & xform)1107     void drawShape(const SWF::ShapeRecord& shape, const Transform& xform)
1108     {
1109         // check if the character needs to be rendered at all
1110         SWFRect cur_bounds;
1111 
1112         cur_bounds.expand_to_transformed_rect(xform.matrix, shape.getBounds());
1113 
1114         if (!bounds_in_clipping_area(cur_bounds.getRange()))
1115         {
1116             return; // no need to draw
1117         }
1118 
1119         for (const SWF::Subshape& subshape : shape.subshapes()) {
1120 
1121             const SWF::ShapeRecord::FillStyles& fillStyles = subshape.fillStyles();
1122             const SWF::ShapeRecord::LineStyles& lineStyles = subshape.lineStyles();
1123             const SWF::ShapeRecord::Paths& paths = subshape.paths();
1124 
1125             // select ranges
1126             select_clipbounds(shape.getBounds(), xform.matrix);
1127 
1128             // render the DisplayObject's subshape.
1129             drawShape(fillStyles, lineStyles, paths, xform.matrix,
1130                       xform.colorTransform);
1131         }
1132     }
1133 
drawShape(const std::vector<FillStyle> & FillStyles,const std::vector<LineStyle> & line_styles,const std::vector<Path> & objpaths,const SWFMatrix & mat,const SWFCxForm & cx)1134     void drawShape(const std::vector<FillStyle>& FillStyles,
1135         const std::vector<LineStyle>& line_styles,
1136         const std::vector<Path>& objpaths, const SWFMatrix& mat,
1137         const SWFCxForm& cx)
1138     {
1139 
1140         bool have_shape, have_outline;
1141 
1142         analyzePaths(objpaths, have_shape, have_outline);
1143 
1144         if (!have_shape && !have_outline) {
1145             // Early return for invisible character.
1146             return;
1147         }
1148 
1149         GnashPaths paths;
1150         apply_matrix_to_path(objpaths, paths, mat);
1151 
1152         // Masks apparently do not use agg_paths, so return
1153         // early
1154         if (m_drawing_mask) {
1155 
1156             // Shape is drawn inside a mask, skip sub-shapes handling and
1157             // outlines
1158             draw_mask_shape(paths, false);
1159             return;
1160         }
1161 
1162         AggPaths agg_paths;
1163         AggPaths agg_paths_rounded;
1164 
1165         // Flash only aligns outlines. Probably this is done at rendering
1166         // level.
1167         if (have_outline) {
1168             buildPaths_rounded(agg_paths_rounded, paths, line_styles);
1169         }
1170 
1171         if (have_shape) {
1172             buildPaths(agg_paths, paths);
1173         }
1174 
1175 
1176         if (_clipbounds_selected.empty()) {
1177 #ifdef GNASH_WARN_WHOLE_CHARACTER_SKIP
1178             log_debug("Warning: AGG renderer skipping a whole character");
1179 #endif
1180             return;
1181         }
1182 
1183         // prepare fill styles
1184         StyleHandler sh;
1185         if (have_shape) build_agg_styles(sh, FillStyles, mat, cx);
1186 
1187 
1188             if (have_shape) {
1189                 draw_shape(paths, agg_paths, sh, true);
1190             }
1191             if (have_outline)            {
1192                 draw_outlines(paths, agg_paths_rounded,
1193                         line_styles, cx, mat);
1194             }
1195 
1196         // Clear selected clipbounds to ease debugging
1197         _clipbounds_selected.clear();
1198     }
1199 
1200     /// Takes a path and translates it using the given SWFMatrix. The new path
1201     /// is stored in paths_out. Both paths_in and paths_out are expected to
1202     /// be in TWIPS.
apply_matrix_to_path(const GnashPaths & paths_in,GnashPaths & paths_out,const SWFMatrix & source_mat)1203     void apply_matrix_to_path(const GnashPaths &paths_in,
1204           GnashPaths& paths_out, const SWFMatrix &source_mat)
1205     {
1206 
1207         SWFMatrix mat;
1208         // make sure paths_out is also in TWIPS to keep accuracy.
1209         mat.concatenate_scale(20.0,  20.0);
1210         mat.concatenate(stage_matrix);
1211         mat.concatenate(source_mat);
1212 
1213         // Copy paths for in-place transform
1214         paths_out = paths_in;
1215 
1216         /// Transform all the paths using the matrix.
1217         std::for_each(paths_out.begin(), paths_out.end(),
1218                 std::bind(&Path::transform, std::placeholders::_1,
1219 		    std::ref(mat)));
1220     }
1221 
1222   // Version of buildPaths that uses rounded coordinates (pixel hinting)
1223   // for line styles that want it.
1224   // This is used for outlines which are aligned to the pixel grid to avoid
1225   // anti-aliasing problems (a perfect horizontal line being drawn over two
1226   // lines and looking blurry). The proprietary player does this too.
1227   //
1228   // Not all points are aligned, only those lines that:
1229   //   - are straight
1230   //   - are pure horizontal or vertical
1231   // Also, single segments of a path may be aligned or not depending on
1232   // the segment properties (this matches MM player behaviour)
1233   //
1234   // This function - in contrast to buildPaths() - also checks noClose
1235   // flag and automatically closes polygons.
1236   //
1237   // TODO: Flash never aligns lines that are wider than 1 pixel on *screen*,
1238   // but we currently don't check the width.
buildPaths_rounded(AggPaths & dest,const GnashPaths & paths,const std::vector<LineStyle> & line_styles)1239   void buildPaths_rounded(AggPaths& dest,
1240     const GnashPaths& paths, const std::vector<LineStyle>& line_styles)
1241   {
1242 
1243     const float subpixel_offset = 0.5f;
1244 
1245     const size_t pcount = paths.size();
1246 
1247     dest.resize(pcount);
1248 
1249     for (size_t pno=0; pno<pcount; ++pno) {
1250 
1251       const Path& this_path = paths[pno];
1252       agg::path_storage& new_path = dest[pno];
1253 
1254       bool hinting=false, closed=false, hairline=false;
1255 
1256       if (this_path.m_line) {
1257         const LineStyle& lstyle = line_styles[this_path.m_line-1];
1258 
1259         hinting = lstyle.doPixelHinting();
1260         closed = this_path.isClosed() && !lstyle.noClose();
1261 
1262         // check if this line is a hairline ON SCREEN
1263         // TODO: we currently only check for hairlines per definiton, not
1264         // for thin lines that become hair lines due to scaling
1265         if (lstyle.getThickness()<=20)
1266           hairline = true;
1267       }
1268 
1269       float prev_ax = twipsToPixels(this_path.ap.x);
1270       float prev_ay = twipsToPixels(this_path.ap.y);
1271       bool prev_align_x = true;
1272       bool prev_align_y = true;
1273 
1274       size_t ecount = this_path.m_edges.size();
1275 
1276       // avoid extra edge when doing implicit close later
1277       if (closed && ecount &&
1278         this_path.m_edges.back().straight()) --ecount;
1279 
1280       for (size_t eno=0; eno<ecount; ++eno) {
1281 
1282         const Edge& this_edge = this_path.m_edges[eno];
1283 
1284         float this_ax = twipsToPixels(this_edge.ap.x);
1285         float this_ay = twipsToPixels(this_edge.ap.y);
1286 
1287         if (hinting || this_edge.straight()) {
1288 
1289           // candidate for alignment?
1290           bool align_x = hinting || (hairline && (prev_ax == this_ax));
1291           bool align_y = hinting || (hairline && (prev_ay == this_ay));
1292 
1293           if (align_x)
1294             this_ax = round(this_ax);
1295 
1296           if (align_y)
1297             this_ay = round(this_ay);
1298 
1299           // first line?
1300           if (eno==0) {
1301 
1302             if (align_x)
1303               prev_ax = round(prev_ax);
1304 
1305             if (align_y)
1306               prev_ay = round(prev_ay);
1307 
1308             new_path.move_to(prev_ax + subpixel_offset,
1309               prev_ay + subpixel_offset);
1310 
1311           } else {
1312 
1313             // not the first line, but the previous anchor point
1314             // might belong to a curve and thus may not be aligned.
1315             // We need to have both anchors of this new line to be
1316             // aligned, so it may be neccesary to add a line
1317             if ((align_x && !prev_align_x) || (align_y && !prev_align_y)) {
1318 
1319               if (align_x)
1320                 prev_ax = round(prev_ax);
1321 
1322               if (align_y)
1323                 prev_ay = round(prev_ay);
1324 
1325               new_path.line_to(prev_ax + subpixel_offset,
1326                 prev_ay + subpixel_offset);
1327 
1328             }
1329 
1330             // TODO: (minor flaw) Flash player never aligns anchor points
1331             // of curves, even if they are attached to straight vertical
1332             // or horizontal lines. It can be seen easily with rounded
1333             // rectangles, where the curves are never aligned and all
1334             // straight lines are. AGG backend will align the curve anchor
1335             // point that follows the straight line. It's not a big problem
1336             // but it's not exact...
1337 
1338           }
1339 
1340           new_path.line_to(this_ax + subpixel_offset,
1341             this_ay + subpixel_offset);
1342 
1343           prev_align_x = align_x;
1344           prev_align_y = align_y;
1345 
1346 
1347         } else {
1348 
1349           // first line?
1350           if (eno==0)
1351             new_path.move_to(prev_ax, prev_ay);
1352 
1353           // never align curves!
1354           new_path.curve3(
1355             twipsToPixels(this_edge.cp.x) + subpixel_offset,
1356             twipsToPixels(this_edge.cp.y) + subpixel_offset,
1357             this_ax + subpixel_offset,
1358             this_ay + subpixel_offset);
1359 
1360           prev_align_x = false;
1361           prev_align_y = false;
1362 
1363         }
1364 
1365         prev_ax = this_ax;
1366         prev_ay = this_ay;
1367 
1368       } //for
1369 
1370       if (closed)
1371         new_path.close_polygon();
1372 
1373     }
1374   } //buildPaths_rounded
1375 
1376     // Initializes the internal styles class for AGG renderer
build_agg_styles(StyleHandler & sh,const std::vector<FillStyle> & FillStyles,const SWFMatrix & fillstyle_matrix,const SWFCxForm & cx)1377     void build_agg_styles(StyleHandler& sh,
1378         const std::vector<FillStyle>& FillStyles,
1379         const SWFMatrix& fillstyle_matrix, const SWFCxForm& cx) {
1380 
1381         SWFMatrix inv_stage_matrix = stage_matrix;
1382         inv_stage_matrix.invert();
1383 
1384         const size_t fcount = FillStyles.size();
1385 
1386         for (size_t fno = 0; fno < fcount; ++fno) {
1387             const AddStyles st(stage_matrix, fillstyle_matrix, cx, sh,
1388                     _quality);
1389             boost::apply_visitor(st, FillStyles[fno].fill);
1390         }
1391     }
1392 
1393 
1394   /// Draws the given path using the given fill style and color transform.
1395   //
1396   /// Normally, Flash shapes are drawn using even-odd filling rule. However,
1397   /// for glyphs non-zero filling rule should be used (even_odd=0).
1398   /// Note the paths have already been transformed by the SWFMatrix and
1399   /// 'subshape_id' defines which sub-shape should be drawn (-1 means all
1400   /// subshapes).
1401   ///
1402   /// Note the *coordinates* in "paths" are not used because they are
1403   /// already prepared in agg_paths. The (nearly ambiguous) "path" parameter
1404   /// is used to access other properties like fill styles and subshapes.
1405   ///
1406   /// @param subshape_id
1407   ///    Defines which subshape to draw. -1 means all subshapes.
1408   ///
draw_shape(const GnashPaths & paths,const AggPaths & agg_paths,StyleHandler & sh,bool even_odd)1409   void draw_shape(const GnashPaths &paths,
1410     const AggPaths& agg_paths,
1411     StyleHandler& sh, bool even_odd) {
1412 
1413     if (_alphaMasks.empty()) {
1414 
1415       // No mask active, use normal scanline renderer
1416 
1417       typedef agg::scanline_u8 scanline_type;
1418 
1419       scanline_type sl;
1420 
1421       draw_shape_impl<scanline_type> (paths, agg_paths,
1422         sh, even_odd, sl);
1423 
1424     } else {
1425 
1426       // Mask is active, use alpha mask scanline renderer
1427 
1428       typedef agg::scanline_u8_am<agg::alpha_mask_gray8> scanline_type;
1429 
1430       scanline_type sl(_alphaMasks.back().getMask());
1431 
1432       draw_shape_impl<scanline_type> (paths, agg_paths,
1433         sh, even_odd, sl);
1434 
1435     }
1436 
1437   }
1438 
1439   /// Template for draw_shape(). Two different scanline types are suppored,
1440   /// one with and one without an alpha mask. This makes drawing without masks
1441   /// much faster.
1442   template <class scanline_type>
draw_shape_impl(const GnashPaths & paths,const AggPaths & agg_paths,StyleHandler & sh,bool even_odd,scanline_type & sl)1443   void draw_shape_impl(const GnashPaths &paths,
1444     const AggPaths& agg_paths,
1445     StyleHandler& sh, bool even_odd, scanline_type& sl) {
1446     /*
1447     Fortunately, AGG provides a rasterizer that fits perfectly to the flash
1448     data model. So we just have to feed AGG with all data and we're done. :-)
1449     This is also far better than recomposing the polygons as the rasterizer
1450     can do everything in one pass and it is also better for adjacent edges
1451     (anti aliasing).
1452     Thank to Maxim Shemanarev for providing us such a great tool with AGG...
1453     */
1454 
1455     assert(m_pixf.get());
1456 
1457     assert(!m_drawing_mask);
1458 
1459     if ( _clipbounds.empty() ) return;
1460 
1461     // Target renderer
1462     renderer_base& rbase = *m_rbase;
1463 
1464     typedef agg::rasterizer_compound_aa<agg::rasterizer_sl_clip_int> ras_type;
1465     ras_type rasc;  // flash-like renderer
1466 
1467     agg::renderer_scanline_aa_solid<
1468       agg::renderer_base<PixelFormat> > ren_sl(rbase); // solid fills
1469     agg::span_allocator<agg::rgba8> alloc;  // span allocator (?)
1470 
1471 
1472     // activate even-odd filling rule
1473     if (even_odd)
1474       rasc.filling_rule(agg::fill_even_odd);
1475     else
1476       rasc.filling_rule(agg::fill_non_zero);
1477 
1478 
1479     for (const geometry::Range2d<int>* bounds : _clipbounds_selected) {
1480 
1481       applyClipBox<ras_type> (rasc, *bounds);
1482 
1483       // push paths to AGG
1484       const size_t pcount = paths.size();
1485 
1486       for (size_t pno=0; pno<pcount; ++pno) {
1487 
1488         const Path &this_path_gnash = paths[pno];
1489         agg::path_storage &this_path_agg =
1490           const_cast<agg::path_storage&>(agg_paths[pno]);
1491 
1492         agg::conv_curve<agg::path_storage> curve(this_path_agg);
1493 
1494         if ((this_path_gnash.m_fill0==0) && (this_path_gnash.m_fill1==0)) {
1495           // Skip this path as it contains no fill style
1496           continue;
1497         }
1498 
1499         // Tell the rasterizer which styles the following path will use.
1500         // The good thing is, that it already supports two fill styles out of
1501         // the box.
1502         // Flash uses value "0" for "no fill", whereas AGG uses "-1" for that.
1503         rasc.styles(this_path_gnash.m_fill0-1, this_path_gnash.m_fill1-1);
1504 
1505         // add path to the compound rasterizer
1506         rasc.add_path(curve);
1507 
1508       }
1509 
1510       agg::render_scanlines_compound_layered(rasc, sl, rbase, alloc, sh);
1511     }
1512 
1513   } // draw_shape_impl
1514 
1515 
1516 
1517 
1518   // very similar to draw_shape but used for generating masks. There are no
1519   // fill styles nor subshapes and such. Just render plain solid shapes.
draw_mask_shape(const GnashPaths & paths,bool even_odd)1520   void draw_mask_shape(const GnashPaths& paths, bool even_odd)
1521   {
1522 
1523     const AlphaMasks::size_type mask_count = _alphaMasks.size();
1524 
1525     if (mask_count < 2) {
1526 
1527       // This is the first level mask
1528 
1529       typedef agg::scanline_u8 scanline_type;
1530 
1531       scanline_type sl;
1532 
1533       draw_mask_shape_impl(paths, even_odd, sl);
1534 
1535     }
1536     else {
1537 
1538       // Woohoo! We're drawing a nested mask! Use the previous mask while
1539       // drawing the new one, the result will be the intersection.
1540 
1541       typedef agg::scanline_u8_am<agg::alpha_mask_gray8> scanline_type;
1542 
1543       scanline_type sl(_alphaMasks[mask_count - 2].getMask());
1544 
1545       draw_mask_shape_impl(paths, even_odd, sl);
1546 
1547     }
1548 
1549   }
1550 
1551 
1552   template <class scanline_type>
draw_mask_shape_impl(const GnashPaths & paths,bool even_odd,scanline_type & sl)1553   void draw_mask_shape_impl(const GnashPaths& paths, bool even_odd,
1554     scanline_type& sl) {
1555 
1556     typedef agg::pixfmt_gray8 pixfmt;
1557     typedef agg::renderer_base<pixfmt> renderer_base;
1558 
1559     assert(!_alphaMasks.empty());
1560 
1561     // dummy style handler
1562     typedef agg_mask_style_handler sh_type;
1563     sh_type sh;
1564 
1565     // compound rasterizer used for flash shapes
1566     typedef agg::rasterizer_compound_aa<agg::rasterizer_sl_clip_int> rasc_type;
1567     rasc_type rasc;
1568 
1569 
1570     // activate even-odd filling rule
1571     if (even_odd) rasc.filling_rule(agg::fill_even_odd);
1572     else rasc.filling_rule(agg::fill_non_zero);
1573 
1574     // push paths to AGG
1575     agg::path_storage path;
1576     agg::conv_curve<agg::path_storage> curve(path);
1577 
1578     for (const Path& this_path : paths) {
1579 
1580       path.remove_all();
1581 
1582       // reduce everything to just one fill style!
1583       rasc.styles(this_path.m_fill0==0 ? -1 : 0,
1584                   this_path.m_fill1==0 ? -1 : 0);
1585 
1586       // starting point of path
1587       path.move_to(twipsToPixels(this_path.ap.x),
1588                    twipsToPixels(this_path.ap.y));
1589 
1590       // Add all edges to the path.
1591       std::for_each(this_path.m_edges.begin(), this_path.m_edges.end(),
1592               EdgeToPath(path));
1593 
1594       // add to rasterizer
1595       rasc.add_path(curve);
1596 
1597     } // for path
1598 
1599     // renderer base
1600     renderer_base& rbase = _alphaMasks.back().get_rbase();
1601 
1602     // span allocator
1603     typedef agg::span_allocator<agg::gray8> alloc_type;
1604     alloc_type alloc;
1605 
1606 
1607     // now render that thing!
1608     agg::render_scanlines_compound_layered (rasc, sl, rbase, alloc, sh);
1609 
1610   } // draw_mask_shape
1611 
1612 
1613 
1614   /// Just like draw_shapes() except that it draws an outline.
draw_outlines(const GnashPaths & paths,const AggPaths & agg_paths,const std::vector<LineStyle> & line_styles,const SWFCxForm & cx,const SWFMatrix & linestyle_matrix)1615   void draw_outlines(const GnashPaths &paths,
1616     const AggPaths& agg_paths,
1617     const std::vector<LineStyle> &line_styles, const SWFCxForm& cx,
1618     const SWFMatrix& linestyle_matrix) {
1619 
1620     if (_alphaMasks.empty()) {
1621 
1622       // No mask active, use normal scanline renderer
1623 
1624       typedef agg::scanline_u8 scanline_type;
1625 
1626       scanline_type sl;
1627 
1628       draw_outlines_impl<scanline_type> (paths, agg_paths,
1629         line_styles, cx, linestyle_matrix, sl);
1630 
1631     } else {
1632 
1633       // Mask is active, use alpha mask scanline renderer
1634 
1635       typedef agg::scanline_u8_am<agg::alpha_mask_gray8> scanline_type;
1636 
1637       scanline_type sl(_alphaMasks.back().getMask());
1638 
1639       draw_outlines_impl<scanline_type> (paths, agg_paths,
1640         line_styles, cx, linestyle_matrix, sl);
1641 
1642     }
1643 
1644   } //draw_outlines
1645 
1646 
1647   /// Template for draw_outlines(), see draw_shapes_impl().
1648   template <class scanline_type>
draw_outlines_impl(const GnashPaths & paths,const AggPaths & agg_paths,const std::vector<LineStyle> & line_styles,const SWFCxForm & cx,const SWFMatrix & linestyle_matrix,scanline_type & sl)1649   void draw_outlines_impl(const GnashPaths &paths,
1650     const AggPaths& agg_paths,
1651     const std::vector<LineStyle> &line_styles, const SWFCxForm& cx,
1652     const SWFMatrix& linestyle_matrix, scanline_type& sl) {
1653 
1654     assert(m_pixf.get());
1655 
1656     // Flash ignores lines in mask /definitions/
1657     if (m_drawing_mask) return;
1658 
1659     if ( _clipbounds.empty() ) return;
1660 
1661     // TODO: While walking the paths for filling them, remember when a path
1662     // has a line style associated, so that we avoid walking the paths again
1663     // when there really are no outlines to draw...
1664 
1665     // use avg between x and y scale
1666     const float stroke_scale = (std::abs(linestyle_matrix.get_x_scale()) +
1667        std::abs(linestyle_matrix.get_y_scale())) / 2.0f * get_stroke_scale();
1668 
1669 
1670     // AGG stuff
1671     typedef agg::rasterizer_scanline_aa<> ras_type;
1672     ras_type ras;  // anti alias
1673 
1674     renderer_base& rbase = *m_rbase;
1675 
1676     agg::renderer_scanline_aa_solid<
1677       agg::renderer_base<PixelFormat> > ren_sl(rbase); // solid fills
1678 
1679 
1680     for (const geometry::Range2d<int>* bounds : _clipbounds_selected) {
1681 
1682       applyClipBox<ras_type> (ras, *bounds);
1683 
1684       for (size_t pno=0, pcount=paths.size(); pno<pcount; ++pno) {
1685 
1686         const Path& this_path_gnash = paths[pno];
1687 
1688         agg::path_storage &this_path_agg =
1689           const_cast<agg::path_storage&>(agg_paths[pno]);
1690 
1691         if (this_path_gnash.m_line==0) {
1692           // Skip this path as it contains no line style
1693           continue;
1694         }
1695 
1696         agg::conv_curve< agg::path_storage > curve(this_path_agg); // to render curves
1697         agg::conv_stroke< agg::conv_curve < agg::path_storage > >
1698           stroke(curve);  // to get an outline
1699 
1700         const LineStyle& lstyle = line_styles[this_path_gnash.m_line-1];
1701 
1702         int thickness = lstyle.getThickness();
1703         if (!thickness) stroke.width(1); // hairline
1704         else if ( (!lstyle.scaleThicknessVertically()) && (!lstyle.scaleThicknessHorizontally()) )
1705         {
1706           stroke.width(twipsToPixels(thickness));
1707         }
1708         else
1709         {
1710           if ((!lstyle.scaleThicknessVertically()) ||
1711                   (!lstyle.scaleThicknessHorizontally()))
1712           {
1713              LOG_ONCE(log_unimpl(_("Unidirectionally scaled strokes in "
1714 				   "AGG renderer (we'll scale by the "
1715 				   "scalable one)")) );
1716           }
1717           stroke.width(std::max(1.0f, thickness*stroke_scale));
1718         }
1719 
1720         // TODO: support endCapStyle
1721 
1722         // TODO: When lstyle.noClose==0 and the start and end point matches,
1723         // then render a real join instead of the caps.
1724 
1725         switch (lstyle.startCapStyle()) {
1726           case CAP_NONE   : stroke.line_cap(agg::butt_cap); break;
1727           case CAP_SQUARE : stroke.line_cap(agg::square_cap); break;
1728           default : case CAP_ROUND : stroke.line_cap(agg::round_cap);
1729         }
1730 
1731         switch (lstyle.joinStyle()) {
1732           case JOIN_BEVEL : stroke.line_join(agg::bevel_join); break;
1733           case JOIN_MITER : stroke.line_join(agg::miter_join); break;
1734           default : case JOIN_ROUND : stroke.line_join(agg::round_join);
1735         }
1736 
1737         stroke.miter_limit(lstyle.miterLimitFactor());
1738 
1739         ras.reset();
1740         ras.add_path(stroke);
1741 
1742         rgba color = cx.transform(lstyle.get_color());
1743         ren_sl.color(agg::rgba8_pre(color.m_r, color.m_g, color.m_b, color.m_a));
1744 
1745         agg::render_scanlines(ras, sl, ren_sl);
1746 
1747       }
1748 
1749 
1750     }
1751 
1752   } // draw_outlines_impl
1753 
1754 
1755 
1756   /// Draws the given polygon.
1757   template <class scanline_type>
draw_poly_impl(const point * corners,size_t corner_count,const rgba & fill,const rgba & outline,scanline_type & sl,const SWFMatrix & poly_mat)1758   void draw_poly_impl(const point* corners, size_t corner_count, const rgba& fill,
1759     const rgba& outline, scanline_type& sl, const SWFMatrix& poly_mat) {
1760 
1761     assert(m_pixf.get());
1762 
1763     if (corner_count<1) return;
1764 
1765     if ( _clipbounds.empty() ) return;
1766 
1767     SWFMatrix mat = stage_matrix;
1768     mat.concatenate(poly_mat);
1769 
1770     typedef agg::rasterizer_scanline_aa<> ras_type;
1771     renderer_base& rbase = *m_rbase;
1772 
1773     ras_type ras;
1774     agg::renderer_scanline_aa_solid<
1775       agg::renderer_base<PixelFormat> > ren_sl(rbase);
1776 
1777     // -- create path --
1778     agg::path_storage path;
1779     point pnt, origin;
1780 
1781 
1782     // Note: The coordinates are rounded and 0.5 is added to snap them to the
1783     // center of the pixel. This avoids blurring caused by anti-aliasing.
1784 
1785     // The default conversion of the boost converter is truncation.
1786     boost::numeric::converter<int,float> truncator;
1787 
1788     mat.transform(&origin,
1789       point(truncator(corners[0].x), truncator(corners[0].y)));
1790     path.move_to(truncator(origin.x)+0.5, truncator(origin.y)+0.5);
1791 
1792     for (unsigned int i=1; i<corner_count; ++i) {
1793 
1794       mat.transform(&pnt, point(corners[i].x, corners[i].y));
1795 
1796       path.line_to(truncator(pnt.x)+0.5, truncator(pnt.y)+0.5);
1797     }
1798 
1799     // close polygon
1800     path.line_to(truncator(origin.x)+0.5, truncator(origin.y)+0.5);
1801 
1802 
1803 
1804     // -- render --
1805 
1806     // iterate through clipping bounds
1807     for (const auto& bounds : _clipbounds) {
1808 
1809       applyClipBox<ras_type> (ras, bounds);
1810 
1811 
1812       // fill polygon
1813       if (fill.m_a>0) {
1814         ras.add_path(path);
1815         ren_sl.color(agg::rgba8_pre(fill.m_r, fill.m_g, fill.m_b, fill.m_a));
1816 
1817         agg::render_scanlines(ras, sl, ren_sl);
1818       }
1819 
1820       // draw outline
1821       if (outline.m_a>0) {
1822         agg::conv_stroke<agg::path_storage> stroke(path);
1823 
1824         stroke.width(1);
1825 
1826         ren_sl.color(agg::rgba8_pre(outline.m_r, outline.m_g, outline.m_b, outline.m_a));
1827 
1828         ras.add_path(stroke);
1829 
1830         agg::render_scanlines(ras, sl, ren_sl);
1831       }
1832     }
1833 
1834   } //draw_poly_impl
1835 
1836 
draw_poly(const std::vector<point> & corners,const rgba & fill,const rgba & outline,const SWFMatrix & mat,bool masked)1837   void draw_poly(const std::vector<point>& corners, const rgba& fill,
1838     const rgba& outline, const SWFMatrix& mat, bool masked) {
1839 
1840     if (masked && !_alphaMasks.empty()) {
1841 
1842       // apply mask
1843 
1844       typedef agg::scanline_u8_am<agg::alpha_mask_gray8> sl_type;
1845 
1846       sl_type sl(_alphaMasks.back().getMask());
1847 
1848       draw_poly_impl<sl_type>(&corners.front(), corners.size(), fill, outline, sl, mat);
1849 
1850     } else {
1851 
1852       // no mask
1853 
1854       typedef agg::scanline_p8 sl_type; // packed scanline (faster for solid fills)
1855 
1856       sl_type sl;
1857 
1858       draw_poly_impl<sl_type>(&corners.front(), corners.size(), fill, outline, sl, mat);
1859 
1860     }
1861 
1862   }
1863 
1864 
get_stroke_scale()1865   inline float get_stroke_scale() {
1866     return (stage_matrix.get_x_scale() + stage_matrix.get_y_scale()) / 2.0f;
1867   }
1868 
world_to_pixel(int & x,int & y,float world_x,float world_y) const1869   inline void world_to_pixel(int& x, int& y,
1870     float world_x, float world_y) const
1871   {
1872     // negative pixels seems ok here... we don't
1873     // clip to valid range, use world_to_pixel(SWFRect&)
1874     // and Intersect() against valid range instead.
1875     point p(world_x, world_y);
1876     stage_matrix.transform(p);
1877     x = (int)p.x;
1878     y = (int)p.y;
1879   }
1880 
world_to_pixel(const SWFRect & wb) const1881   geometry::Range2d<int> world_to_pixel(const SWFRect& wb) const
1882   {
1883       using namespace gnash::geometry;
1884 
1885     if ( wb.is_null() ) return Range2d<int>(nullRange);
1886     if ( wb.is_world() ) return Range2d<int>(worldRange);
1887 
1888     int xmin, ymin, xmax, ymax;
1889 
1890     world_to_pixel(xmin, ymin, wb.get_x_min(), wb.get_y_min());
1891     world_to_pixel(xmax, ymax, wb.get_x_max(), wb.get_y_max());
1892 
1893     return Range2d<int>(xmin, ymin, xmax, ymax);
1894   }
1895 
1896   point
pixel_to_world(int x,int y) const1897   pixel_to_world(int x, int y) const
1898   {
1899     point p(x, y);
1900     SWFMatrix mat = stage_matrix;
1901     mat.invert().transform(p);
1902     return p;
1903   };
1904 
set_invalidated_region_world()1905   void set_invalidated_region_world() {
1906     InvalidatedRanges ranges;
1907     ranges.setWorld();
1908     set_invalidated_regions(ranges);
1909   }
1910 
set_invalidated_regions(const InvalidatedRanges & ranges)1911   virtual void set_invalidated_regions(const InvalidatedRanges& ranges) {
1912     using gnash::geometry::Range2d;
1913 
1914     int count=0;
1915 
1916     _clipbounds_selected.clear();
1917     _clipbounds.clear();
1918 
1919     // TODO: cache 'visiblerect' and maintain in sync with
1920     //       xres/yres.
1921     Range2d<int> visiblerect;
1922     if ( xres && yres ) visiblerect = Range2d<int>(0, 0, xres-1, yres-1);
1923 
1924     for (size_t rno=0; rno<ranges.size(); ++rno) {
1925 
1926       const Range2d<int>& range = ranges.getRange(rno);
1927 
1928       Range2d<int> pixbounds = Renderer::world_to_pixel(range);
1929 
1930       geometry::Range2d<int> bounds = Intersection(pixbounds, visiblerect);
1931 
1932       if (bounds.isNull()) continue; // out of screen
1933 
1934       assert(bounds.isFinite());
1935 
1936       _clipbounds.push_back(bounds);
1937 
1938       ++count;
1939     }
1940     //log_debug(_("%d inv. bounds in frame"), count);
1941 
1942   }
1943 
1944 
bounds_in_clipping_area(const geometry::Range2d<int> & bounds) const1945   virtual bool bounds_in_clipping_area(const geometry::Range2d<int>& bounds)
1946     const {
1947 
1948     using gnash::geometry::Range2d;
1949 
1950     Range2d<int> pixbounds = Renderer::world_to_pixel(bounds);
1951 
1952     for (const auto& bounds : _clipbounds) {
1953       if (Intersect(pixbounds, bounds))
1954         return true;
1955     }
1956     return false;
1957   }
1958 
getPixel(rgba & color_return,int x,int y) const1959   bool getPixel(rgba& color_return, int x, int y) const {
1960 
1961     if ((x<0) || (y<0) || (x>=xres) || (y>=yres))
1962       return false;
1963 
1964     agg::rgba8 color = m_pixf->pixel(x, y);
1965 
1966     color_return.m_r = color.r;
1967     color_return.m_g = color.g;
1968     color_return.m_b = color.b;
1969     color_return.m_a = color.a;
1970 
1971     return true;
1972   }
1973 
set_scale(float new_xscale,float new_yscale)1974   void set_scale(float new_xscale, float new_yscale) {
1975 
1976     scale_set=true;
1977     stage_matrix.set_identity();
1978     stage_matrix.set_scale(new_xscale/20.0f, new_yscale/20.0f);
1979   }
1980 
set_translation(float xoff,float yoff)1981   void set_translation(float xoff, float yoff) {
1982     stage_matrix.set_translation(xoff, yoff);
1983   }
1984 
getBytesPerPixel() const1985   virtual unsigned int getBytesPerPixel() const {
1986     return bpp/8;
1987   }
1988 
1989 private:  // private variables
1990 
1991     typedef agg::renderer_base<PixelFormat> renderer_base;
1992 
1993     // renderer base
1994     std::unique_ptr<renderer_base> m_rbase;
1995 
1996     // An external renderer.
1997     std::unique_ptr<Renderer> _external;
1998 
1999     int xres;
2000     int yres;
2001     int bpp;  // bits per pixel
2002     gnash::SWFMatrix stage_matrix;  // conversion from TWIPS to pixels
2003     bool scale_set;
2004 
2005     agg::rendering_buffer m_rbuf;
2006 
2007     std::unique_ptr<PixelFormat> m_pixf;
2008 
2009     /// clipping rectangle
2010     ClipBounds _clipbounds;
2011     std::vector< geometry::Range2d<int> const* > _clipbounds_selected;
2012 
2013     // this flag is set while a mask is drawn
2014     bool m_drawing_mask;
2015 
2016     // Alpha mask stack
2017     AlphaMasks _alphaMasks;
2018 
2019     /// Cached fill style list with just one entry used for font rendering
2020     std::vector<FillStyle> m_single_FillStyles;
2021 
2022 
2023 };
2024 
2025 
2026 
2027 // detect the endianess of the host (would prefer to NOT have this function
2028 // here)
is_little_endian_host()2029 bool is_little_endian_host() {
2030 
2031   union {
2032     std::uint16_t word;
2033     struct {
2034       std::uint8_t b1;
2035       std::uint8_t b2;
2036     } s;
2037   } u;
2038 
2039   u.s.b1 = 1;
2040   u.s.b2 = 2;
2041 
2042   return u.word == 0x0201;
2043 
2044 }
2045 
2046 
create_Renderer_agg(const char * pixelformat)2047 DSOEXPORT Renderer_agg_base*  create_Renderer_agg(const char *pixelformat)
2048 {
2049 
2050   if (!pixelformat) return nullptr;
2051 
2052   if (is_little_endian_host())
2053     log_debug("Framebuffer pixel format is %s (little-endian host)", pixelformat);
2054   else
2055     log_debug("Framebuffer pixel format is %s (big-endian host)", pixelformat);
2056 
2057 #ifdef PIXELFORMAT_RGB555
2058   if (!strcmp(pixelformat, "RGB555"))
2059     return new Renderer_agg<agg::pixfmt_rgb555_pre> (16); // yep, 16!
2060 
2061   else
2062 #endif
2063 #ifdef PIXELFORMAT_RGB565
2064   if (!strcmp(pixelformat, "RGB565") || !strcmp(pixelformat, "RGBA16"))
2065     return new Renderer_agg<agg::pixfmt_rgb565_pre> (16);
2066   else
2067 #endif
2068 #ifdef PIXELFORMAT_RGB24
2069   if (!strcmp(pixelformat, "RGB24"))
2070     return new Renderer_agg<agg::pixfmt_rgb24_pre> (24);
2071   else
2072 #endif
2073 #ifdef PIXELFORMAT_BGR24
2074   if (!strcmp(pixelformat, "BGR24"))
2075     return new Renderer_agg<agg::pixfmt_bgr24_pre> (24);
2076   else
2077 #endif
2078 #ifdef PIXELFORMAT_RGBA32
2079   if (!strcmp(pixelformat, "RGBA32"))
2080     return new Renderer_agg<agg::pixfmt_rgba32_pre> (32);
2081   else
2082 #endif
2083 #ifdef PIXELFORMAT_BGRA32
2084   if (!strcmp(pixelformat, "BGRA32"))
2085     return new Renderer_agg<agg::pixfmt_bgra32_pre> (32);
2086 #endif
2087 #ifdef PIXELFORMAT_RGBA32
2088   if (!strcmp(pixelformat, "ARGB32"))
2089     return new Renderer_agg<agg::pixfmt_argb32_pre> (32);
2090   else
2091 #endif
2092 #ifdef PIXELFORMAT_BGRA32
2093   if (!strcmp(pixelformat, "ABGR32"))
2094     return new Renderer_agg<agg::pixfmt_abgr32_pre> (32);
2095 
2096   else
2097 #endif
2098   {
2099       log_error(_("Unknown pixelformat: %s\n"), pixelformat);
2100     return nullptr;
2101     //abort();
2102   }
2103 
2104   return nullptr; // avoid compiler warning
2105 }
2106 
2107 
agg_detect_pixel_format(unsigned int rofs,unsigned int rsize,unsigned int gofs,unsigned int gsize,unsigned int bofs,unsigned int bsize,unsigned int bpp)2108 DSOEXPORT const char *agg_detect_pixel_format(unsigned int rofs,
2109         unsigned int rsize, unsigned int gofs, unsigned int gsize,
2110         unsigned int bofs, unsigned int bsize, unsigned int bpp)
2111 {
2112 
2113   if (!is_little_endian_host() && (bpp>=24)) {
2114 
2115     // Swap bits for big endian hosts, because the following tests assume
2116     // little endians. The pixel format string matches the bytes in memory.
2117 
2118     // This applies for 24 bpp and 32 bpp modes only because AGG uses arrays
2119     // in the premultiply() implementation for these modes. 16 bpp modes
2120     // instead use bit shifting, which is transparent to host endianess.
2121     // See bug #22799.
2122 
2123     rofs = bpp - rofs - rsize;
2124     gofs = bpp - gofs - gsize;
2125     bofs = bpp - bofs - bsize;
2126 
2127   }
2128 
2129   // 15 bits RGB (hicolor)
2130   if ((rofs==10) && (rsize==5)
2131    && (gofs==5) && (gsize==5)
2132    && (bofs==0) && (bsize==5) ) {
2133 
2134     return "RGB555";
2135 
2136   } else
2137   // 16 bits RGB (hicolor)
2138   if ((rofs==11) && (rsize==5)
2139    && (gofs==5) && (gsize==6)
2140    && (bofs==0) && (bsize==5) ) {
2141 
2142     return "RGB565";
2143 
2144   } else
2145 
2146   // 24 bits RGB (truecolor)
2147   if ((rofs==16) && (rsize==8)
2148    && (gofs==8) && (gsize==8)
2149    && (bofs==0) && (bsize==8) ) {
2150 
2151     if (bpp==24)
2152       return "BGR24";
2153     else
2154       return "BGRA32";
2155 
2156   } else
2157   // 24 bits BGR (truecolor)
2158   if ((rofs==0) && (rsize==8)
2159    && (gofs==8) && (gsize==8)
2160    && (bofs==16) && (bsize==8)) {
2161 
2162     if (bpp==24)
2163       return "RGB24";
2164     else
2165       return "RGBA32";
2166 
2167   } else
2168   // special 32 bits (mostly on big endian hosts)
2169   if ((rofs==8) && (rsize==8)
2170    && (gofs==16) && (gsize==8)
2171    && (bofs==24) && (bsize==8)) {
2172 
2173    return "ARGB32";
2174 
2175   } else
2176   // special 32 bits (mostly on big endian hosts)
2177   if ((rofs==24) && (rsize==8)
2178    && (gofs==16) && (gsize==8)
2179    && (bofs==8) && (bsize==8)) {
2180 
2181    return "ABGR32";
2182 
2183   }
2184 
2185   return nullptr; // unknown format
2186 
2187 }
2188 
2189 } // end of namespace gnash
2190 
2191 
2192 // Local Variables:
2193 // mode: C++
2194 // indent-tabs-mode: t
2195 // End:
2196 /* vim: set cindent tabstop=8 softtabstop=4 shiftwidth=4: */
2197