Add initial test for case sensitiveness with mixed version
[gnash.git] / librender / Renderer_ogl.cpp
blobf39ccebe078dcdb0c9efe2f9a378292e9616f175
1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
3 // 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
20 #ifdef HAVE_CONFIG_H
21 #include "gnashconfig.h"
22 #endif
24 #include <cstring>
25 #include <cmath>
26 #include <boost/scoped_ptr.hpp>
28 #include "smart_ptr.h"
29 #include "swf/ShapeRecord.h"
30 #include "gnash.h"
31 #include "RGBA.h"
32 #include "GnashImage.h"
33 #include "GnashTexture.h"
34 #include "GnashNumeric.h"
35 #include "log.h"
36 #include "utility.h"
37 #include "Range2d.h"
38 #include "SWFCxForm.h"
39 #include "FillStyle.h"
40 #include "Transform.h"
42 #if defined(_WIN32) || defined(WIN32)
43 # include <Windows.h>
44 #endif
46 #include "Renderer_ogl.h"
48 #include <boost/utility.hpp>
49 #include <boost/bind.hpp>
51 #ifdef HAVE_VA_VA_GLX_H
52 # include "GnashVaapiImage.h"
53 # include "GnashVaapiTexture.h"
54 #endif
56 // Defined to 1 to disable (slow) anti-aliasing with the accumulation buffer
57 #define NO_ANTIALIASING 1
59 /// \file Renderer_ogl.cpp
60 /// \brief The OpenGL renderer and related code.
61 ///
62 /// So how does this thing work?
63 ///
64 /// 1. Flash graphics are fundamentally incompatible with OpenGL. Flash shapes
65 /// are defined by an arbitrary number of paths, which in turn are formed
66 /// from an arbitrary number of edges. An edge describes a quadratic Bezier
67 /// curve. A shape is defined by at least one path enclosing a space -- this
68 /// space is the shape. Every path may have a left and/or right fill style,
69 /// determining (if the path is thought of as a vector) which side(s) of
70 /// the path is to be filled.
71 /// OpenGL, on the other hand, understands only triangles, lines and points.
72 /// We must break Flash graphics down into primitives that OpenGL can
73 /// understand before we can render them.
74 ///
75 /// 2. First, we must ensure that OpenGL receives only closed shapes with a
76 /// single fill style. Paths with two fill styles are duplicated. Then,
77 /// shapes with
78 /// a left fill style are reversed and the fill style is moved to the right.
79 /// The shapes must be closed, so the tesselator can parse them; this
80 /// involves a fun game of connect-the-dots. Fortunately, Flash guarantees
81 /// that shapes are always closed and that they're never self-intersecting.
82 ///
83 /// 3. Now that we have a consistent set of shapes, we can interpolate the
84 /// Bezier curves of which each path is made of. OpenGL can do this for us,
85 /// using evaluators, but we currently do it ourselves.
86 ///
87 /// 4. Being the proud owners of a brand new set of interpolated coordinates,
88 /// we can feed the coordinates into the GLU tesselator. The tesselator will
89 /// break our complex (but never self-intersecting) polygon into OpenGL-
90 /// grokkable primitives (say, a triangle fan or strip). We let the
91 /// tesselator worry about that part. When the tesselator is finished, all
92 /// we have to do is set up the fill style and draw the primitives given to
93 /// us. The GLU tesselator will take care of shapes having inner boundaries
94 /// (for example a donut shape). This makes life a LOT easier!
97 // TODO:
98 // - Profiling!
99 // - Optimize code:
100 // * Use display lists
101 // * Use better suited standard containers
102 // * convert to double at a later stage (oglVertex)
103 // * keep data for less time
104 // * implement hardware accelerated gradients. Most likely this will require
105 // the use of fragment shader language.
107 // * The "Programming Tips" in the OpenGL "red book" discusses a coordinate system
108 // that would give "exact two-dimensional rasterization". AGG uses a similar
109 // system; consider the benefits and drawbacks of switching.
112 namespace gnash {
114 namespace {
115 const CachedBitmap* createGradientBitmap(const GradientFill& gf,
116 Renderer& renderer);
119 namespace {
121 class bitmap_info_ogl : public CachedBitmap
123 public:
125 /// Set line and fill styles for mesh & line_strip rendering.
126 enum bitmap_wrap_mode
128 WRAP_REPEAT,
129 WRAP_CLAMP
132 bitmap_info_ogl(std::auto_ptr<image::GnashImage> image, GLenum pixelformat,
133 bool ogl_accessible);
135 ~bitmap_info_ogl();
137 /// TODO: implement this meaningfully.
138 virtual void dispose() {
139 _disposed = true;
142 virtual bool disposed() const {
143 return _disposed;
146 virtual image::GnashImage& image() {
147 if (_cache.get()) return *_cache;
148 switch (_pixel_format) {
149 case GL_RGB:
150 _cache.reset(new image::ImageRGB(_orig_width, _orig_height));
151 break;
152 case GL_RGBA:
153 _cache.reset(new image::ImageRGBA(_orig_width, _orig_height));
154 break;
155 default:
156 std::abort();
158 std::fill(_cache->begin(), _cache->end(), 0xff);
159 return *_cache;
162 void apply(const gnash::SWFMatrix& bitmap_matrix,
163 bitmap_wrap_mode wrap_mode) const;
164 private:
165 inline bool ogl_accessible() const;
166 void setup() const;
167 void upload(boost::uint8_t* data, size_t width, size_t height) const;
169 mutable boost::scoped_ptr<image::GnashImage> _img;
170 mutable boost::scoped_ptr<image::GnashImage> _cache;
171 GLenum _pixel_format;
172 GLenum _ogl_img_type;
173 mutable bool _ogl_accessible;
174 mutable GLuint _texture_id;
175 size_t _orig_width;
176 size_t _orig_height;
177 bool _disposed;
180 /// Style handler
182 /// Transfer FillStyles to the ogl renderer.
183 struct StyleHandler : boost::static_visitor<>
185 StyleHandler(const SWFCxForm& c, Renderer& r)
187 _cx(c),
188 _renderer(r)
191 void operator()(const GradientFill& f) const {
193 const bitmap_info_ogl* binfo = static_cast<const bitmap_info_ogl*>(
194 createGradientBitmap(f, _renderer));
196 SWFMatrix m = f.matrix();
197 binfo->apply(m, bitmap_info_ogl::WRAP_CLAMP);
200 void operator()(const SolidFill& f) const {
201 const rgba c = _cx.transform(f.color());
202 glColor4ub(c.m_r, c.m_g, c.m_b, c.m_a);
205 void operator()(const BitmapFill& f) const {
206 const bitmap_info_ogl* binfo = static_cast<const bitmap_info_ogl*>(
207 f.bitmap());
208 binfo->apply(f.matrix(), f.type() == BitmapFill::TILED ?
209 bitmap_info_ogl::WRAP_REPEAT : bitmap_info_ogl::WRAP_CLAMP);
212 private:
213 const SWFCxForm& _cx;
214 Renderer& _renderer;
220 #ifdef OSMESA_TESTING
222 class OSRenderMesa : public boost::noncopyable
224 public:
225 OSRenderMesa(size_t width, size_t height)
226 : _width(width),
227 _height(height),
228 _buffer(new boost::uint8_t[width * height * 3]),
229 #if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305
230 _context(OSMesaCreateContextExt(OSMESA_RGB, 0, 2, 0, NULL))
231 #else
232 _context(OSMesaCreateContext(OSMESA_RGB, NULL))
233 #endif
235 if (!_context) {
236 log_error("OSMesaCreateContext failed!");
237 return; // FIXME: throw an exception?
240 if (!OSMesaMakeCurrent(_context, _buffer.get(), GL_UNSIGNED_BYTE, width,
241 height)) {
242 log_error("OSMesaMakeCurrent failed!");
243 return;
246 // FIXME: is there any reason to do this?
247 OSMesaColorClamp(GL_TRUE);
249 log_debug("OSMesa handle successfully created. with width %d"
250 " and height %d.", width, height);
253 ~OSRenderMesa()
255 if (!_context) {
256 return;
258 OSMesaDestroyContext(_context);
261 bool getPixel(rgba& color_out, int x, int y) const
263 glFinish(); // Force pending commands out (and wait until they're done).
265 if (x > _width || y > _height) {
266 return false;
269 ptrdiff_t offset = (_height - y) * (_width * 3) + x * 3;
270 color_out.set(_buffer[offset], _buffer[offset+1], _buffer[offset+2], 255);
272 return true;
275 unsigned int getBitsPerPixel() const
277 return 24;
280 private:
281 size_t _width;
282 size_t _height;
283 boost::scoped_array<boost::uint8_t> _buffer;
284 OSMesaContext _context;
287 #endif // OSMESA_TESTING
290 typedef std::vector<Path> PathVec;
292 class oglScopeEnable : public boost::noncopyable
294 public:
295 oglScopeEnable(GLenum capability)
296 :_cap(capability)
298 glEnable(_cap);
301 ~oglScopeEnable()
303 glDisable(_cap);
305 private:
306 GLenum _cap;
310 class oglScopeMatrix : public boost::noncopyable
312 public:
313 oglScopeMatrix(const SWFMatrix& m)
315 glPushMatrix();
317 // Multiply (AKA "append") the new SWFMatrix with the current OpenGL one.
318 float mat[16];
319 memset(&mat[0], 0, sizeof(mat));
320 mat[0] = m.sx / 65536.0f;
321 mat[1] = m.shx / 65536.0f;
322 mat[4] = m.shy / 65536.0f;
323 mat[5] = m.sy / 65536.0f;
324 mat[10] = 1;
325 mat[12] = m.tx;
326 mat[13] = m.ty;
327 mat[15] = 1;
328 glMultMatrixf(mat);
331 ~oglScopeMatrix()
333 glPopMatrix();
337 static void
338 check_error()
340 GLenum error = glGetError();
342 if (error == GL_NO_ERROR) {
343 return;
346 log_error("OpenGL: %s", gluErrorString(error));
349 /// @ret A point in the middle of points a and b, that is, the middle of a line
350 /// drawn from a to b.
351 point middle(const point& a, const point& b)
353 return point(0.5 * (a.x + b.x), 0.5 * (a.y + b.y));
356 namespace {
358 class
359 PointSerializer
361 public:
362 PointSerializer(std::vector<boost::int16_t>& dest)
364 _dest(dest)
367 void operator()(const point& p)
369 _dest.push_back(p.x);
370 _dest.push_back(p.y);
372 private:
373 std::vector<boost::int16_t>& _dest;
378 // Unfortunately, we can't use OpenGL as-is to interpolate the curve for us. It
379 // is legal for Flash coordinates to be outside of the viewport, which will
380 // be ignored by OpenGL's feedback mode. Feedback mode
381 // will simply not return those coordinates, which will destroy a shape which
382 // is partly off-screen.
384 // So, if we transform the coordinates to be always positive, it should be
385 // possible to use evaluators. This then presents another problem: what if
386 // one coordinate is negative and the other is not, and what if both of
387 // those are outside of the viewport?
389 // one solution would be to use feedback mode unless one of the coordinates
390 // is outside of the viewport.
391 void trace_curve(const point& startP, const point& controlP,
392 const point& endP, std::vector<oglVertex>& coords)
394 // Midpoint on line between two endpoints.
395 point mid = middle(startP, endP);
397 // Midpoint on the curve.
398 point q = middle(mid, controlP);
400 if (mid.distance(q) < 0.1 /*error tolerance*/) {
401 coords.push_back(oglVertex(endP));
402 } else {
403 // Error is too large; subdivide.
404 trace_curve(startP, middle(startP, controlP), q, coords);
406 trace_curve(q, middle(controlP, endP), endP, coords);
412 std::vector<oglVertex> interpolate(const std::vector<Edge>& edges,
413 const float& anchor_x, const float& anchor_y)
415 point anchor(anchor_x, anchor_y);
417 std::vector<oglVertex> shape_points;
418 shape_points.push_back(oglVertex(anchor));
420 for (std::vector<Edge>::const_iterator it = edges.begin(), end = edges.end();
421 it != end; ++it) {
422 const Edge& the_edge = *it;
424 point target(the_edge.ap.x, the_edge.ap.y);
426 if (the_edge.straight()) {
427 shape_points.push_back(oglVertex(target));
428 } else {
429 point control(the_edge.cp.x, the_edge.cp.y);
431 trace_curve(anchor, control, target, shape_points);
433 anchor = target;
436 return shape_points;
440 // FIXME: OSX doesn't like void (*)().
441 Tesselator::Tesselator()
442 : _tessobj(gluNewTess())
444 gluTessCallback(_tessobj, GLU_TESS_ERROR,
445 reinterpret_cast<GLUCALLBACKTYPE>(Tesselator::error));
446 gluTessCallback(_tessobj, GLU_TESS_COMBINE_DATA,
447 reinterpret_cast<GLUCALLBACKTYPE>(Tesselator::combine));
449 gluTessCallback(_tessobj, GLU_TESS_BEGIN,
450 reinterpret_cast<GLUCALLBACKTYPE>(glBegin));
451 gluTessCallback(_tessobj, GLU_TESS_END,
452 reinterpret_cast<GLUCALLBACKTYPE>(glEnd));
454 gluTessCallback(_tessobj, GLU_TESS_VERTEX,
455 reinterpret_cast<GLUCALLBACKTYPE>(glVertex3dv));
457 #if 0
458 // for testing, draw only the outside of shapes.
459 gluTessProperty(_tessobj, GLU_TESS_BOUNDARY_ONLY, GL_TRUE);
460 #endif
462 // all coordinates lie in the x-y plane
463 // this speeds up tesselation
464 gluTessNormal(_tessobj, 0.0, 0.0, 1.0);
467 Tesselator::~Tesselator()
469 gluDeleteTess(_tessobj);
472 void
473 Tesselator::beginPolygon()
475 gluTessBeginPolygon(_tessobj, this);
478 void Tesselator::beginContour()
480 gluTessBeginContour(_tessobj);
483 void
484 Tesselator::feed(std::vector<oglVertex>& vertices)
486 for (std::vector<oglVertex>::const_iterator it = vertices.begin(), end = vertices.end();
487 it != end; ++it) {
488 GLdouble* vertex = const_cast<GLdouble*>(&(*it)._x);
489 gluTessVertex(_tessobj, vertex, vertex);
493 void
494 Tesselator::endContour()
496 gluTessEndContour(_tessobj);
499 void
500 Tesselator::tesselate()
502 gluTessEndPolygon(_tessobj);
504 for (std::vector<GLdouble*>::iterator it = _vertices.begin(),
505 end = _vertices.end(); it != end; ++it) {
506 delete [] *it;
509 _vertices.clear();
512 void
513 Tesselator::rememberVertex(GLdouble* v)
515 _vertices.push_back(v);
520 // static
521 void
522 Tesselator::error(GLenum error)
524 log_error("GLU: %s", gluErrorString(error));
527 // static
528 void
529 Tesselator::combine(GLdouble coords [3], void **/*vertex_data[4] */,
530 GLfloat * /* weight[4] */, void **outData, void* userdata)
532 Tesselator* tess = (Tesselator*)userdata;
533 assert(tess);
535 GLdouble* v = new GLdouble[3];
536 v[0] = coords[0];
537 v[1] = coords[1];
538 v[2] = coords[2];
540 *outData = v;
542 tess->rememberVertex(v);
546 bool isEven(const size_t& n)
548 return n % 2 == 0;
552 bitmap_info_ogl::bitmap_info_ogl(std::auto_ptr<image::GnashImage> image,
553 GLenum pixelformat, bool ogl_accessible)
555 _img(image.release()),
556 _pixel_format(pixelformat),
557 _ogl_img_type(_img->height() == 1 ? GL_TEXTURE_1D : GL_TEXTURE_2D),
558 _ogl_accessible(ogl_accessible),
559 _texture_id(0),
560 _orig_width(_img->width()),
561 _orig_height(_img->height()),
562 _disposed(false)
564 if (!_ogl_accessible) {
565 return;
568 setup();
571 bitmap_info_ogl::~bitmap_info_ogl()
573 glDeleteTextures(1, &_texture_id);
574 glDisable(_ogl_img_type);
577 void
578 bitmap_info_ogl::setup() const
580 oglScopeEnable enabler(_ogl_img_type);
582 glGenTextures(1, &_texture_id);
583 glBindTexture(_ogl_img_type, _texture_id);
585 bool resize = false;
586 if (_img->height() == 1) {
587 if (!isEven(_img->width())) {
588 resize = true;
591 else {
592 if (!isEven(_img->width()) || !isEven(_img->height())) {
593 resize = true;
597 if (!resize) {
598 upload(_img->begin(), _img->width(), _img->height());
600 else {
602 size_t w = 1; while (w < _img->width()) { w <<= 1; }
603 size_t h = 1;
604 if (_img->height() != 1) {
605 while (h < _img->height()) { h <<= 1; }
608 boost::scoped_array<boost::uint8_t> resized_data(
609 new boost::uint8_t[w * h * _img->channels()]);
610 // Q: Would mipmapping these textures aid in performance?
612 GLint rv = gluScaleImage(_pixel_format, _img->width(),
613 _img->height(), GL_UNSIGNED_BYTE, _img->begin(), w, h,
614 GL_UNSIGNED_BYTE, resized_data.get());
615 if (rv != 0) {
616 Tesselator::error(rv);
617 assert(0);
620 upload(resized_data.get(), w, h);
623 _img.reset();
626 void
627 bitmap_info_ogl::upload(boost::uint8_t* data, size_t width, size_t height) const
629 glTexParameteri(_ogl_img_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
631 // FIXME: confirm that OpenGL can handle this image
633 if (_ogl_img_type == GL_TEXTURE_1D) {
634 glTexImage1D(GL_TEXTURE_1D, 0, _pixel_format, width,
635 0, _pixel_format, GL_UNSIGNED_BYTE, data);
637 } else {
638 glTexImage2D(_ogl_img_type, 0, _pixel_format, width, height,
639 0, _pixel_format, GL_UNSIGNED_BYTE, data);
644 void
645 bitmap_info_ogl::apply(const gnash::SWFMatrix& bitmap_matrix,
646 bitmap_wrap_mode wrap_mode) const
648 // TODO: use the altered data. Currently it doesn't display anything,
649 // so I don't feel obliged to implement this!
651 glEnable(_ogl_img_type);
653 glEnable(GL_TEXTURE_GEN_S);
654 glEnable(GL_TEXTURE_GEN_T);
656 if (!_ogl_accessible) {
657 // renderer context wasn't available when this class was instantiated.
658 _ogl_accessible=true;
659 setup();
662 glEnable(_ogl_img_type);
663 glEnable(GL_TEXTURE_GEN_S);
664 glEnable(GL_TEXTURE_GEN_T);
666 glBindTexture(_ogl_img_type, _texture_id);
668 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
670 if (wrap_mode == WRAP_CLAMP) {
671 glTexParameteri(_ogl_img_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
672 glTexParameteri(_ogl_img_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
673 } else {
674 glTexParameteri(_ogl_img_type, GL_TEXTURE_WRAP_S, GL_REPEAT);
675 glTexParameteri(_ogl_img_type, GL_TEXTURE_WRAP_T, GL_REPEAT);
678 // Set up the bitmap SWFMatrix for texgen.
680 float inv_width = 1.0f / _orig_width;
681 float inv_height = 1.0f / _orig_height;
683 const gnash::SWFMatrix& m = bitmap_matrix;
684 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
685 float p[4] = { 0, 0, 0, 0 };
686 p[0] = m.sx / 65536.0f * inv_width;
687 p[1] = m.shy / 65536.0f * inv_width;
688 p[3] = m.tx * inv_width;
689 glTexGenfv(GL_S, GL_OBJECT_PLANE, p);
691 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
692 p[0] = m.shx / 65536.0f * inv_height;
693 p[1] = m.shy / 65536.0f * inv_height;
694 p[3] = m.ty * inv_height;
695 glTexGenfv(GL_T, GL_OBJECT_PLANE, p);
699 template<typename C, typename T, typename R, typename A>
700 void
701 for_each(C& container, R (T::*pmf)(const A&),const A& arg)
703 std::for_each(container.begin(), container.end(),
704 boost::bind(pmf, _1, boost::ref(arg)));
709 class DSOEXPORT Renderer_ogl : public Renderer
711 public:
712 Renderer_ogl()
713 : _xscale(1.0),
714 _yscale(1.0),
715 _drawing_mask(false)
719 std::string description() const { return "OpenGL"; }
721 void init()
723 // Turn on alpha blending.
724 // FIXME: do this when it's actually used?
725 glEnable(GL_BLEND);
726 // This blend operation is best for rendering antialiased points and lines in
727 // no particular order.
728 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
730 // Turn on line smoothing. Antialiased lines can be used to
731 // smooth the outsides of shapes.
732 glEnable(GL_LINE_SMOOTH);
734 // Use fastest line smoothing since additional anti-aliasing will happen later.
735 glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST); // GL_NICEST, GL_FASTEST, GL_DONT_CARE
737 glMatrixMode(GL_PROJECTION);
739 float oversize = 1.0;
741 // Flip the image, since (0,0) by default in OpenGL is the bottom left.
742 gluOrtho2D(-oversize, oversize, oversize, -oversize);
743 // Restore the SWFMatrix mode to the default.
744 glMatrixMode(GL_MODELVIEW);
745 glLoadIdentity();
748 #ifdef FIX_I810_LOD_BIAS
749 // If 2D textures weren't previously enabled, enable
750 // them now and force the driver to notice the update,
751 // then disable them again.
752 if (!glIsEnabled(GL_TEXTURE_2D)) {
753 // Clearing a mask of zero *should* have no
754 // side effects, but coupled with enbling
755 // GL_TEXTURE_2D it works around a segmentation
756 // fault in the driver for the Intel 810 chip.
757 oglScopeEnable enabler(GL_TEXTURE_2D);
758 glClear(0);
760 #endif
762 glShadeModel(GL_FLAT);
766 ~Renderer_ogl()
770 inline bool
771 ogl_accessible() const
773 #if defined(_WIN32) || defined(WIN32)
774 return wglGetCurrentContext();
775 #elif defined(__APPLE_CC__)
776 return aglGetCurrentContext();
777 #else
778 # ifdef OSMESA_TESTING
779 if (_offscreen.get()) {
780 return OSMesaGetCurrentContext();
782 # endif
783 return glXGetCurrentContext();
784 #endif
787 virtual CachedBitmap* createCachedBitmap(std::auto_ptr<image::GnashImage> im)
789 switch (im->type()) {
790 case image::TYPE_RGB:
792 std::auto_ptr<image::GnashImage> rgba(
793 new image::ImageRGBA(im->width(), im->height()));
795 image::GnashImage::iterator it = rgba->begin();
796 for (size_t i = 0; i < im->size(); ++i) {
797 *it++ = *(im->begin() + i);
798 if (!(i % 3)) *it++ = 0xff;
800 im = rgba;
802 case image::TYPE_RGBA:
803 return new bitmap_info_ogl(im, GL_RGBA, ogl_accessible());
804 default:
805 std::abort();
809 boost::shared_ptr<GnashTexture> getCachedTexture(image::GnashImage *frame)
811 boost::shared_ptr<GnashTexture> texture;
812 GnashTextureFormat frameFormat(frame->type());
813 unsigned int frameFlags;
815 switch (frame->location()) {
816 case image::GNASH_IMAGE_CPU:
817 frameFlags = 0;
818 break;
819 #ifdef HAVE_VA_VA_GLX_H
820 case image::GNASH_IMAGE_GPU:
821 frameFlags = GNASH_TEXTURE_VAAPI;
822 break;
823 #endif
824 default:
825 assert(0);
826 return texture;
829 // Look for a texture with the same dimensions and type
830 std::list< boost::shared_ptr<GnashTexture> >::iterator it;
831 for (it = _cached_textures.begin(); it != _cached_textures.end(); it++) {
832 if ((*it)->width() == frame->width() &&
833 (*it)->height() == frame->height() &&
834 (*it)->internal_format() == frameFormat.internal_format() &&
835 (*it)->format() == frameFormat.format() &&
836 (*it)->flags() == frameFlags)
837 break;
840 // Found texture and remove it from cache. It will be pushed
841 // back into the cache when rendering is done, in end_display()
842 if (it != _cached_textures.end()) {
843 texture = *it;
844 _cached_textures.erase(it);
847 // Otherwise, create one and empty cache because they may no
848 // longer be referenced
849 else {
850 _cached_textures.clear();
852 switch (frame->location()) {
853 case image::GNASH_IMAGE_CPU:
854 texture.reset(new GnashTexture(frame->width(),
855 frame->height(),
856 frame->type()));
857 break;
858 case image::GNASH_IMAGE_GPU:
859 // This case should never be reached if vaapi is not
860 // enabled; but has to be handled to keep the compiler
861 // happy.
862 #ifdef HAVE_VA_VA_GLX_H
863 texture.reset(new GnashVaapiTexture(frame->width(),
864 frame->height(),
865 frame->type()));
866 #endif
867 break;
871 assert(texture->width() == frame->width());
872 assert(texture->height() == frame->height());
873 assert(texture->internal_format() == frameFormat.internal_format());
874 assert(texture->format() == frameFormat.format());
875 assert(texture->flags() == frameFlags);
876 return texture;
879 // Since we store drawing operations in display lists, we take special care
880 // to store video frame operations in their own display list, lest they be
881 // anti-aliased with the rest of the drawing. Since display lists cannot be
882 // concatenated this means we'll add up with several display lists for normal
883 // drawing operations.
884 virtual void drawVideoFrame(image::GnashImage* frame, const Transform& xform,
885 const SWFRect* bounds, bool /*smooth*/)
887 GLint index;
889 glGetIntegerv(GL_LIST_INDEX, &index);
891 if (index >= 255) {
892 log_error("An insane number of video frames have been requested to be "
893 "drawn. Further video frames will be ignored.");
894 return;
897 glEndList();
899 boost::shared_ptr<GnashTexture> texture = getCachedTexture(frame);
900 if (!texture.get())
901 return;
903 switch (frame->location()) {
904 case image::GNASH_IMAGE_CPU:
905 texture->update(frame->begin());
906 break;
907 #ifdef HAVE_VA_VA_GLX_H
908 case image::GNASH_IMAGE_GPU:
909 dynamic_cast<GnashVaapiTexture *>(texture.get())->update(dynamic_cast<GnashVaapiImage *>(frame)->surface());
910 break;
911 #endif
912 default:
913 assert(0);
914 return;
916 _render_textures.push_back(texture);
918 glGenLists(2);
920 ++index;
922 glNewList(index, GL_COMPILE);
923 _render_indices.push_back(index);
925 reallyDrawVideoFrame(texture, &xform.matrix, bounds);
927 glEndList();
929 ++index;
931 glNewList(index, GL_COMPILE);
932 _render_indices.push_back(index);
935 private:
936 void reallyDrawVideoFrame(boost::shared_ptr<GnashTexture> texture, const SWFMatrix* m, const SWFRect* bounds)
938 glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);
939 glPushMatrix();
941 gnash::point l, u;
942 m->transform(&l, point(bounds->get_x_min(), bounds->get_y_min()));
943 m->transform(&u, point(bounds->get_x_max(), bounds->get_y_max()));
944 const unsigned int w = u.x - l.x;
945 const unsigned int h = u.y - l.y;
947 texture->bind();
948 glTranslatef(l.x, l.y, 0.0f);
949 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
950 glBegin(GL_QUADS);
952 glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0);
953 glTexCoord2f(0.0f, 1.0f); glVertex2i(0, h);
954 glTexCoord2f(1.0f, 1.0f); glVertex2i(w, h);
955 glTexCoord2f(1.0f, 0.0f); glVertex2i(w, 0);
957 glEnd();
958 texture->release();
960 glPopMatrix();
961 glPopAttrib();
964 // FIXME
965 geometry::Range2d<int>
966 world_to_pixel(const SWFRect& worldbounds)
968 // TODO: verify this is correct
969 geometry::Range2d<int> ret(worldbounds.getRange());
970 ret.scale(1.0/20.0); // twips to pixels
971 return ret;
974 // FIXME
975 point
976 pixel_to_world(int x, int y)
978 // TODO: verify this is correct
979 return point(pixelsToTwips(x), pixelsToTwips(y));
982 public:
984 virtual Renderer* startInternalRender(image::GnashImage& /*im*/) {
985 return 0;
988 virtual void endInternalRender() {}
990 virtual void begin_display(
991 const rgba& bg_color,
992 int viewport_width, int viewport_height,
993 float x0, float x1, float y0, float y1)
995 glViewport(0, 0, viewport_width, viewport_height);
996 glLoadIdentity();
998 gluOrtho2D(x0, x1, y0, y1);
1000 _width = std::fabs(x1 - x0);
1001 _height = std::fabs(y1 - y0);
1003 glScalef(static_cast<float>(twipsToPixels(_width)) /
1004 static_cast<float>(viewport_width),
1005 static_cast<float>(twipsToPixels(_height)) /
1006 static_cast<float>(viewport_height), 1.0f);
1008 // Setup the clear color. The actual clearing will happen in end_display.
1009 if (bg_color.m_a) {
1010 glClearColor(bg_color.m_r / 255.0, bg_color.m_g / 255.0, bg_color.m_b / 255.0,
1011 bg_color.m_a / 255.0);
1012 } else {
1013 glClearColor(1.0, 1.0, 1.0, 1.0);
1016 glGenLists(1);
1018 // Start a new display list which will contain almost everything we draw.
1019 glNewList(1, GL_COMPILE);
1020 _render_indices.push_back(1);
1024 virtual void
1025 end_display()
1027 glEndList();
1029 #if NO_ANTIALIASING
1030 // Don't use accumulation buffer based anti-aliasing
1031 glClear(GL_COLOR_BUFFER_BIT);
1032 glCallLists(_render_indices.size(), GL_UNSIGNED_BYTE,
1033 &_render_indices.front());
1034 #else
1035 // This is a table of randomly generated numbers between -0.5 and 0.5.
1036 struct {
1037 GLfloat x;
1038 GLfloat y;
1039 } points [] = {
1040 { -0.448823, 0.078771 },
1041 { -0.430852, 0.240592 },
1042 { -0.208887, -0.492535 },
1043 { -0.061232, -1.109432 },
1044 { -0.034984, -0.247317 },
1045 { -0.367119, -0.440909 },
1046 { 0.047688, 0.315757 },
1047 { 0.434600, -0.204068 }
1050 int numPoints = 8;
1052 GLint viewport[4];
1053 glGetIntegerv (GL_VIEWPORT, viewport);
1054 const GLint& viewport_width = viewport[2],
1055 viewport_height = viewport[3];
1057 glClearAccum(0.0, 0.0, 0.0, 0.0);
1059 glClear(GL_ACCUM_BUFFER_BIT);
1061 for (int i = 0; i < numPoints; ++i) {
1063 glClear(GL_COLOR_BUFFER_BIT);
1064 glPushMatrix ();
1066 glTranslatef (points[i].x * _width / viewport_width,
1067 points[i].y * _height / viewport_height, 0.0);
1069 glCallLists(_render_indices.size(), GL_UNSIGNED_BYTE,
1070 &_render_indices.front());
1072 glPopMatrix ();
1074 glAccum(GL_ACCUM, 1.0/numPoints);
1077 glAccum (GL_RETURN, 1.0);
1078 #endif
1080 #if 0
1081 GLint box[4];
1082 glGetIntegerv(GL_SCISSOR_BOX, box);
1084 int x = pixelsToTwips(box[0]),
1085 y = pixelsToTwips(box[1]),
1086 w = pixelsToTwips(box[2]),
1087 h = pixelsToTwips(box[3]);
1089 glRectd(x, y - h, x + w, y + h);
1090 #endif
1092 glDeleteLists(1, _render_indices.size());
1093 _render_indices.clear();
1095 for (size_t i = 0; i < _render_textures.size(); i++)
1096 _cached_textures.push_front(_render_textures[i]);
1097 _render_textures.clear();
1099 check_error();
1101 glFlush(); // Make OpenGL execute all commands in the buffer.
1104 /// Draw a line-strip directly, using a thin, solid line.
1106 /// Can be used to draw empty boxes and cursors.
1107 virtual void drawLine(const std::vector<point>& coords, const rgba& color,
1108 const SWFMatrix& mat)
1110 oglScopeMatrix scope_mat(mat);
1112 const size_t numPoints = coords.size();
1114 glColor3ub(color.m_r, color.m_g, color.m_b);
1116 std::vector<boost::int16_t> pointList;
1117 pointList.reserve(numPoints * 2);
1118 std::for_each(coords.begin(), coords.end(), PointSerializer(pointList));
1120 // Send the line-strip to OpenGL
1121 glEnableClientState(GL_VERTEX_ARRAY);
1122 glVertexPointer(2, GL_SHORT, 0 /* tight packing */, &pointList.front());
1123 glDrawArrays(GL_LINE_STRIP, 0, numPoints);
1125 // Draw a dot on the beginning and end coordinates to round lines.
1126 // glVertexPointer: skip all but the first and last coordinates in the line.
1127 glVertexPointer(2, GL_SHORT, (sizeof(boost::int16_t) * 2) *
1128 (numPoints - 1), &pointList.front());
1129 glEnable(GL_POINT_SMOOTH); // Draw a round (antialiased) point.
1130 glDrawArrays(GL_POINTS, 0, 2);
1131 glDisable(GL_POINT_SMOOTH);
1132 glPointSize(1); // return to default
1134 glDisableClientState(GL_VERTEX_ARRAY);
1137 // NOTE: this implementation can't handle complex polygons (such as concave
1138 // polygons.
1139 virtual void draw_poly(const point* corners, size_t corner_count,
1140 const rgba& fill, const rgba& outline, const SWFMatrix& mat, bool /* masked */)
1142 if (corner_count < 1) {
1143 return;
1146 oglScopeMatrix scope_mat(mat);
1148 glColor4ub(fill.m_r, fill.m_g, fill.m_b, fill.m_a);
1150 glEnableClientState(GL_VERTEX_ARRAY);
1152 // Draw simple polygon
1153 glVertexPointer(2, GL_FLOAT, 0 /* tight packing */, corners);
1154 glDrawArrays(GL_POLYGON, 0, corner_count);
1156 // Draw outline
1157 glLineWidth(1.0);
1158 glColor4ub(outline.m_r, outline.m_g, outline.m_b, outline.m_a);
1159 glVertexPointer(2, GL_FLOAT, 0 /* tight packing */, corners);
1160 glDrawArrays(GL_LINE_LOOP, 0, corner_count);
1162 glDisableClientState(GL_VERTEX_ARRAY);
1164 glPopMatrix();
1167 virtual void set_antialiased(bool /* enable */ )
1169 log_unimpl("set_antialiased");
1172 virtual void begin_submit_mask()
1174 PathVec mask;
1175 _masks.push_back(mask);
1177 _drawing_mask = true;
1180 virtual void end_submit_mask()
1182 _drawing_mask = false;
1184 apply_mask();
1187 /// Apply the current mask; nesting is supported.
1189 /// This method marks the stencil buffer by incrementing every stencil pixel
1190 /// by one every time a solid from one of the current masks is drawn. When
1191 /// all the mask solids are drawn, we change the stencil operation to permit
1192 /// only drawing where all masks have drawn, in other words, where all masks
1193 /// intersect, or in even other words, where the stencil pixel buffer equals
1194 /// the number of masks.
1195 void apply_mask()
1197 if (_masks.empty()) {
1198 return;
1201 glEnable(GL_STENCIL_TEST);
1203 glClearStencil(0x0); // FIXME: default is zero, methinks
1204 glClear(GL_STENCIL_BUFFER_BIT);
1206 // GL_NEVER means the stencil test will never succeed, so OpenGL wil never
1207 // continue to the drawing stage.
1208 glStencilFunc (GL_NEVER, 0x1, 0x1);
1210 glStencilOp (GL_INCR /* Stencil test fails */,
1211 GL_KEEP /* ignored */,
1212 GL_KEEP /* Stencil test passes; never happens */);
1214 // Call add_paths for each mask.
1215 std::for_each(_masks.begin(), _masks.end(),
1216 boost::bind(&Renderer_ogl::add_paths, this, _1));
1218 glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
1219 glStencilFunc(GL_EQUAL, _masks.size(), _masks.size());
1222 void
1223 add_paths(const PathVec& path_vec)
1225 SWFCxForm dummy_cx;
1226 std::vector<FillStyle> dummy_fs;
1228 FillStyle coloring = FillStyle(SolidFill(rgba(0, 0, 0, 0)));
1230 dummy_fs.push_back(coloring);
1232 std::vector<LineStyle> dummy_ls;
1234 draw_subshape(path_vec, SWFMatrix(), dummy_cx, dummy_fs, dummy_ls);
1237 virtual void disable_mask()
1239 _masks.pop_back();
1241 if (_masks.empty()) {
1242 glDisable(GL_STENCIL_TEST);
1243 } else {
1244 apply_mask();
1248 #if 0
1249 void print_path(const Path& path)
1251 std::cout << "Origin: ("
1252 << path.ap.x
1253 << ", "
1254 << path.ap.y
1255 << ") fill0: "
1256 << path.m_fill0
1257 << " fill1: "
1258 << path.m_fill1
1259 << " line: "
1260 << path.m_line
1261 << " new shape: "
1262 << path.m_new_shape
1263 << " number of edges: "
1264 << path.m_edges.size()
1265 << " edge endpoint: ("
1266 << path.m_edges.back().ap.x
1267 << ", "
1268 << path.m_edges.back().ap.y
1269 << " ) points:";
1271 for (std::vector<edge>::const_iterator it = path.m_edges.begin(), end = path.m_edges.end();
1272 it != end; ++it) {
1273 const edge& cur_edge = *it;
1274 std::cout << "( " << cur_edge.ap.x << ", " << cur_edge.ap.y << ") ";
1276 std::cout << std::endl;
1278 #endif
1281 Path reverse_path(const Path& cur_path)
1283 const Edge& cur_end = cur_path.m_edges.back();
1285 float prev_cx = cur_end.cp.x;
1286 float prev_cy = cur_end.cp.y;
1288 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);
1290 float prev_ax = cur_end.ap.x;
1291 float prev_ay = cur_end.ap.y;
1293 for (std::vector<Edge>::const_reverse_iterator it = cur_path.m_edges.rbegin()+1, end = cur_path.m_edges.rend();
1294 it != end; ++it) {
1295 const Edge& cur_edge = *it;
1297 if (prev_ax == prev_cx && prev_ay == prev_cy) {
1298 prev_cx = cur_edge.ap.x;
1299 prev_cy = cur_edge.ap.y;
1302 Edge newedge(prev_cx, prev_cy, cur_edge.ap.x, cur_edge.ap.y);
1304 newpath.m_edges.push_back(newedge);
1306 prev_cx = cur_edge.cp.x;
1307 prev_cy = cur_edge.cp.y;
1308 prev_ax = cur_edge.ap.x;
1309 prev_ay = cur_edge.ap.y;
1313 Edge newlastedge(prev_cx, prev_cy, cur_path.ap.x, cur_path.ap.y);
1314 newpath.m_edges.push_back(newlastedge);
1316 return newpath;
1319 const Path* find_connecting_path(const Path& to_connect,
1320 std::list<const Path*> path_refs)
1323 float target_x = to_connect.m_edges.back().ap.x;
1324 float target_y = to_connect.m_edges.back().ap.y;
1326 if (target_x == to_connect.ap.x &&
1327 target_y == to_connect.ap.y) {
1328 return NULL;
1331 for (std::list<const Path*>::const_iterator it = path_refs.begin(), end = path_refs.end();
1332 it != end; ++it) {
1333 const Path* cur_path = *it;
1335 if (cur_path == &to_connect) {
1337 continue;
1342 if (cur_path->ap.x == target_x && cur_path->ap.y == target_y) {
1344 if (cur_path->m_fill1 != to_connect.m_fill1) {
1345 continue;
1347 return cur_path;
1352 return NULL;
1355 PathVec normalize_paths(const PathVec &paths)
1357 PathVec normalized;
1359 for (PathVec::const_iterator it = paths.begin(), end = paths.end();
1360 it != end; ++it) {
1361 const Path& cur_path = *it;
1363 if (cur_path.m_edges.empty()) {
1364 continue;
1366 } else if (cur_path.m_fill0 && cur_path.m_fill1) {
1368 // Two fill styles; duplicate and then reverse the left-filled one.
1369 normalized.push_back(cur_path);
1370 normalized.back().m_fill0 = 0;
1372 Path newpath = reverse_path(cur_path);
1373 newpath.m_fill0 = 0;
1375 normalized.push_back(newpath);
1377 } else if (cur_path.m_fill0) {
1378 // Left fill style.
1379 Path newpath = reverse_path(cur_path);
1380 newpath.m_fill0 = 0;
1382 normalized.push_back(newpath);
1383 } else if (cur_path.m_fill1) {
1384 // Right fill style.
1386 normalized.push_back(cur_path);
1387 } else {
1388 // No fill styles; copy without modifying.
1389 normalized.push_back(cur_path);
1394 return normalized;
1402 /// Analyzes a set of paths to detect real presence of fills and/or outlines
1403 /// TODO: This should be something the character tells us and should be
1404 /// cached.
1405 void analyze_paths(const PathVec &paths, bool& have_shape,
1406 bool& have_outline) {
1407 //normalize_paths(paths);
1408 have_shape=false;
1409 have_outline=false;
1411 int pcount = paths.size();
1413 for (int pno=0; pno<pcount; pno++) {
1415 const Path &the_path = paths[pno];
1417 if ((the_path.m_fill0>0) || (the_path.m_fill1>0)) {
1418 have_shape=true;
1419 if (have_outline) return; // have both
1422 if (the_path.m_line>0) {
1423 have_outline=true;
1424 if (have_shape) return; // have both
1430 void apply_FillStyle(const FillStyle& style, const SWFMatrix& /* mat */, const SWFCxForm& cx)
1432 const StyleHandler st(cx, *this);
1433 boost::apply_visitor(st, style.fill);
1438 bool apply_line_style(const LineStyle& style, const SWFCxForm& cx, const SWFMatrix& mat)
1440 // GNASH_REPORT_FUNCTION;
1442 // In case GL_TEXTURE_2D was enabled by apply_FillStyle(), disable it now.
1443 // FIXME: this sucks
1444 glDisable(GL_TEXTURE_2D);
1447 bool rv = true;
1449 float width = style.getThickness();
1450 // float pointSize;
1452 if (!width)
1454 glLineWidth(1.0f);
1455 rv = false; // Don't draw rounded lines.
1457 else if ( (!style.scaleThicknessVertically()) && (!style.scaleThicknessHorizontally()) )
1459 float pxThickness = twipsToPixels(width);
1460 glLineWidth(pxThickness);
1461 glPointSize(pxThickness);
1463 else
1465 if ( (!style.scaleThicknessVertically()) || (!style.scaleThicknessHorizontally()) )
1467 LOG_ONCE( log_unimpl(_("Unidirectionally scaled strokes in OGL renderer")) );
1470 float stroke_scale = std::fabs(mat.get_x_scale()) + std::fabs(mat.get_y_scale());
1471 stroke_scale /= 2.0f;
1472 stroke_scale *= (std::fabs(_xscale) + std::fabs(_yscale)) / 2.0f;
1473 width *= stroke_scale;
1474 width = twipsToPixels(width);
1476 GLfloat width_info[2];
1478 glGetFloatv(GL_LINE_WIDTH_RANGE, width_info);
1480 if (width > width_info[1]) {
1481 LOG_ONCE( log_unimpl("Your OpenGL implementation does not support the line width" \
1482 " requested. Lines will be drawn with reduced width.") );
1483 width = width_info[1];
1487 glLineWidth(width);
1488 glPointSize(width);
1489 #if 0
1490 if (width >= 1.5) {
1491 glPointSize(width-1);
1492 } else {
1493 // Don't draw rounded lines.
1494 rv = false;
1496 #endif
1501 rgba c = cx.transform(style.get_color());
1503 glColor4ub(c.m_r, c.m_g, c.m_b, c.m_a);
1505 return rv;
1508 PathPointMap getPathPoints(const PathVec& path_vec)
1511 PathPointMap pathpoints;
1513 for (PathVec::const_iterator it = path_vec.begin(), end = path_vec.end();
1514 it != end; ++it) {
1515 const Path& cur_path = *it;
1517 if (!cur_path.m_edges.size()) {
1518 continue;
1521 pathpoints[&cur_path] = interpolate(cur_path.m_edges, cur_path.ap.x,
1522 cur_path.ap.y);
1526 return pathpoints;
1529 typedef std::vector<const Path*> PathPtrVec;
1531 static void
1532 draw_point(const Edge& point_edge)
1534 glVertex2d(point_edge.ap.x, point_edge.ap.y);
1537 void
1538 draw_outlines(const PathVec& path_vec, const PathPointMap& pathpoints,
1539 const SWFMatrix& mat, const SWFCxForm& cx,
1540 const std::vector<FillStyle>& /* FillStyles */,
1541 const std::vector<LineStyle>& line_styles)
1544 for (PathVec::const_iterator it = path_vec.begin(), end = path_vec.end();
1545 it != end; ++it) {
1546 const Path& cur_path = *it;
1548 if (!cur_path.m_line) {
1549 continue;
1552 bool draw_points = apply_line_style(line_styles[cur_path.m_line-1], cx, mat);
1554 assert(pathpoints.find(&cur_path) != pathpoints.end());
1556 const std::vector<oglVertex>& shape_points = (*pathpoints.find(&cur_path)).second;
1558 // Draw outlines.
1559 glEnableClientState(GL_VERTEX_ARRAY);
1560 glVertexPointer(3, GL_DOUBLE, 0 /* tight packing */, &shape_points.front());
1561 glDrawArrays(GL_LINE_STRIP, 0, shape_points.size());
1562 glDisableClientState(GL_VERTEX_ARRAY);
1564 if (!draw_points) {
1565 continue;
1568 // Drawing points on the edges will allow us to simulate rounded lines.
1570 glEnable(GL_POINT_SMOOTH); // Draw round points.
1572 glBegin(GL_POINTS);
1574 glVertex2d(cur_path.ap.x, cur_path.ap.y);
1575 std::for_each(cur_path.m_edges.begin(), cur_path.m_edges.end(),
1576 draw_point);
1578 glEnd();
1582 std::list<PathPtrVec> get_contours(const PathPtrVec &paths)
1584 std::list<const Path*> path_refs;
1585 std::list<PathPtrVec> contours;
1588 for (PathPtrVec::const_iterator it = paths.begin(), end = paths.end();
1589 it != end; ++it) {
1590 const Path* cur_path = *it;
1591 path_refs.push_back(cur_path);
1594 for (std::list<const Path*>::const_iterator it = path_refs.begin(), end = path_refs.end();
1595 it != end; ++it) {
1596 const Path* cur_path = *it;
1598 if (cur_path->m_edges.empty()) {
1599 continue;
1602 if (!cur_path->m_fill0 && !cur_path->m_fill1) {
1603 continue;
1606 PathPtrVec contour;
1608 contour.push_back(cur_path);
1610 const Path* connector = find_connecting_path(*cur_path, path_refs);
1612 while (connector) {
1613 contour.push_back(connector);
1615 const Path* tmp = connector;
1616 connector = find_connecting_path(*connector, std::list<const Path*>(boost::next(it), end));
1618 // make sure we don't iterate over the connecting path in the for loop.
1619 path_refs.remove(tmp);
1623 contours.push_back(contour);
1626 return contours;
1630 void draw_mask(const PathVec& path_vec)
1632 for (PathVec::const_iterator it = path_vec.begin(), end = path_vec.end();
1633 it != end; ++it) {
1634 const Path& cur_path = *it;
1636 if (cur_path.m_fill0 || cur_path.m_fill1) {
1637 _masks.back().push_back(cur_path);
1638 _masks.back().back().m_line = 0;
1643 PathPtrVec
1644 paths_by_style(const PathVec& path_vec, unsigned int style)
1646 PathPtrVec paths;
1647 for (PathVec::const_iterator it = path_vec.begin(), end = path_vec.end();
1648 it != end; ++it) {
1649 const Path& cur_path = *it;
1651 if (cur_path.m_fill0 == style) {
1652 paths.push_back(&cur_path);
1655 if (cur_path.m_fill1 == style) {
1656 paths.push_back(&cur_path);
1660 return paths;
1664 std::vector<PathVec::const_iterator>
1665 find_subshapes(const PathVec& path_vec)
1667 std::vector<PathVec::const_iterator> subshapes;
1669 PathVec::const_iterator it = path_vec.begin(),
1670 end = path_vec.end();
1672 subshapes.push_back(it);
1673 ++it;
1675 for (;it != end; ++it) {
1676 const Path& cur_path = *it;
1678 if (cur_path.m_new_shape) {
1679 subshapes.push_back(it);
1683 if (subshapes.back() != end) {
1684 subshapes.push_back(end);
1687 return subshapes;
1690 /// Takes a path and translates it using the given SWFMatrix.
1691 void
1692 apply_matrix_to_paths(std::vector<Path>& paths, const SWFMatrix& mat)
1694 std::for_each(paths.begin(), paths.end(),
1695 boost::bind(&Path::transform, _1, boost::ref(mat)));
1697 //for_each(paths, &path::transform, mat);
1700 void
1701 draw_subshape(const PathVec& path_vec,
1702 const SWFMatrix& mat,
1703 const SWFCxForm& cx,
1704 const std::vector<FillStyle>& FillStyles,
1705 const std::vector<LineStyle>& line_styles)
1707 PathVec normalized = normalize_paths(path_vec);
1708 PathPointMap pathpoints = getPathPoints(normalized);
1710 for (size_t i = 0; i < FillStyles.size(); ++i) {
1711 PathPtrVec paths = paths_by_style(normalized, i+1);
1713 if (!paths.size()) {
1714 continue;
1717 std::list<PathPtrVec> contours = get_contours(paths);
1719 _tesselator.beginPolygon();
1721 for (std::list<PathPtrVec>::const_iterator iter = contours.begin(),
1722 final = contours.end(); iter != final; ++iter) {
1723 const PathPtrVec& refs = *iter;
1725 _tesselator.beginContour();
1727 for (PathPtrVec::const_iterator it = refs.begin(), end = refs.end();
1728 it != end; ++it) {
1729 const Path& cur_path = *(*it);
1731 assert(pathpoints.find(&cur_path) != pathpoints.end());
1733 _tesselator.feed(pathpoints[&cur_path]);
1737 _tesselator.endContour();
1740 apply_FillStyle(FillStyles[i], mat, cx);
1742 // This is terrible, but since the renderer is half dead I don't care.
1743 try {
1744 boost::get<SolidFill>(FillStyles[i].fill);
1746 catch (const boost::bad_get&) {
1747 // For non solid fills...
1748 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1751 _tesselator.tesselate();
1753 try {
1754 boost::get<SolidFill>(FillStyles[i].fill);
1756 catch (const boost::bad_get&) {
1757 // Restore to original.
1758 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1761 glDisable(GL_TEXTURE_GEN_S);
1762 glDisable(GL_TEXTURE_GEN_T);
1763 glDisable(GL_TEXTURE_1D);
1764 glDisable(GL_TEXTURE_2D);
1767 draw_outlines(normalized, pathpoints, mat, cx, FillStyles, line_styles);
1770 // Drawing procedure:
1771 // 1. Separate paths by subshape.
1772 // 2. Separate subshapes by fill style.
1773 // 3. For every subshape/fill style combo:
1774 // a. Separate contours: find closed shapes by connecting ends.
1775 // b. Apply fill style.
1776 // c. Feed the contours in the tesselator. (Render.)
1777 // d. Draw outlines for every path in the subshape with a line style.
1779 // 4. ...
1780 // 5. Profit!
1782 virtual void drawShape(const SWF::ShapeRecord& shape, const Transform& xform)
1785 const PathVec& path_vec = shape.paths();
1787 if (!path_vec.size()) {
1788 // No paths. Nothing to draw...
1789 return;
1792 if (_drawing_mask) {
1793 PathVec scaled_path_vec = path_vec;
1795 apply_matrix_to_paths(scaled_path_vec, xform.matrix);
1796 draw_mask(scaled_path_vec);
1797 return;
1800 bool have_shape, have_outline;
1802 analyze_paths(path_vec, have_shape, have_outline);
1804 if (!have_shape && !have_outline) {
1805 return; // invisible character
1808 oglScopeMatrix scope_mat(xform.matrix);
1810 std::vector<PathVec::const_iterator> subshapes = find_subshapes(path_vec);
1812 const std::vector<FillStyle>& FillStyles = shape.fillStyles();
1813 const std::vector<LineStyle>& line_styles = shape.lineStyles();
1815 for (size_t i = 0; i < subshapes.size()-1; ++i) {
1816 PathVec subshape_paths;
1818 if (subshapes[i] != subshapes[i+1]) {
1819 subshape_paths = PathVec(subshapes[i], subshapes[i+1]);
1820 } else {
1821 subshape_paths.push_back(*subshapes[i]);
1824 draw_subshape(subshape_paths, xform.matrix, xform.colorTransform,
1825 FillStyles, line_styles);
1829 virtual void drawGlyph(const SWF::ShapeRecord& rec, const rgba& c,
1830 const SWFMatrix& mat)
1832 if (_drawing_mask) abort();
1833 SWFCxForm dummy_cx;
1834 std::vector<FillStyle> glyph_fs;
1836 FillStyle coloring = FillStyle(SolidFill(c));
1838 glyph_fs.push_back(coloring);
1840 std::vector<LineStyle> dummy_ls;
1842 oglScopeMatrix scope_mat(mat);
1844 draw_subshape(rec.paths(), mat, dummy_cx, glyph_fs, dummy_ls);
1847 virtual void set_scale(float xscale, float yscale) {
1848 _xscale = xscale;
1849 _yscale = yscale;
1852 virtual void set_invalidated_regions(const InvalidatedRanges& /* ranges */)
1854 #if 0
1855 if (ranges.isWorld() || ranges.isNull()) {
1856 glDisable(GL_SCISSOR_TEST);
1857 return;
1860 glEnable(GL_SCISSOR_TEST);
1862 geometry::Range2d<float> area = ranges.getFullArea;
1864 glScissor( (int)twipsToPixels(area.getMinX()), window_height-(int)twipsToPixels(area.getMaxY()),
1865 (int)twipsToPixels(area.width()), (int)twipsToPixels(area.height()));
1866 #endif
1869 #ifdef OSMESA_TESTING
1870 bool getPixel(rgba& color_out, int x, int y) const
1872 return _offscreen->getPixel(color_out, x, y);
1875 bool initTestBuffer(unsigned width, unsigned height)
1877 GNASH_REPORT_FUNCTION;
1879 _offscreen.reset(new OSRenderMesa(width, height));
1881 init();
1883 return true;
1886 unsigned int getBitsPerPixel() const
1888 return _offscreen->getBitsPerPixel();
1890 #endif // OSMESA_TESTING
1893 private:
1895 Tesselator _tesselator;
1896 float _xscale;
1897 float _yscale;
1898 float _width; // Width of the movie, in world coordinates.
1899 float _height;
1901 std::vector<PathVec> _masks;
1902 bool _drawing_mask;
1904 std::vector<boost::uint8_t> _render_indices;
1905 std::vector< boost::shared_ptr<GnashTexture> > _render_textures;
1906 std::list< boost::shared_ptr<GnashTexture> > _cached_textures;
1908 #ifdef OSMESA_TESTING
1909 std::auto_ptr<OSRenderMesa> _offscreen;
1910 #endif
1911 }; // class Renderer_ogl
1913 Renderer* create_Renderer_ogl(bool init)
1914 // Factory.
1916 Renderer_ogl* renderer = new Renderer_ogl;
1917 if (init) {
1918 renderer->init();
1920 return renderer;
1923 namespace {
1925 // TODO: this function is rubbish and shouldn't survive a rewritten OGL
1926 // renderer.
1927 rgba
1928 sampleGradient(const GradientFill& fill, boost::uint8_t ratio)
1931 // By specs, first gradient should *always* be 0,
1932 // anyway a malformed SWF could break this,
1933 // so we cannot rely on that information...
1934 if (ratio < fill.record(0).ratio) {
1935 return fill.record(0).color;
1938 if (ratio >= fill.record(fill.recordCount() - 1).ratio) {
1939 return fill.record(fill.recordCount() - 1).color;
1942 for (size_t i = 1, n = fill.recordCount(); i < n; ++i) {
1944 const GradientRecord& gr1 = fill.record(i);
1945 if (gr1.ratio < ratio) continue;
1947 const GradientRecord& gr0 = fill.record(i - 1);
1948 if (gr0.ratio > ratio) continue;
1950 float f = 0.0f;
1952 if (gr0.ratio != gr1.ratio) {
1953 f = (ratio - gr0.ratio) / float(gr1.ratio - gr0.ratio);
1955 else {
1956 // Ratios are equal IFF first and second GradientRecord
1957 // have the same ratio. This would be a malformed SWF.
1958 IF_VERBOSE_MALFORMED_SWF(
1959 log_swferror(_("two gradients in a FillStyle "
1960 "have the same position/ratio: %d"),
1961 gr0.ratio);
1965 rgba result;
1966 result.set_lerp(gr0.color, gr1.color, f);
1967 return result;
1970 // Assuming gradients are ordered by ratio? see start comment
1971 return fill.record(fill.recordCount() - 1).color;
1974 const CachedBitmap*
1975 createGradientBitmap(const GradientFill& gf, Renderer& renderer)
1977 std::auto_ptr<image::ImageRGBA> im;
1979 switch (gf.type())
1981 case GradientFill::LINEAR:
1982 // Linear gradient.
1983 im.reset(new image::ImageRGBA(256, 1));
1985 for (size_t i = 0; i < im->width(); i++) {
1987 rgba sample = sampleGradient(gf, i);
1988 im->setPixel(i, 0, sample.m_r, sample.m_g,
1989 sample.m_b, sample.m_a);
1991 break;
1993 case GradientFill::RADIAL:
1994 // Focal gradient.
1995 im.reset(new image::ImageRGBA(64, 64));
1997 for (size_t j = 0; j < im->height(); j++)
1999 for (size_t i = 0; i < im->width(); i++)
2001 float radiusy = (im->height() - 1) / 2.0f;
2002 float radiusx = radiusy + std::abs(radiusy * gf.focalPoint());
2003 float y = (j - radiusy) / radiusy;
2004 float x = (i - radiusx) / radiusx;
2005 int ratio = std::floor(255.5f * std::sqrt(x*x + y*y));
2007 if (ratio > 255) ratio = 255;
2009 rgba sample = sampleGradient(gf, ratio);
2010 im->setPixel(i, j, sample.m_r, sample.m_g,
2011 sample.m_b, sample.m_a);
2014 break;
2015 default:
2016 break;
2019 const CachedBitmap* bi = renderer.createCachedBitmap(
2020 static_cast<std::auto_ptr<image::GnashImage> >(im));
2022 return bi;
2027 } // namespace gnash