2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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.
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.
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
21 #include "gnashconfig.h"
26 #include <boost/scoped_ptr.hpp>
28 #include "smart_ptr.h"
29 #include "swf/ShapeRecord.h"
32 #include "GnashImage.h"
33 #include "GnashTexture.h"
34 #include "GnashNumeric.h"
38 #include "SWFCxForm.h"
39 #include "FillStyle.h"
40 #include "Transform.h"
42 #if defined(_WIN32) || defined(WIN32)
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"
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.
62 /// So how does this thing work?
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.
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,
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.
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.
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!
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.
115 const CachedBitmap
* createGradientBitmap(const GradientFill
& gf
,
121 class bitmap_info_ogl
: public CachedBitmap
125 /// Set line and fill styles for mesh & line_strip rendering.
126 enum bitmap_wrap_mode
132 bitmap_info_ogl(std::auto_ptr
<image::GnashImage
> image
, GLenum pixelformat
,
133 bool ogl_accessible
);
137 /// TODO: implement this meaningfully.
138 virtual void dispose() {
142 virtual bool disposed() const {
146 virtual image::GnashImage
& image() {
147 if (_cache
.get()) return *_cache
;
148 switch (_pixel_format
) {
150 _cache
.reset(new image::ImageRGB(_orig_width
, _orig_height
));
153 _cache
.reset(new image::ImageRGBA(_orig_width
, _orig_height
));
158 std::fill(_cache
->begin(), _cache
->end(), 0xff);
162 void apply(const gnash::SWFMatrix
& bitmap_matrix
,
163 bitmap_wrap_mode wrap_mode
) const;
165 inline bool ogl_accessible() 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
;
182 /// Transfer FillStyles to the ogl renderer.
183 struct StyleHandler
: boost::static_visitor
<>
185 StyleHandler(const SWFCxForm
& c
, 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
*>(
208 binfo
->apply(f
.matrix(), f
.type() == BitmapFill::TILED
?
209 bitmap_info_ogl::WRAP_REPEAT
: bitmap_info_ogl::WRAP_CLAMP
);
213 const SWFCxForm
& _cx
;
220 #ifdef OSMESA_TESTING
222 class OSRenderMesa
: public boost::noncopyable
225 OSRenderMesa(size_t width
, size_t 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
))
232 _context(OSMesaCreateContext(OSMESA_RGB
, NULL
))
236 log_error("OSMesaCreateContext failed!");
237 return; // FIXME: throw an exception?
240 if (!OSMesaMakeCurrent(_context
, _buffer
.get(), GL_UNSIGNED_BYTE
, width
,
242 log_error("OSMesaMakeCurrent failed!");
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
);
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
) {
269 ptrdiff_t offset
= (_height
- y
) * (_width
* 3) + x
* 3;
270 color_out
.set(_buffer
[offset
], _buffer
[offset
+1], _buffer
[offset
+2], 255);
275 unsigned int getBitsPerPixel() const
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
295 oglScopeEnable(GLenum capability
)
310 class oglScopeMatrix
: public boost::noncopyable
313 oglScopeMatrix(const SWFMatrix
& m
)
317 // Multiply (AKA "append") the new SWFMatrix with the current OpenGL one.
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
;
340 GLenum error
= glGetError();
342 if (error
== GL_NO_ERROR
) {
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
));
362 PointSerializer(std::vector
<boost::int16_t>& dest
)
367 void operator()(const point
& p
)
369 _dest
.push_back(p
.x
);
370 _dest
.push_back(p
.y
);
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
));
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();
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
));
429 point
control(the_edge
.cp
.x
, the_edge
.cp
.y
);
431 trace_curve(anchor
, control
, target
, 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
));
458 // for testing, draw only the outside of shapes.
459 gluTessProperty(_tessobj
, GLU_TESS_BOUNDARY_ONLY
, GL_TRUE
);
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
);
473 Tesselator::beginPolygon()
475 gluTessBeginPolygon(_tessobj
, this);
478 void Tesselator::beginContour()
480 gluTessBeginContour(_tessobj
);
484 Tesselator::feed(std::vector
<oglVertex
>& vertices
)
486 for (std::vector
<oglVertex
>::const_iterator it
= vertices
.begin(), end
= vertices
.end();
488 GLdouble
* vertex
= const_cast<GLdouble
*>(&(*it
)._x
);
489 gluTessVertex(_tessobj
, vertex
, vertex
);
494 Tesselator::endContour()
496 gluTessEndContour(_tessobj
);
500 Tesselator::tesselate()
502 gluTessEndPolygon(_tessobj
);
504 for (std::vector
<GLdouble
*>::iterator it
= _vertices
.begin(),
505 end
= _vertices
.end(); it
!= end
; ++it
) {
513 Tesselator::rememberVertex(GLdouble
* v
)
515 _vertices
.push_back(v
);
522 Tesselator::error(GLenum error
)
524 log_error("GLU: %s", gluErrorString(error
));
529 Tesselator::combine(GLdouble coords
[3], void **/
*vertex_data
[4] */
,
530 GLfloat
* /* weight[4] */, void **outData
, void* userdata
)
532 Tesselator
* tess
= (Tesselator
*)userdata
;
535 GLdouble
* v
= new GLdouble
[3];
542 tess
->rememberVertex(v
);
546 bool isEven(const size_t& n
)
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
),
560 _orig_width(_img
->width()),
561 _orig_height(_img
->height()),
564 if (!_ogl_accessible
) {
571 bitmap_info_ogl::~bitmap_info_ogl()
573 glDeleteTextures(1, &_texture_id
);
574 glDisable(_ogl_img_type
);
578 bitmap_info_ogl::setup() const
580 oglScopeEnable
enabler(_ogl_img_type
);
582 glGenTextures(1, &_texture_id
);
583 glBindTexture(_ogl_img_type
, _texture_id
);
586 if (_img
->height() == 1) {
587 if (!isEven(_img
->width())) {
592 if (!isEven(_img
->width()) || !isEven(_img
->height())) {
598 upload(_img
->begin(), _img
->width(), _img
->height());
602 size_t w
= 1; while (w
< _img
->width()) { w
<<= 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());
616 Tesselator::error(rv
);
620 upload(resized_data
.get(), w
, h
);
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
);
638 glTexImage2D(_ogl_img_type
, 0, _pixel_format
, width
, height
,
639 0, _pixel_format
, GL_UNSIGNED_BYTE
, data
);
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;
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
);
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
>
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
719 std::string
description() const { return "OpenGL"; }
723 // Turn on alpha blending.
724 // FIXME: do this when it's actually used?
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
);
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
);
762 glShadeModel(GL_FLAT
);
771 ogl_accessible() const
773 #if defined(_WIN32) || defined(WIN32)
774 return wglGetCurrentContext();
775 #elif defined(__APPLE_CC__)
776 return aglGetCurrentContext();
778 # ifdef OSMESA_TESTING
779 if (_offscreen
.get()) {
780 return OSMesaGetCurrentContext();
783 return glXGetCurrentContext();
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;
802 case image::TYPE_RGBA
:
803 return new bitmap_info_ogl(im
, GL_RGBA
, ogl_accessible());
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
:
819 #ifdef HAVE_VA_VA_GLX_H
820 case image::GNASH_IMAGE_GPU
:
821 frameFlags
= GNASH_TEXTURE_VAAPI
;
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
)
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()) {
844 _cached_textures
.erase(it
);
847 // Otherwise, create one and empty cache because they may no
848 // longer be referenced
850 _cached_textures
.clear();
852 switch (frame
->location()) {
853 case image::GNASH_IMAGE_CPU
:
854 texture
.reset(new GnashTexture(frame
->width(),
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
862 #ifdef HAVE_VA_VA_GLX_H
863 texture
.reset(new GnashVaapiTexture(frame
->width(),
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
);
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*/)
889 glGetIntegerv(GL_LIST_INDEX
, &index
);
892 log_error("An insane number of video frames have been requested to be "
893 "drawn. Further video frames will be ignored.");
899 boost::shared_ptr
<GnashTexture
> texture
= getCachedTexture(frame
);
903 switch (frame
->location()) {
904 case image::GNASH_IMAGE_CPU
:
905 texture
->update(frame
->begin());
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());
916 _render_textures
.push_back(texture
);
922 glNewList(index
, GL_COMPILE
);
923 _render_indices
.push_back(index
);
925 reallyDrawVideoFrame(texture
, &xform
.matrix
, bounds
);
931 glNewList(index
, GL_COMPILE
);
932 _render_indices
.push_back(index
);
936 void reallyDrawVideoFrame(boost::shared_ptr
<GnashTexture
> texture
, const SWFMatrix
* m
, const SWFRect
* bounds
)
938 glPushAttrib(GL_ENABLE_BIT
| GL_COLOR_BUFFER_BIT
);
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
;
948 glTranslatef(l
.x
, l
.y
, 0.0f
);
949 glColor4f(1.0f
, 1.0f
, 1.0f
, 1.0f
);
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);
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
976 pixel_to_world(int x
, int y
)
978 // TODO: verify this is correct
979 return point(pixelsToTwips(x
), pixelsToTwips(y
));
984 virtual Renderer
* startInternalRender(image::GnashImage
& /*im*/) {
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
);
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.
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);
1013 glClearColor(1.0, 1.0, 1.0, 1.0);
1018 // Start a new display list which will contain almost everything we draw.
1019 glNewList(1, GL_COMPILE
);
1020 _render_indices
.push_back(1);
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());
1035 // This is a table of randomly generated numbers between -0.5 and 0.5.
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 }
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
);
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());
1074 glAccum(GL_ACCUM
, 1.0/numPoints
);
1077 glAccum (GL_RETURN
, 1.0);
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
);
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();
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
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) {
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
);
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
);
1167 virtual void set_antialiased(bool /* enable */ )
1169 log_unimpl("set_antialiased");
1172 virtual void begin_submit_mask()
1175 _masks
.push_back(mask
);
1177 _drawing_mask
= true;
1180 virtual void end_submit_mask()
1182 _drawing_mask
= false;
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.
1197 if (_masks
.empty()) {
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());
1223 add_paths(const PathVec
& path_vec
)
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()
1241 if (_masks
.empty()) {
1242 glDisable(GL_STENCIL_TEST
);
1249 void print_path(const Path
& path
)
1251 std::cout
<< "Origin: ("
1263 << " number of edges: "
1264 << path
.m_edges
.size()
1265 << " edge endpoint: ("
1266 << path
.m_edges
.back().ap
.x
1268 << path
.m_edges
.back().ap
.y
1271 for (std::vector
<edge
>::const_iterator it
= path
.m_edges
.begin(), end
= path
.m_edges
.end();
1273 const edge
& cur_edge
= *it
;
1274 std::cout
<< "( " << cur_edge
.ap
.x
<< ", " << cur_edge
.ap
.y
<< ") ";
1276 std::cout
<< std::endl
;
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();
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
);
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
) {
1331 for (std::list
<const Path
*>::const_iterator it
= path_refs
.begin(), end
= path_refs
.end();
1333 const Path
* cur_path
= *it
;
1335 if (cur_path
== &to_connect
) {
1342 if (cur_path
->ap
.x
== target_x
&& cur_path
->ap
.y
== target_y
) {
1344 if (cur_path
->m_fill1
!= to_connect
.m_fill1
) {
1355 PathVec
normalize_paths(const PathVec
&paths
)
1359 for (PathVec::const_iterator it
= paths
.begin(), end
= paths
.end();
1361 const Path
& cur_path
= *it
;
1363 if (cur_path
.m_edges
.empty()) {
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
) {
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
);
1388 // No fill styles; copy without modifying.
1389 normalized
.push_back(cur_path
);
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
1405 void analyze_paths(const PathVec
&paths
, bool& have_shape
,
1406 bool& have_outline
) {
1407 //normalize_paths(paths);
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)) {
1419 if (have_outline
) return; // have both
1422 if (the_path
.m_line
>0) {
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
);
1449 float width
= style
.getThickness();
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
);
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];
1491 glPointSize(width
-1);
1493 // Don't draw rounded lines.
1501 rgba c
= cx
.transform(style
.get_color());
1503 glColor4ub(c
.m_r
, c
.m_g
, c
.m_b
, c
.m_a
);
1508 PathPointMap
getPathPoints(const PathVec
& path_vec
)
1511 PathPointMap pathpoints
;
1513 for (PathVec::const_iterator it
= path_vec
.begin(), end
= path_vec
.end();
1515 const Path
& cur_path
= *it
;
1517 if (!cur_path
.m_edges
.size()) {
1521 pathpoints
[&cur_path
] = interpolate(cur_path
.m_edges
, cur_path
.ap
.x
,
1529 typedef std::vector
<const Path
*> PathPtrVec
;
1532 draw_point(const Edge
& point_edge
)
1534 glVertex2d(point_edge
.ap
.x
, point_edge
.ap
.y
);
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();
1546 const Path
& cur_path
= *it
;
1548 if (!cur_path
.m_line
) {
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
;
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
);
1568 // Drawing points on the edges will allow us to simulate rounded lines.
1570 glEnable(GL_POINT_SMOOTH
); // Draw round 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(),
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();
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();
1596 const Path
* cur_path
= *it
;
1598 if (cur_path
->m_edges
.empty()) {
1602 if (!cur_path
->m_fill0
&& !cur_path
->m_fill1
) {
1608 contour
.push_back(cur_path
);
1610 const Path
* connector
= find_connecting_path(*cur_path
, path_refs
);
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
);
1630 void draw_mask(const PathVec
& path_vec
)
1632 for (PathVec::const_iterator it
= path_vec
.begin(), end
= path_vec
.end();
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;
1644 paths_by_style(const PathVec
& path_vec
, unsigned int style
)
1647 for (PathVec::const_iterator it
= path_vec
.begin(), end
= path_vec
.end();
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
);
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
);
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
);
1690 /// Takes a path and translates it using the given SWFMatrix.
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);
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()) {
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();
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.
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();
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.
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...
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
);
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]);
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();
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
) {
1852 virtual void set_invalidated_regions(const InvalidatedRanges
& /* ranges */)
1855 if (ranges
.isWorld() || ranges
.isNull()) {
1856 glDisable(GL_SCISSOR_TEST
);
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()));
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
));
1886 unsigned int getBitsPerPixel() const
1888 return _offscreen
->getBitsPerPixel();
1890 #endif // OSMESA_TESTING
1895 Tesselator _tesselator
;
1898 float _width
; // Width of the movie, in world coordinates.
1901 std::vector
<PathVec
> _masks
;
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
;
1911 }; // class Renderer_ogl
1913 Renderer
* create_Renderer_ogl(bool init
)
1916 Renderer_ogl
* renderer
= new Renderer_ogl
;
1925 // TODO: this function is rubbish and shouldn't survive a rewritten OGL
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;
1952 if (gr0
.ratio
!= gr1
.ratio
) {
1953 f
= (ratio
- gr0
.ratio
) / float(gr1
.ratio
- gr0
.ratio
);
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"),
1966 result
.set_lerp(gr0
.color
, gr1
.color
, f
);
1970 // Assuming gradients are ordered by ratio? see start comment
1971 return fill
.record(fill
.recordCount() - 1).color
;
1975 createGradientBitmap(const GradientFill
& gf
, Renderer
& renderer
)
1977 std::auto_ptr
<image::ImageRGBA
> im
;
1981 case GradientFill::LINEAR
:
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
);
1993 case GradientFill::RADIAL
:
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
);
2019 const CachedBitmap
* bi
= renderer
.createCachedBitmap(
2020 static_cast<std::auto_ptr
<image::GnashImage
> >(im
));
2027 } // namespace gnash