[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] gnash ChangeLog backend/render_handler_ogl.cpp ...
From: |
Bastiaan Jacques |
Subject: |
[Gnash-commit] gnash ChangeLog backend/render_handler_ogl.cpp ... |
Date: |
Thu, 01 Nov 2007 08:04:10 +0000 |
CVSROOT: /sources/gnash
Module name: gnash
Changes by: Bastiaan Jacques <bjacques> 07/11/01 08:04:10
Modified files:
. : ChangeLog
backend : render_handler_ogl.cpp
gui : gtk_glue_cairo.cpp gtk_glue_gtkglext.cpp
Added files:
backend : render_handler_ogl.h
Log message:
* backend/render_handler_ogl.h: New header file; the beginnings
of header/implementaion separation.
* backend/render_handler_ogl.cpp: New OpenGL renderer
implementation. In this initial version, support for subshapes
and poor man's anti-aliasing (only filled shapes are affected)
are added. The new renderer will facilitate the addition of
more good stuff, like real anti-aliasing and to eliminate the
many remaining OpenGL bugs. Simple masking now works.
* gui/gtk_glue_cairo.cpp: Use a RGBA buffer by default.
* gui/gtk_glue_gtkglext.cpp: Enable the stencil buffer rather
than the depth buffer, since the former is used while the
latter is not.
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/gnash/ChangeLog?cvsroot=gnash&r1=1.4751&r2=1.4752
http://cvs.savannah.gnu.org/viewcvs/gnash/backend/render_handler_ogl.cpp?cvsroot=gnash&r1=1.83&r2=1.84
http://cvs.savannah.gnu.org/viewcvs/gnash/backend/render_handler_ogl.h?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/gui/gtk_glue_cairo.cpp?cvsroot=gnash&r1=1.13&r2=1.14
http://cvs.savannah.gnu.org/viewcvs/gnash/gui/gtk_glue_gtkglext.cpp?cvsroot=gnash&r1=1.12&r2=1.13
Patches:
Index: ChangeLog
===================================================================
RCS file: /sources/gnash/gnash/ChangeLog,v
retrieving revision 1.4751
retrieving revision 1.4752
diff -u -b -r1.4751 -r1.4752
--- ChangeLog 31 Oct 2007 11:17:47 -0000 1.4751
+++ ChangeLog 1 Nov 2007 08:04:08 -0000 1.4752
@@ -1,3 +1,18 @@
+2007-10-31 Bastiaan Jacques <address@hidden>
+
+ * backend/render_handler_ogl.h: New header file; the beginnings
+ of header/implementaion separation.
+ * backend/render_handler_ogl.cpp: New OpenGL renderer
+ implementation. In this initial version, support for subshapes
+ and poor man's anti-aliasing (only filled shapes are affected)
+ are added. The new renderer will facilitate the addition of
+ more good stuff, like real anti-aliasing and to eliminate the
+ many remaining OpenGL bugs. Simple masking now works.
+ * gui/gtk_glue_cairo.cpp: Use a RGBA buffer by default.
+ * gui/gtk_glue_gtkglext.cpp: Enable the stencil buffer rather
+ than the depth buffer, since the former is used while the
+ latter is not.
+
2007-10-31 Sandro Santilli <address@hidden>
* testsuite/actionscript.all/TextField.as: add test for
Index: backend/render_handler_ogl.cpp
===================================================================
RCS file: /sources/gnash/gnash/backend/render_handler_ogl.cpp,v
retrieving revision 1.83
retrieving revision 1.84
diff -u -b -r1.83 -r1.84
--- backend/render_handler_ogl.cpp 12 Sep 2007 17:13:29 -0000 1.83
+++ backend/render_handler_ogl.cpp 1 Nov 2007 08:04:09 -0000 1.84
@@ -1,11 +1,20 @@
-// render_handler_ogl.cpp -- Willem Kokke <address@hidden> 2003
-
-// This source code has been donated to the Public Domain. Do
-// whatever you want with it.
-
-// A render_handler that uses SDL & OpenGL
+//
+// Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-/* $Id: render_handler_ogl.cpp,v 1.83 2007/09/12 17:13:29 bjacques Exp $ */
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -14,9 +23,14 @@
#include <cstring>
#include <cmath>
-//#include "gnash.h"
#include "render_handler.h"
-#include "render_handler_tri.h"
+
+#include "gnash.h"
+#include "types.h"
+#include "image.h"
+#include "utility.h"
+#include "log.h"
+
#include "types.h"
#include "image.h"
#include "utility.h"
@@ -25,219 +39,420 @@
# include <Windows.h>
#endif
-#if defined(NOT_SGI_GL) || defined(__APPLE_CC__)
-#include <AGL/agl.h>
-#include <OpenGL/gl.h>
-#include <OpenGL/glu.h>
-#include <OpenGL/glext.h>
-#else
-# include <GL/gl.h>
-# ifdef WIN32
-# define GL_CLAMP_TO_EDGE 0x812F
-# else
-# include <GL/glx.h>
-# endif
-# include <GL/glu.h>
-# ifndef APIENTRY
-# define APIENTRY
-# endif
-#endif
+#include "render_handler_ogl.h"
-using namespace gnash;
+#include <boost/utility.hpp>
-// choose the resampling method:
-// 1 = hardware (experimental, should be fast, somewhat buggy)
-// 2 = fast software bilinear (default)
-// 3 = use image::resample(), slow software resampling
-#define RESAMPLE_METHOD 2
+/// \file render_handler_ogl.cpp
+/// \brief The OpenGL renderer and related code.
+///
+/// So how does this thing work?
+///
+/// 1. Flash graphics are fundamentally incompatible with OpenGL. Flash shapes
+/// are defined by an arbitrary number of paths, which in turn are formed
+/// from an arbitrary number of edges. An edge describes a quadratic Bezier
+/// curve. A shape is defined by at least one path enclosing a space -- this
+/// space is the shape. Every path may have a left and/or right fill style,
+/// determining (if the shape is thought of as a vector) which side(s) of
+/// the path is to be filled.
+/// OpenGL, on the other hand, understands only triangles, lines and points.
+/// We must break Flash graphics down into primitives that OpenGL can
+/// understand before we can render them.
+///
+/// 2. First, we must ensure that OpenGL receives only closed shapes with a
+/// single fill style. Paths with two fill styles are duplicated. Then,
+/// shapes with
+/// a left fill style are reversed and the fill style is moved to the right.
+/// The shapes must be closed, so the tesselator can parse them; this
+/// involves a fun game of connect-the-dots. Fortunately, Flash guarantees
+/// that shapes are always closed and that they're never self-intersecting.
+///
+/// 3. Now that we have a consistent set of shapes, we can interpolate the
+/// Bezier curves of which each path is made of. OpenGL can do this for us,
+/// using evaluators, but we currently do it ourselves.
+///
+/// 4. Being the proud owners of a brand new set of interpolated coordinates,
+/// we can feed the coordinates into the GLU tesselator. The tesselator will
+/// break our complex (but never self-intersecting) polygon into OpenGL-
+/// grokkable primitives (say, a triangle fan or strip). We let the
+/// tesselator worry about that part. When the tesselator is finished, all
+/// we have to do is set up the fill style and draw the primitives given to
+/// us. The GLU tesselator will take care of shapes having inner boundaries
+/// (for example a donut shape). This makes life a LOT easier!
+
+
+// TODO:
+// - Implement:
+// * Nested masks
+// * Alpha images
+// * Real antialiasing
+//
+// - Profiling!
+// - Optimize code:
+// * Use display lists
+// * Use better suited standard containers
+// * convert to double at a later stage (oglVertex)
+// * keep data for less time
+
+// * The "Programming Tips" in the OpenGL "red book" discusses a coordinate
system
+// that would give "exact two-dimensional rasterization". AGG uses a similar
+// system; consider the benefits and drawbacks of switching.
-//#define DEBUG_OPENGL 1
+namespace gnash {
-// bitmap_info_ogl declaration
-class bitmap_info_ogl : public gnash::bitmap_info
+typedef std::vector<path> PathVec;
+
+class oglScopeEnable : public boost::noncopyable
{
public:
- bitmap_info_ogl();
- bitmap_info_ogl(int width, int height, uint8_t* data);
- bitmap_info_ogl(image::rgb* im);
- bitmap_info_ogl(image::rgba* im);
-
- ~bitmap_info_ogl() {
- if (m_texture_id > 0) {
- glDeleteTextures(1, (GLuint*) &m_texture_id);
- }
+ oglScopeEnable(GLenum capability)
+ :_cap(capability)
+ {
+ glEnable(_cap);
}
- void layout_image(image::image_base* im);
-
- std::auto_ptr<image::image_base> m_suspended_image;
+ ~oglScopeEnable()
+ {
+ glDisable(_cap);
+ }
+private:
+ GLenum _cap;
};
-// static GLint iquad[] = {-1, 1, 1, 1, 1, -1, -1, -1};
-
-#ifdef DEBUG_OPENGL
-static void check_error()
+static void
+check_error()
{
GLenum error = glGetError();
- while (error != GL_NO_ERROR) {
-
- std::cerr << "OpenGL error: " << (const char*) gluErrorString(error)
- << std::endl;
- error = glGetError();
+ if (error == GL_NO_ERROR) {
+ return;
}
+
+ log_error("OpenGL: %s", gluErrorString(error));
}
-#endif // DEBUG_OPENGL
-class render_handler_ogl : public gnash::triangulating_render_handler
+/// @ret A point in the middle of points a and b, that is, the middle of a line
+/// drawn from a to b.
+point middle(const point& a, const point& b)
{
-public:
+ return point(0.5 * (a.m_x + b.m_x), 0.5 * (a.m_y + b.m_y));
+}
+
- // Some renderer state.
- // Enable/disable antialiasing.
- bool m_enable_antialias;
+// Unfortunately, we can't use OpenGL as-is to interpolate the curve for us. It
+// is legal for Flash coordinates to be outside of the viewport, which will
+// be ignored by OpenGL's feedback mode. Feedback mode
+// will simply not return those coordinates, which will destroy a shape which
+// is partly off-screen.
+
+// So, if we transform the coordinates to be always positive, it should be
+// possible to use evaluators. This then presents another problem: what if
+// one coordinate is negative and the other is not, and what if both of
+// those are outside of the viewport?
+
+// one solution would be to use feedback mode unless one of the coordinates
+// is outside of the viewport.
+void trace_curve(const point& startP, const point& controlP,
+ const point& endP, std::vector<oglVertex>& coords)
+{
+ // Midpoint on line between two endpoints.
+ point mid = middle(startP, endP);
- // Output size.
- float m_display_width;
- float m_display_height;
+ // Midpoint on the curve.
+ point q = middle(mid, controlP);
- gnash::matrix m_current_matrix;
- gnash::cxform m_current_cxform;
+ float dist = std::abs(mid.m_x - q.m_x) + std::abs(mid.m_y - q.m_y);
- gnash::point _scale;
+ if (dist < 0.1 /*error tolerance*/) {
+ coords.push_back(oglVertex(endP));
+ } else {
+ // Error is too large; subdivide.
+ trace_curve(startP, middle(startP, controlP), q, coords);
- void set_antialiased(bool enable) {
- m_enable_antialias = enable;
+ trace_curve(q, middle(controlP, endP), endP, coords);
}
+}
- // Utility. Mutates *width, *height and *data to create the
- // next mip level.
- static void make_next_miplevel(int* width, int* height, uint8_t* data)
- {
- assert(width);
- assert(height);
- assert(data);
- int new_w = *width >> 1;
- int new_h = *height >> 1;
- if (new_w < 1) new_w = 1;
- if (new_h < 1) new_h = 1;
- if (new_w * 2 != *width || new_h * 2 != *height) {
- // Image can't be shrunk along (at least) one
- // of its dimensions, so don't bother
- // resampling. Technically we should, but
- // it's pretty useless at this point. Just
- // change the image dimensions and leave the
- // existing pixels.
+std::vector<oglVertex> interpolate(const std::vector<edge>& edges, const
float& anchor_x,
+ const float& anchor_y)
+{
+ point anchor(anchor_x, anchor_y);
+
+ std::vector<oglVertex> shape_points;
+ shape_points.push_back(oglVertex(anchor));
+
+ for (std::vector<edge>::const_iterator it = edges.begin(), end = edges.end();
+ it != end; ++it) {
+ const edge& the_edge = *it;
+
+ point target(the_edge.m_ax, the_edge.m_ay);
+
+ if (the_edge.is_straight()) {
+ shape_points.push_back(oglVertex(target));
} else {
- // Resample. Simple average 2x2 --> 1, in-place.
- for (int j = 0; j < new_h; j++) {
- uint8_t* out = ((uint8_t*) data) + j * new_w;
- uint8_t* in = ((uint8_t*) data) + (j << 1) * *width;
- for (int i = 0; i < new_w; i++) {
- int a;
- a = (*(in + 0) + *(in + 1) + *(in + 0 + *width) + *(in
+ 1 + *width));
- *(out) = a >> 2;
- out++;
- in += 2;
+ point control(the_edge.m_cx, the_edge.m_cy);
+
+ trace_curve(anchor, control, target, shape_points);
}
+ anchor = target;
}
+
+ return shape_points;
+}
+
+
+// FIXME: OSX doesn't like void (*)().
+Tesselator::Tesselator()
+: _tessobj(gluNewTess())
+{
+ gluTessCallback(_tessobj, GLU_TESS_ERROR,
+ reinterpret_cast<void (*)()>(Tesselator::error));
+ gluTessCallback(_tessobj, GLU_TESS_COMBINE_DATA,
+ reinterpret_cast<void (*)()>(Tesselator::combine));
+
+ gluTessCallback(_tessobj, GLU_TESS_BEGIN,
+ reinterpret_cast<void (*)()>(glBegin));
+ gluTessCallback(_tessobj, GLU_TESS_END,
+ reinterpret_cast<void (*)()>(glEnd));
+
+ gluTessCallback(_tessobj, GLU_TESS_VERTEX,
+ reinterpret_cast<void (*)()>(glVertex3dv));
+
+#if 0
+ // for testing, draw only the outside of shapes.
+ gluTessProperty(_tessobj, GLU_TESS_BOUNDARY_ONLY, GL_TRUE);
+#endif
+
+ // all coordinates lie in the x-y plane
+ // this speeds up tesselation
+ gluTessNormal(_tessobj, 0.0, 0.0, 1.0);
+}
+
+Tesselator::~Tesselator()
+{
+ gluDeleteTess(_tessobj);
+}
+
+void
+Tesselator::beginPolygon()
+{
+ gluTessBeginPolygon(_tessobj, this);
+}
+
+void Tesselator::beginContour()
+{
+ gluTessBeginContour(_tessobj);
+}
+
+void
+Tesselator::feed(std::vector<oglVertex>& vertices)
+{
+ for (std::vector<oglVertex>::const_iterator it = vertices.begin(), end =
vertices.end();
+ it != end; ++it) {
+ GLdouble* vertex = const_cast<GLdouble*>(&(*it)._x);
+ gluTessVertex(_tessobj, vertex, vertex);
}
+}
+
+void Tesselator::endContour()
+{
+ gluTessEndContour(_tessobj);
+}
- // Munge parameters to reflect the shrunken image.
- *width = new_w;
- *height = new_h;
+void
+Tesselator::tesselate()
+{
+ gluTessEndPolygon(_tessobj);
+
+ for (std::vector<GLdouble*>::iterator it = _vertices.begin(),
+ end = _vertices.end(); it != end; ++it) {
+ delete [] *it;
}
+ _vertices.clear();
+}
- class fill_style
- {
+void
+Tesselator::rememberVertex(GLdouble* v)
+{
+ _vertices.push_back(v);
+}
+
+
+
+// static
+void
+Tesselator::error(GLenum error)
+{
+ log_error("GLU: %s", gluErrorString(error));
+}
+
+// static
+void
+Tesselator::combine(GLdouble coords [3], void *vertex_data[4],
+ GLfloat weight[4], void **outData, void* userdata)
+{
+ Tesselator* tess = (Tesselator*)userdata;
+ assert(tess);
+
+ GLdouble* v = new GLdouble[3];
+ v[0] = coords[0];
+ v[1] = coords[1];
+ v[2] = coords[2];
+
+ *outData = v;
+
+ tess->rememberVertex(v);
+}
+
+
+bool isEven(const size_t& n)
+{
+ return n % 2 == 0;
+}
+
+class bitmap_info_ogl : public bitmap_info
+{
public:
- enum mode
+ bitmap_info_ogl(image::image_base* image, GLenum pixelformat)
+ :
+ _img(image->clone()),
+ _pixel_format(pixelformat),
+ _ogl_img_type(_img->height() == 1 ? GL_TEXTURE_1D : GL_TEXTURE_2D),
+ _ogl_accessible(ogl_accessible()),
+ _texture_id(0),
+ _orig_width(_img->width()),
+ _orig_height(_img->height())
{
- INVALID,
- COLOR,
- BITMAP_WRAP,
- BITMAP_CLAMP,
- LINEAR_GRADIENT,
- RADIAL_GRADIENT
- };
- mode m_mode;
- gnash::rgba m_color;
- bitmap_info_ogl* m_bitmap_info;
- gnash::matrix m_bitmap_matrix;
- gnash::cxform m_bitmap_color_transform;
- bool m_has_nonzero_bitmap_additive_color;
+ if (!_ogl_accessible) {
+ return;
+ }
- fill_style()
- :
- m_mode(INVALID),
- m_bitmap_info(0),
- m_has_nonzero_bitmap_additive_color(false)
+ setup();
+ }
+
+ ~bitmap_info_ogl()
{
+ glDeleteTextures(1, &_texture_id);
+ glDisable(_ogl_img_type);
}
- // Push our style into OpenGL.
- void apply(/*const matrix& current_matrix*/) const
+ inline bool
+ ogl_accessible() const
{
-// GNASH_REPORT_FUNCTION;
- assert(m_mode != INVALID);
+ #if defined(_WIN32) || defined(WIN32)
+ return wglGetCurrentContext();
+ #elif defined(__APPLE_CC__)
+ return aglGetCurrentContext();
+ #else
+ return glXGetCurrentContext();
+ #endif
+ }
- if (m_mode == COLOR) {
- apply_color(m_color);
- glDisable(GL_TEXTURE_2D);
- } else if (m_mode == BITMAP_WRAP
- || m_mode == BITMAP_CLAMP) {
- assert(m_bitmap_info != 0);
+ void setup()
+ {
+ oglScopeEnable enabler(_ogl_img_type);
- apply_color(m_color);
+ glGenTextures(1, &_texture_id);
+ glBindTexture(_ogl_img_type, _texture_id);
- if (m_bitmap_info == 0) {
- glDisable(GL_TEXTURE_2D);
+ bool resize = false;
+ if (_img->height() == 1) {
+ if ( !isEven( _img->width() ) ) {
+ resize = true;
+ }
} else {
- // Set up the texture for rendering.
- {
- // Do the modulate part of the color
- // transform in the first pass. The
- // additive part, if any, needs to
- // happen in a second pass.
- glColor4f(m_bitmap_color_transform.m_[0][0],
- m_bitmap_color_transform.m_[1][0],
- m_bitmap_color_transform.m_[2][0],
- m_bitmap_color_transform.m_[3][0]
- );
+ if (!isEven( _img->width() ) || !isEven(_img->height()) ) {
+ resize = true;
+ }
+ }
+
+ if (!resize) {
+ upload(_img->data(), _img->width(), _img->height());
+ } else {
+
+ size_t w = 1; while (w < _img->width()) { w <<= 1; }
+ size_t h = 1;
+ if (_img->height() != 1) {
+ while (h < _img->height()) { h <<= 1; }
+ }
+
+ boost::scoped_array<uint8_t> resized_data(new
uint8_t[w*h*_img->pixelSize()]);
+ // Q: Would mipmapping these textures aid in performance?
+
+ GLint rv = gluScaleImage(_pixel_format, _img->width(),
+ _img->height(), GL_UNSIGNED_BYTE, _img->data(), w, h,
+ GL_UNSIGNED_BYTE, resized_data.get());
+ if (rv != 0) {
+ Tesselator::error(rv);
+ assert(0);
+ }
+
+ upload(resized_data.get(), w, h);
+ }
+
+ // _img (or a modified version thereof) has been uploaded to OpenGL. We
+ // no longer need to keep it around. Of course this goes against the
+ // principles of auto_ptr...
+ delete _img.release();
}
- if (m_bitmap_info->m_texture_id == 0 &&
m_bitmap_info->m_suspended_image.get() )
+ void upload(uint8_t* data, size_t width, size_t height)
{
-
m_bitmap_info->layout_image(m_bitmap_info->m_suspended_image.get());
-
m_bitmap_info->m_suspended_image.reset();
+ glTexParameteri(_ogl_img_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ // FIXME: confirm that OpenGL can handle this image
+
+ if (_ogl_img_type == GL_TEXTURE_1D) {
+ glTexImage1D(GL_TEXTURE_1D, 0, _pixel_format, width,
+ 0, _pixel_format, GL_UNSIGNED_BYTE, data);
+
+ } else {
+ glTexImage2D(_ogl_img_type, 0, _pixel_format, width, height,
+ 0, _pixel_format, GL_UNSIGNED_BYTE, data);
+
+ }
}
- // assert(m_bitmap_info->m_texture_id);
+ void apply(const gnash::matrix& bitmap_matrix,
+ render_handler::bitmap_wrap_mode wrap_mode)
+ {
+ glEnable(_ogl_img_type);
+
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+
+ if (!_ogl_accessible) {
+ // renderer context wasn't available when this class was instantiated.
+ _ogl_accessible=true;
+ setup();
+ }
- glBindTexture(GL_TEXTURE_2D,
m_bitmap_info->m_texture_id);
- glEnable(GL_TEXTURE_2D);
+ glEnable(_ogl_img_type);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
- if (m_mode == BITMAP_CLAMP) {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
+ glBindTexture(_ogl_img_type, _texture_id);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ if (wrap_mode == render_handler::WRAP_CLAMP) {
+ glTexParameteri(_ogl_img_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(_ogl_img_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else {
- assert(m_mode == BITMAP_WRAP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
GL_REPEAT);
+ glTexParameteri(_ogl_img_type, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(_ogl_img_type, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
// Set up the bitmap matrix for texgen.
- float inv_width = 1.0f /
m_bitmap_info->m_original_width;
- float inv_height = 1.0f /
m_bitmap_info->m_original_height;
+ float inv_width = 1.0f / _orig_width;
+ float inv_height = 1.0f / _orig_height;
- const gnash::matrix& m = m_bitmap_matrix;
+ const gnash::matrix& m = bitmap_matrix;
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
float p[4] = { 0, 0, 0, 0 };
p[0] = m.m_[0][0] * inv_width;
@@ -250,175 +465,126 @@
p[1] = m.m_[1][1] * inv_height;
p[3] = m.m_[1][2] * inv_height;
glTexGenfv(GL_T, GL_OBJECT_PLANE, p);
- }
- }
- }
-
- // Return true if we need to do a second pass to make
- // a valid color. This is for cxforms with additive
- // parts; this is the simplest way (that we know of)
- // to implement an additive color with stock OpenGL.
- bool needs_second_pass() const
- {
- if (m_mode == BITMAP_WRAP
- || m_mode == BITMAP_CLAMP) {
- return m_has_nonzero_bitmap_additive_color;
- } else {
- return false;
}
- }
-
- // Set OpenGL state for a necessary second pass.
- void apply_second_pass() const
- {
- assert(needs_second_pass());
-
- // The additive color also seems to be modulated by the
texture. So,
- // maybe we can fake this in one pass using using the mean
value of
- // the colors: c0*t+c1*t = ((c0+c1)/2) * t*2
- // I don't know what the alpha component of the color is for.
- //glDisable(GL_TEXTURE_2D);
- glColor4f(
- m_bitmap_color_transform.m_[0][1] / 255.0f,
- m_bitmap_color_transform.m_[1][1] / 255.0f,
- m_bitmap_color_transform.m_[2][1] / 255.0f,
- m_bitmap_color_transform.m_[3][1] / 255.0f
- );
+ private:
+ std::auto_ptr<image::image_base> _img;
+ GLenum _pixel_format;
+ GLenum _ogl_img_type;
+ bool _ogl_accessible;
+ GLuint _texture_id;
+ size_t _orig_width;
+ size_t _orig_height;
+};
- glBlendFunc(GL_ONE, GL_ONE);
- }
- void cleanup_second_pass() const
+class DSOEXPORT render_handler_ogl : public render_handler
+{
+public:
+ render_handler_ogl()
+ : _xscale(1.0),
+ _yscale(1.0)
{
+ // Turn on alpha blending.
+ // FIXME: do this when it's actually used?
+ glEnable(GL_BLEND);
+ // This blend operation is best for rendering antialiased points and lines
in
+ // no particular order.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
+ // Turn on line smoothing. Antialiased lines can be used to
+ // smooth the outsides of shapes.
+ glEnable(GL_LINE_SMOOTH);
+#if 0
+ // FIXME: figure out which of these is appropriate.
+ //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // GL_NICEST, GL_FASTEST,
GL_DONT_CARE
+#endif
- void disable() { m_mode = INVALID; }
- void set_color(const gnash::rgba& color) { m_mode = COLOR; m_color =
color; }
- void set_bitmap(const gnash::bitmap_info* bi_, const gnash::matrix&
m, bitmap_wrap_mode wm, const gnash::cxform& color_transform)
- {
- m_mode = (wm == WRAP_REPEAT) ? BITMAP_WRAP : BITMAP_CLAMP;
+ glMatrixMode(GL_PROJECTION);
- bitmap_info* bi = const_cast<bitmap_info*>(bi_);
+ float oversize = 1.0;
- assert( dynamic_cast<bitmap_info_ogl*> ( bi ) );
- m_bitmap_info = static_cast<bitmap_info_ogl*> ( bi );
- m_bitmap_matrix = m;
- m_bitmap_color_transform = color_transform;
- m_bitmap_color_transform.clamp();
+ // Flip the image, since (0,0) by default in OpenGL is the bottom left.
+ gluOrtho2D(-oversize, oversize, oversize, -oversize);
+ // Restore the matrix mode to the default.
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
- m_color = gnash::rgba(
- uint8_t(m_bitmap_color_transform.m_[0][0] * 255.0f),
- uint8_t(m_bitmap_color_transform.m_[1][0] * 255.0f),
- uint8_t(m_bitmap_color_transform.m_[2][0] * 255.0f),
- uint8_t(m_bitmap_color_transform.m_[3][0] * 255.0f));
- if (m_bitmap_color_transform.m_[0][1] > 1.0f
- || m_bitmap_color_transform.m_[1][1] > 1.0f
- || m_bitmap_color_transform.m_[2][1] > 1.0f
- || m_bitmap_color_transform.m_[3][1] > 1.0f)
- {
- m_has_nonzero_bitmap_additive_color = true;
- }
- else
- {
- m_has_nonzero_bitmap_additive_color = false;
- }
+#ifdef FIX_I810_LOD_BIAS
+ // If 2D textures weren't previously enabled, enable
+ // them now and force the driver to notice the update,
+ // then disable them again.
+ if (!glIsEnabled(GL_TEXTURE_2D)) {
+ // Clearing a mask of zero *should* have no
+ // side effects, but coupled with enbling
+ // GL_TEXTURE_2D it works around a segmentation
+ // fault in the driver for the Intel 810 chip.
+ oglScopeEnable enabler(GL_TEXTURE_2D);
+ glClear(0);
}
- bool is_valid() const { return m_mode != INVALID; }
- };
-
-
- // Style state.
- enum style_index
- {
- LEFT_STYLE = 0,
- RIGHT_STYLE,
- LINE_STYLE,
+#endif
- STYLE_COUNT
- };
- fill_style m_current_styles[STYLE_COUNT];
+ glShadeModel(GL_FLAT);
+ }
- gnash::bitmap_info* create_bitmap_info_rgb(image::rgb* im)
- // Given an image, returns a pointer to a bitmap_info class
- // that can later be passed to fill_styleX_bitmap(), to set a
- // bitmap fill style.
+ ~render_handler_ogl()
{
- return new bitmap_info_ogl(im);
}
- gnash::bitmap_info* create_bitmap_info_rgba(image::rgba* im)
- // Given an image, returns a pointer to a bitmap_info class
- // that can later be passed to fill_style_bitmap(), to set a
- // bitmap fill style.
- //
- // This version takes an image with an alpha channel.
+ virtual bitmap_info* create_bitmap_info_alpha(int w, int h, unsigned char*
data)
{
- return new bitmap_info_ogl(im);
+ log_unimpl("create_bitmap_info_alpha()");
+ return NULL;
}
- gnash::bitmap_info* create_bitmap_info_alpha(int w, int h, uint8_t*
data)
- // Create a bitmap_info so that it contains an alpha texture
- // with the given data (1 byte per texel).
- //
- // Munges *data (in order to make mipmaps)!!
+ virtual bitmap_info* create_bitmap_info_rgb(image::rgb* im)
{
- return new bitmap_info_ogl(w, h, data);
+ return new bitmap_info_ogl(im, GL_RGB);
}
+ virtual bitmap_info* create_bitmap_info_rgba(image::rgba* im)
+ {
+ return new bitmap_info_ogl(im, GL_RGBA);
+ }
- void delete_bitmap_info(gnash::bitmap_info* bi)
- // Delete the given bitmap info class.
+ virtual void delete_bitmap_info(bitmap_info* bi)
{
delete bi;
}
-#define GLYUV 0
+ enum video_frame_format
+ {
+ NONE,
+ YUV,
+ RGB
+ };
- // Returns the format the current renderer wants videoframes in.
- int videoFrameFormat() {
-#if GLYUV
- return YUV;
-#else
+ virtual int videoFrameFormat()
+ {
return RGB;
-#endif
}
- /// Draws the video frames
- void drawVideoFrame(image::image_base* baseframe, const matrix* m,
const rect* bounds){
-#if GLYUV
- image::yuv* frame = static_cast<image::yuv*>(baseframe);
-#else
+
+ virtual void drawVideoFrame(image::image_base* baseframe, const matrix* m,
const rect* bounds)
+ {
+ GNASH_REPORT_FUNCTION;
+
image::rgb* frame = static_cast<image::rgb*>(baseframe);
-#endif
+
glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);
-#if GLYUV
- static GLfloat yuv_rgb[16] = {
- 1, 1, 1, 0,
- 0, -0.3946517043589703515f, 2.032110091743119266f, 0,
- 1.139837398373983740f, -0.5805986066674976801f, 0, 0,
- 0, 0, 0, 1
- };
-#endif
glMatrixMode(GL_COLOR);
glPushMatrix();
-#if GLYUV
- glLoadMatrixf(yuv_rgb);
- glPixelTransferf(GL_GREEN_BIAS, -0.5f);
- glPixelTransferf(GL_BLUE_BIAS, -0.5f);
-#else
+
glLoadIdentity();
glPixelTransferf(GL_GREEN_BIAS, 0.0);
glPixelTransferf(GL_BLUE_BIAS, 0.0);
-#endif
+
gnash::point a, b, c, d;
m->transform(&a, gnash::point(bounds->get_x_min(),
bounds->get_y_min()));
m->transform(&b, gnash::point(bounds->get_x_max(),
bounds->get_y_min()));
@@ -434,32 +600,12 @@
float ypos = a.m_y < 0 ? 0.0f : a.m_y; //hack
glRasterPos2f(xpos, ypos); //hack
-#if GLYUV
- GLenum rgb[3] = {GL_RED, GL_GREEN, GL_BLUE};
-
- for (int i = 0; i < 3; ++i)
- {
- float zx = w_bounds / (float) frame->planes[i].w;
- float zy = h_bounds / (float) frame->planes[i].h;
- glPixelZoom(zx, - zy); // flip & zoom image
-
- if (i > 0)
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
- }
-
- glDrawPixels(frame->planes[i].w, frame->planes[i].h,
rgb[i], GL_UNSIGNED_BYTE, ptr);
- ptr += frame->planes[i].size;
- }
-#else
size_t height = frame->height();
size_t width = frame->width();
float zx = w_bounds / (float) width;
float zy = h_bounds / (float) height;
glPixelZoom(zx, -zy); // flip & zoom image
glDrawPixels(width, height, GL_RGB, GL_UNSIGNED_BYTE, ptr);
-#endif
glPopMatrix();
@@ -470,966 +616,844 @@
}
- // Ctor stub.
- render_handler_ogl()
+ // FIXME
+ geometry::Range2d<int>
+ world_to_pixel(const rect& worldbounds)
{
+ // TODO: verify this is correct
+ geometry::Range2d<int> ret(worldbounds.getRange());
+ ret.scale(1.0/20.0); // twips to pixels
+ return ret;
}
- // Dtor stub.
- ~render_handler_ogl()
+ // FIXME
+ point
+ pixel_to_world(int x, int y)
{
+ // TODO: verify this is correct
+ return point(PIXELS_TO_TWIPS(x), PIXELS_TO_TWIPS(y));
}
- void begin_display(
- const gnash::rgba& bg_color,
+ virtual void begin_display(
+ const rgba& bg_color,
int viewport_x0, int viewport_y0,
int viewport_width, int viewport_height,
float x0, float x1, float y0, float y1)
{
-// GNASH_REPORT_FUNCTION;
-
- m_display_width = fabsf(x1 - x0);
- m_display_height = fabsf(y1 - y0);
-
glViewport(viewport_x0, viewport_y0, viewport_width,
viewport_height);
+ glLoadIdentity();
- glPushMatrix();
- glOrtho(x0, x1, y0, y1, -1, 1);
+ gluOrtho2D(x0, x1, y0, y1);
- // Clear the background, if background color has alpha > 0.
- if (bg_color.m_a > 0)
- {
+ if (bg_color.m_a) {
// Setup the clearing color.
- glClearColor(bg_color.m_r / 255, bg_color.m_g / 255,
bg_color.m_b / 255,
- bg_color.m_a / 255);
- glClear(GL_COLOR_BUFFER_BIT);
+ glClearColor(bg_color.m_r / 255.0, bg_color.m_g / 255.0, bg_color.m_b /
255.0,
+ bg_color.m_a / 255.0);
+ // Do the actual clearing.
+ } else {
+ glClearColor(1.0, 1.0, 1.0, 1.0);
}
-#ifdef DEBUG_OPENGL
- check_error();
-#endif
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
- // Markus: Implement anti-aliasing here...
-#if 0
-/*
-Code from Timo Kanera...
-if (gl_antialias)
+ static void
+ apply_matrix(const gnash::matrix& m)
+ // multiply current matrix with opengl matrix
{
- glShadeModel (GL_SMOOTH);
- glEnable (GL_POLYGON_SMOOTH);
- glEnable (GL_LINE_SMOOTH);
- glEnable (GL_POINT_SMOOTH);
- mp_msg(MSGT_VO, MSGL_INFO, "[sgi] antialiasing on\n");
-}
-else {
- glShadeModel (GL_FLAT);
- glDisable (GL_POLYGON_SMOOTH);
- glDisable (GL_LINE_SMOOTH);
- glDisable (GL_POINT_SMOOTH);
- mp_msg(MSGT_VO, MSGL_INFO, "[sgi] antialiasing off\n");
-}
-*/
- // See if we want to, and can, use multitexture
- // antialiasing.
- bool s_multitexture_antialias = false;
- if (m_enable_antialias)
- {
- int tex_units = 0;
- glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &tex_units);
- if (tex_units >= 2)
- {
- s_multitexture_antialias = true;
- }
- // Make sure we have an edge texture available.
- if (s_multitexture_antialias == true)
- {
- // Very simple texture: 2 texels wide, 1 texel high.
- // Both texels are white; left texel is all clear,
right texel is all opaque.
- unsigned char edge_data[8] = { 255, 255, 255,
0, 255, 255, 255, 255 };
- GLuint s_edge_texture_id = 0;
-
- glActiveTextureARB(GL_TEXTURE1_ARB);
- glEnable(GL_TEXTURE_2D);
- glGenTextures(1, (GLuint*)&s_edge_texture_id);
- glBindTexture(GL_TEXTURE_2D, s_edge_texture_id);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ // FIXME: applying matrix transformations is faster than using
glMultMatrix!
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 1, 0,
GL_RGBA, GL_UNSIGNED_BYTE, edge_data);
-
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
GL_MODULATE); // @@ should we use a 1D texture???
-
- glDisable(GL_TEXTURE_2D);
- glActiveTextureARB(GL_TEXTURE0_ARB);
- glDisable(GL_TEXTURE_2D);
- }
- }
-#endif // 0
+ float mat[16];
+ memset(&mat[0], 0, sizeof(mat));
+ mat[0] = m.m_[0][0];
+ mat[1] = m.m_[1][0];
+ mat[4] = m.m_[0][1];
+ mat[5] = m.m_[1][1];
+ mat[10] = 1;
+ mat[12] = m.m_[0][2];
+ mat[13] = m.m_[1][2];
+ mat[15] = 1;
+ glMultMatrixf(mat);
}
- /// Sets the x/y scale for the movie
- void set_scale(float xscale, float yscale)
+ virtual void
+ end_display()
{
- _scale.set(xscale, yscale);
+ glFlush(); // Make OpenGL execute all commands in the buffer.
}
- void get_scale(point& scale)
+ /// Geometric transforms for mesh and line_strip rendering.
+ virtual void
+ set_matrix(const matrix& m)
{
- scale = _scale;
+ log_unimpl("set_matrix");
}
-
- bool getPixel(rgba& color_out, int x, int y)
+ /// Color transforms for mesh and line_strip rendering.
+ virtual void
+ set_cxform(const cxform& cx)
{
- if (x < 0 || y < 0) {
- return false;
- }
-
- GLint viewport[4];
- glGetIntegerv( GL_VIEWPORT, viewport);
- GLint buf_width = viewport[2], buf_height = viewport[3];
-
- if (x >= buf_width || y >= buf_height) {
- // X/Y coordinates are outside of the framebuffer.
- return false;
- }
-
- char buf[4];
-
- // Note that (0,0) in OpenGL is the lower left corner, while in Gnash
- // it is the top left corner.
- glReadPixels(x, buf_height - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buf);
-
- color_out.set(buf[0], buf[1], buf[2], buf[3]);
- return true;
+ log_unimpl("set_cxform");
}
-
-
- void end_display()
- // Clean up after rendering a frame. Client program is still
- // responsible for calling glSwapBuffers() or whatever.
+ /// Draw a line-strip directly, using a thin, solid line.
+ //
+ /// Can be used to draw empty boxes and cursors.
+ virtual void
+ draw_line_strip(const void* coords, int vertex_count, const rgba& color)
{
-// GNASH_REPORT_FUNCTION
+ glPushMatrix();
-#ifdef DEBUG_OPENGL
- check_error();
-#endif // DEBUG_OPENGL
+ glColor3ub(color.m_r, color.m_g, color.m_b);
- glPopMatrix();
- }
+ // Send the line-strip to OpenGL
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_SHORT, 0 /* tight packing */, coords);
+ glDrawArrays(GL_LINE_STRIP, 0, vertex_count);
+ // Draw a dot on the beginning and end coordinates to round lines.
+ // glVertexPointer: skip all but the first and last coordinates in the
line.
+ glVertexPointer(2, GL_SHORT, (sizeof(int16_t) * 2) * (vertex_count - 1),
coords);
+ glEnable(GL_POINT_SMOOTH); // Draw a round (antialiased) point.
+ glDrawArrays(GL_POINTS, 0, 2);
+ glDisable(GL_POINT_SMOOTH);
+ glPointSize(1); // return to default
- void set_matrix(const gnash::matrix& m)
- // Set the current transform for mesh & line-strip rendering.
- {
- m_current_matrix = m;
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glPopMatrix();
}
-
- void set_cxform(const gnash::cxform& cx)
- // Set the current color transform for mesh & line-strip rendering.
+ virtual void draw_poly(const point* corners, size_t corner_count,
+ const rgba& fill, const rgba& outline, bool masked)
{
- m_current_cxform = cx;
+ log_unimpl("draw_poly");
}
- static void apply_matrix(const gnash::matrix& m)
- // multiply current matrix with opengl matrix
+ virtual void draw_bitmap(
+ const matrix& m,
+ const bitmap_info* bi,
+ const rect& coords,
+ const rect& uv_coords,
+ const rgba& color)
{
- float mat[16];
- memset(&mat[0], 0, sizeof(mat));
- mat[0] = m.m_[0][0];
- mat[1] = m.m_[1][0];
- mat[4] = m.m_[0][1];
- mat[5] = m.m_[1][1];
- mat[10] = 1;
- mat[12] = m.m_[0][2];
- mat[13] = m.m_[1][2];
- mat[15] = 1;
- glMultMatrixf(mat);
+ log_unimpl("draw_bitmap");
}
- static void apply_color(const gnash::rgba& c)
- // Set the given color.
+ virtual void set_antialiased(bool enable)
{
- glColor4ub(c.m_r, c.m_g, c.m_b, c.m_a);
+ log_unimpl("set_antialiased");
}
- void fill_style_disable(int fill_side)
- // Don't fill on the {0 == left, 1 == right} side of a path.
+ virtual void begin_submit_mask()
{
- assert(fill_side >= 0 && fill_side < 2);
+ glEnable(GL_STENCIL_TEST);
+ glClearStencil(0x0); // FIXME: default is zero, methinks
+ glClear(GL_STENCIL_BUFFER_BIT);
- m_current_styles[fill_side].disable();
+ // Since we are marking the stencil, the stencil function test should
+ // always succeed.
+ glStencilFunc (GL_NEVER, 0x1, 0x1);
+
+ glStencilOp (GL_REPLACE /* Stencil test fails */ ,
+ GL_ZERO /* Value is ignored */ ,
+ GL_REPLACE /* Stencil test passes */);
}
+ virtual void end_submit_mask()
+ {
+ glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
+ glStencilFunc(GL_EQUAL, 0x1, 0x1);
+ }
+
+ virtual void disable_mask()
+ {
+ glDisable(GL_STENCIL_TEST);
+ }
- void line_style_disable()
- // Don't draw a line on this path.
+#if 0
+ void print_path(const path& path)
{
- m_current_styles[LINE_STYLE].disable();
+ std::cout << "Origin: ("
+ << path.m_ax
+ << ", "
+ << path.m_ay
+ << ") fill0: "
+ << path.m_fill0
+ << " fill1: "
+ << path.m_fill1
+ << " line: "
+ << path.m_line
+ << " new shape: "
+ << path.m_new_shape
+ << " number of edges: "
+ << path.m_edges.size()
+ << " edge endpoint: ("
+ << path.m_edges.back().m_ax
+ << ", "
+ << path.m_edges.back().m_ay
+ << " ) points:";
+
+ for (std::vector<edge>::const_iterator it = path.m_edges.begin(), end =
path.m_edges.end();
+ it != end; ++it) {
+ const edge& cur_edge = *it;
+ std::cout << "( " << cur_edge.m_ax << ", " << cur_edge.m_ay << ") ";
}
+ std::cout << std::endl;
+ }
+#endif
- void fill_style_color(int fill_side, const gnash::rgba& color)
- // Set fill style for the left interior of the shape. If
- // enable is false, turn off fill for the left interior.
+ path reverse_path(const path& cur_path)
{
- assert(fill_side >= 0 && fill_side < 2);
+ const edge& cur_end = cur_path.m_edges.back();
-
m_current_styles[fill_side].set_color(m_current_cxform.transform(color));
+ float prev_cx = cur_end.m_cx;
+ float prev_cy = cur_end.m_cy;
+
+ path newpath(cur_end.m_ax, cur_end.m_ay, cur_path.m_fill1,
cur_path.m_fill0, cur_path.m_line);
+
+ float prev_ax = cur_end.m_ax;
+ float prev_ay = cur_end.m_ay;
+
+ for (std::vector<edge>::const_reverse_iterator it =
cur_path.m_edges.rbegin()+1, end = cur_path.m_edges.rend();
+ it != end; ++it) {
+ const edge& cur_edge = *it;
+
+ if (prev_ax == prev_cx && prev_ay == prev_cy) {
+ prev_cx = cur_edge.m_ax;
+ prev_cy = cur_edge.m_ay;
}
+ edge newedge(prev_cx, prev_cy, cur_edge.m_ax, cur_edge.m_ay);
+
+ newpath.m_edges.push_back(newedge);
+
+ prev_cx = cur_edge.m_cx;
+ prev_cy = cur_edge.m_cy;
+ prev_ax = cur_edge.m_ax;
+ prev_ay = cur_edge.m_ay;
- void line_style_color(const gnash::rgba& color)
- // Set the line style of the shape. If enable is false, turn
- // off lines for following curve segments.
- {
-
m_current_styles[LINE_STYLE].set_color(m_current_cxform.transform(color));
}
+ edge newlastedge(prev_cx, prev_cy, cur_path.m_ax, cur_path.m_ay);
+ newpath.m_edges.push_back(newlastedge);
- void fill_style_bitmap(int fill_side, const gnash::bitmap_info* bi,
const gnash::matrix& m, bitmap_wrap_mode wm)
- {
- assert(fill_side >= 0 && fill_side < 2);
- m_current_styles[fill_side].set_bitmap(bi, m, wm, m_current_cxform);
+ return newpath;
}
- void line_style_width(float width)
+ const path* find_connecting_path(const path& to_connect,
+ std::list<const path*> path_refs)
{
- float norm_width = std::max(1.0f, width); // TODO: stroke
scaling
-// GNASH_REPORT_FUNCTION;
- if ( norm_width == 1.0 ) // "hairline", see render_handler_tri.h
- {
- glLineWidth(1); // expected: 1 pixel
+ float target_x = to_connect.m_edges.back().m_ax;
+ float target_y = to_connect.m_edges.back().m_ay;
+
+ if (target_x == to_connect.m_ax &&
+ target_y == to_connect.m_ay) {
+ return NULL;
}
- else
- {
- // TODO: OpenGL doesn't seem to handle very
- // low-width lines well, even with anti-aliasing
- // enabled
- // But this is a start (20 TWIPS' width = 1 pixel's)
- float resized_width =
- norm_width * ( (_scale.m_x + _scale.m_y) / 2.0 );
- glLineWidth(TWIPS_TO_PIXELS(resized_width));
- glPointSize(TWIPS_TO_PIXELS(resized_width));
+ for (std::list<const path*>::const_iterator it = path_refs.begin(), end =
path_refs.end();
+ it != end; ++it) {
+ const path* cur_path = *it;
+
+ if (cur_path == &to_connect) {
+
+ continue;
+
+ }
+
+
+ if (cur_path->m_ax == target_x && cur_path->m_ay == target_y) {
+
+ if (cur_path->m_fill1 != to_connect.m_fill1) {
+ continue;
+ }
+ return cur_path;
}
}
- void draw_mesh_strip(const void* coords, int vertex_count)
+ return NULL;
+ }
+
+ PathVec normalize_paths(const PathVec &paths)
{
-// GNASH_REPORT_FUNCTION;
+ PathVec normalized;
-#define NORMAL_RENDERING
-//#define MULTIPASS_ANTIALIASING
+ for (PathVec::const_iterator it = paths.begin(), end = paths.end();
+ it != end; ++it) {
+ const path& cur_path = *it;
-#ifdef NORMAL_RENDERING
- // Set up current style.
- m_current_styles[LEFT_STYLE].apply();
+ if (cur_path.m_edges.empty()) {
+ continue;
- glPushMatrix();
- apply_matrix(m_current_matrix);
+ } else if (cur_path.m_fill0 && cur_path.m_fill1) {
- // Send the tris to OpenGL
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(2, GL_SHORT, sizeof(int16_t) * 2, coords);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count);
+ // Two fill styles; duplicate and then reverse the left-filled one.
+ normalized.push_back(cur_path);
+ normalized.back().m_fill0 = 0;
- if (m_current_styles[LEFT_STYLE].needs_second_pass())
- {
- m_current_styles[LEFT_STYLE].apply_second_pass();
- glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count);
- m_current_styles[LEFT_STYLE].cleanup_second_pass();
+ path newpath = reverse_path(cur_path);
+ newpath.m_fill0 = 0;
+
+ normalized.push_back(newpath);
+
+ } else if (cur_path.m_fill0) {
+ // Left fill style.
+ path newpath = reverse_path(cur_path);
+ newpath.m_fill0 = 0;
+
+ normalized.push_back(newpath);
+ } else if (cur_path.m_fill1) {
+ // Right fill style.
+
+ normalized.push_back(cur_path);
+ } else {
+ // No fill styles; copy without modifying.
+ normalized.push_back(cur_path);
}
- glDisableClientState(GL_VERTEX_ARRAY);
+ }
- glPopMatrix();
-#endif // NORMAL_RENDERING
+ return normalized;
+ }
-#ifdef MULTIPASS_ANTIALIASING
- // So this approach basically works. This
- // implementation is not totally finished; two pass
- // materials (i.e. w/ additive color) aren't correct,
- // and there are some texture etc issues because I'm
- // just hosing state uncarefully here. It needs the
- // optimization of only filling the bounding box of
- // the shape. You must have destination alpha.
- //
- // It doesn't look quite perfect on my GF4. For one
- // thing, you kinda want to crank down the max curve
- // subdivision error, because suddenly you can see
- // sub-pixel shape much better. For another thing,
- // the antialiasing isn't quite perfect, to my eye.
- // It could be limited alpha precision, imperfections
- // GL_POLYGON_SMOOTH, and/or my imagination.
- glDisable(GL_TEXTURE_2D);
- glEnable(GL_POLYGON_SMOOTH);
- glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); // GL_NICEST,
GL_FASTEST, GL_DONT_CARE
- // Clear destination alpha.
- //
- // @@ TODO Instead of drawing this huge screen-filling
- // quad, we should take a bounding-box param from the
- // caller, and draw the box (after apply_matrix;
- // i.e. the box is in object space). The point being,
- // to only fill the part of the screen that the shape
- // is in.
- glBlendFunc(GL_ZERO, GL_SRC_COLOR);
- glColor4f(1, 1, 1, 0);
- glBegin(GL_QUADS);
- glVertex2f(0, 0);
- glVertex2f(100000, 0);
- glVertex2f(100000, 100000);
- glVertex2f(0, 100000);
- glEnd();
-
- // Set mode for drawing alpha mask.
- glBlendFunc(GL_ONE, GL_ONE); // additive blending
- glColor4f(0, 0, 0, m_current_styles[LEFT_STYLE].m_color.m_a /
255.0f);
- glPushMatrix();
- apply_matrix(m_current_matrix);
- // Send the tris to OpenGL. This produces an
- // antialiased alpha mask of the mesh shape, in the
- // destination alpha channel.
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(2, GL_SHORT, sizeof(int16_t) * 2, coords);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count);
- glDisableClientState(GL_VERTEX_ARRAY);
+ /// Analyzes a set of paths to detect real presence of fills and/or outlines
+ /// TODO: This should be something the character tells us and should be
+ /// cached.
+ void analyze_paths(const PathVec &paths, bool& have_shape,
+ bool& have_outline) {
+ //normalize_paths(paths);
+ have_shape=false;
+ have_outline=false;
- glPopMatrix();
+ int pcount = paths.size();
- // Set up desired fill style.
- m_current_styles[LEFT_STYLE].apply();
+ for (int pno=0; pno<pcount; pno++) {
- // Apply fill, modulated with alpha mask.
- //
- // @@ TODO see note above about filling bounding box only.
- glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
- glBegin(GL_QUADS);
- glVertex2f(0, 0);
- glVertex2f(100000, 0);
- glVertex2f(100000, 100000);
- glVertex2f(0, 100000);
- glEnd();
-
-// xxxxx ??? Hm, is our mask still intact, or did we just erase it?
-// if (m_current_styles[LEFT_STYLE].needs_second_pass())
-// {
-// m_current_styles[LEFT_STYLE].apply_second_pass();
-// glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count);
-// m_current_styles[LEFT_STYLE].cleanup_second_pass();
-// }
+ const path &the_path = paths[pno];
- // @@ hm, there is perhaps more state that needs
- // fixing here, or setting elsewhere.
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-#endif // MULTIPASS_ANTIALIASING
+ if ((the_path.m_fill0>0) || (the_path.m_fill1>0)) {
+ have_shape=true;
+ if (have_outline) return; // have both
+ }
+
+ if (the_path.m_line>0) {
+ have_outline=true;
+ if (have_shape) return; // have both
}
+ }
+ }
- void draw_line_strip(const void* coords, int vertex_count)
- // Draw the line strip formed by the sequence of points.
+ void apply_fill_style(const fill_style& style, const matrix& mat, const
cxform& cx)
{
-// GNASH_REPORT_FUNCTION;
- // Set up current style.
- m_current_styles[LINE_STYLE].apply();
+ int fill_type = style.get_type();
- glPushMatrix();
- apply_matrix(m_current_matrix);
+ rgba c = cx.transform(style.get_color());
- // Send the line-strip to OpenGL
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(2, GL_SHORT, 0 /* tight packing */, coords);
- glDrawArrays(GL_LINE_STRIP, 0, vertex_count);
+ glColor4ub(c.m_r, c.m_g, c.m_b, c.m_a);
- // Draw a dot on the beginning and end coordinates to round lines.
- // glVertexPointer: skip all but the first and last coordinates
in the line.
- glVertexPointer(2, GL_SHORT, (sizeof(int16_t) * 2) * (vertex_count
- 1), coords);
- glEnable(GL_POINT_SMOOTH); // Draw a round (antialiased) point.
- glDrawArrays(GL_POINTS, 0, 2);
- glDisable(GL_POINT_SMOOTH);
- glPointSize(1); // return to default
+ switch (fill_type) {
- glDisableClientState(GL_VERTEX_ARRAY);
- glPopMatrix();
- }
+ case SWF::FILL_LINEAR_GRADIENT:
+ case SWF::FILL_RADIAL_GRADIENT:
+ case SWF::FILL_FOCAL_GRADIENT:
+ {
+ bitmap_info_ogl* binfo =
static_cast<bitmap_info_ogl*>(style.need_gradient_bitmap());
+ matrix m = style.get_gradient_matrix();
- void draw_bitmap(
- const gnash::matrix& m,
- const gnash::bitmap_info* bi_,
- const gnash::rect& coords,
- const gnash::rect& uv_coords,
- const gnash::rgba& color)
- // Draw a rectangle textured with the given bitmap, with the
- // given color. Apply given transform; ignore any currently
- // set transforms.
- //
- // Intended for textured glyph rendering.
+ binfo->apply(m, render_handler::WRAP_CLAMP);
+
+ break;
+ }
+ case SWF::FILL_TILED_BITMAP_HARD:
+ case SWF::FILL_TILED_BITMAP:
{
-// GNASH_REPORT_FUNCTION;
- assert(bi_);
+ bitmap_info_ogl* binfo =
static_cast<bitmap_info_ogl*>(style.get_bitmap_info());
- apply_color(color);
+ binfo->apply(style.get_bitmap_matrix(), render_handler::WRAP_REPEAT);
+ break;
+ }
- gnash::point a, b, c, d;
- m.transform(&a, gnash::point(coords.get_x_min(),
coords.get_y_min()));
- m.transform(&b, gnash::point(coords.get_x_max(),
coords.get_y_min()));
- m.transform(&c, gnash::point(coords.get_x_min(),
coords.get_y_max()));
- d.m_x = b.m_x + c.m_x - a.m_x;
- d.m_y = b.m_y + c.m_y - a.m_y;
+ case SWF::FILL_CLIPPED_BITMAP:
+ // smooth=true;
+ case SWF::FILL_CLIPPED_BITMAP_HARD:
+ {
+ bitmap_info_ogl* binfo =
dynamic_cast<bitmap_info_ogl*>(style.get_bitmap_info());
- gnash::bitmap_info* cbi_ = const_cast<gnash::bitmap_info*>(bi_);
+ assert(binfo);
- assert(dynamic_cast<bitmap_info_ogl*>(cbi_));
- bitmap_info_ogl* bi = static_cast<bitmap_info_ogl*>(cbi_);
+ binfo->apply(style.get_bitmap_matrix(), render_handler::WRAP_CLAMP);
- if (bi->m_texture_id == 0 &&
bi->m_suspended_image.get() )
+ break;
+ }
+
+ case SWF::FILL_SOLID:
{
- bi->layout_image(bi->m_suspended_image.get());
- bi->m_suspended_image.reset();
+ rgba c = cx.transform(style.get_color());
+
+ glColor4ub(c.m_r, c.m_g, c.m_b, c.m_a);
}
- // assert(bi->m_texture_id);
+ } // switch
+ }
- glBindTexture(GL_TEXTURE_2D, bi->m_texture_id);
- glEnable(GL_TEXTURE_2D);
- glDisable(GL_TEXTURE_GEN_S);
- glDisable(GL_TEXTURE_GEN_T);
- glBegin(GL_TRIANGLE_STRIP);
- float xmin = uv_coords.get_x_min();
- float xmax = uv_coords.get_x_max();
- float ymin = uv_coords.get_y_min();
- float ymax = uv_coords.get_y_max();
+ void apply_line_style(const line_style& style, const cxform& cx, const
matrix& mat)
+ {
+ // GNASH_REPORT_FUNCTION;
- glTexCoord2f(xmin, ymin);
- glVertex2f(a.m_x, a.m_y);
+ // In case GL_TEXTURE_2D was enabled by apply_fill_style(), disable it now.
+ // FIXME: this sucks
+ glDisable(GL_TEXTURE_2D);
- glTexCoord2f(xmax, ymin);
- glVertex2f(b.m_x, b.m_y);
- glTexCoord2f(xmin, ymax);
- glVertex2f(c.m_x, c.m_y);
+ float width = style.get_width();
- glTexCoord2f(xmax, ymax);
- glVertex2f(d.m_x, d.m_y);
+ if (width <= 1.0f) {
+ glLineWidth(1.0f);
+ } else {
- glEnd();
+ float stroke_scale = fabsf(mat.get_x_scale()) + fabsf(mat.get_y_scale());
+ stroke_scale /= 2.0f;
+ stroke_scale *= (fabsf(_xscale) + fabsf(_yscale)) / 2.0f;
+ width *= stroke_scale;
+ width = TWIPS_TO_PIXELS(width);
+
+ GLfloat width_info[2];
+
+ glGetFloatv( GL_LINE_WIDTH_RANGE, width_info);
+
+ if (width > width_info[1]) {
+ log_error("Your OpenGL implementation does not support the line width"
\
+ " requested. Lines will be drawn with reduced width.");
}
- void begin_submit_mask()
- {
-// GNASH_REPORT_FUNCTION;
- glEnable(GL_STENCIL_TEST);
- glClearStencil(0);
- glClear(GL_STENCIL_BUFFER_BIT);
- glColorMask(0,0,0,0); // disable framebuffer writes
- glEnable(GL_STENCIL_TEST); // enable stencil buffer for "marking"
the mask
- glStencilFunc(GL_ALWAYS, 1, 1); // always passes, 1 bit plane,
1 as mask
- glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // we set the stencil
buffer to 1 where we draw any polygon
- // keep if test fails,
keep if test passes but buffer test fails
- // replace if test
passes
+ glLineWidth(width);
+ glPointSize(width);
}
- void end_submit_mask()
- {
- glColorMask(1,1,1,1); // enable framebuffer writes
- glStencilFunc(GL_EQUAL, 1, 1); // we draw only where the
stencil is 1 (where the mask was drawn)
- glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't change the
stencil buffer
+ rgba c = cx.transform(style.get_color());
+
+ glColor4ub(c.m_r, c.m_g, c.m_b, c.m_a);
}
- void disable_mask()
+ PathPointMap getPathPoints(const PathVec& path_vec)
{
- glDisable(GL_STENCIL_TEST);
- }
-}; // end class render_handler_ogl
+ PathPointMap pathpoints;
+ for (PathVec::const_iterator it = path_vec.begin(), end = path_vec.end();
+ it != end; ++it) {
+ const path& cur_path = *it;
-// bitmap_info_ogl implementation
+ if (!cur_path.m_edges.size()) {
+ continue;
+ }
-/*
+ pathpoints[&cur_path] = interpolate(cur_path.m_edges, cur_path.m_ax,
+ cur_path.m_ay);
-Markus: A. A. I still miss you and the easter 2006, you know.
- A. J. I miss you too, but you'll probably not read this code ever... :/
+ }
-*/
+ return pathpoints;
+ }
-void hardware_resample(int bytes_per_pixel, int src_width, int src_height,
uint8_t* src_data, int dst_width, int dst_height)
-// Code from Alex Streit
-//
-// Sets the current texture to a resampled/expanded version of the
-// given image data.
-{
-// GNASH_REPORT_FUNCTION;
- assert(bytes_per_pixel == 3 || bytes_per_pixel == 4);
+ typedef std::vector<const path*> PathPtrVec;
- unsigned int in_format = bytes_per_pixel == 3 ? GL_RGB : GL_RGBA;
- unsigned int out_format = bytes_per_pixel == 3 ? GL_RGB : GL_RGBA;
- // alex: use the hardware to resample the image
- // issue: does not work when image > allocated window size!
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glPushAttrib(GL_TEXTURE_BIT | GL_ENABLE_BIT);
+ void
+ draw_outlines(const PathVec& path_vec, const PathPointMap& pathpoints, const
matrix& mat,
+ const cxform& cx, const std::vector<fill_style>& fill_styles,
+ const std::vector<line_style>& line_styles)
{
- char* temp = new char[dst_width * dst_height * bytes_per_pixel];
- //memset(temp,255,w*h*3);
- glTexImage2D(GL_TEXTURE_2D, 0, in_format, dst_width, dst_height, 0,
out_format, GL_UNSIGNED_BYTE, temp);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, src_width, src_height,
out_format, GL_UNSIGNED_BYTE, src_data);
- glLoadIdentity();
- glViewport(0, 0, dst_width, dst_height);
- glOrtho(0, dst_width, 0, dst_height, 0.9, 1.1);
- glColor3f(1, 1, 1);
- glNormal3f(0, 0, 1);
- glBegin(GL_QUADS);
- {
- glTexCoord2f(0, (float) src_height / dst_height);
- glVertex3f(0, 0, -1);
- glTexCoord2f( (float) src_width / dst_width, (float) src_height /
dst_height);
- glVertex3f((float) dst_width, 0, -1);
- glTexCoord2f( (float) src_width / dst_width, 0);
- glVertex3f((float) dst_width, (float) dst_height, -1);
- glTexCoord2f(0, 0);
- glVertex3f(0, (float) dst_height, -1);
- }
- glEnd();
- glCopyTexImage2D(GL_TEXTURE_2D, 0, out_format, 0,0, dst_width,
dst_height, 0);
- delete[] temp;
- }
- glPopAttrib();
- glPopMatrix();
- glPopMatrix();
-}
+ for (PathVec::const_iterator it = path_vec.begin(), end = path_vec.end();
+ it != end; ++it) {
+ const path& cur_path = *it;
+ if (cur_path.m_line) {
+ apply_line_style(line_styles[cur_path.m_line-1], cx, mat);
+ } else if (fill_styles[cur_path.m_fill1-1].get_type() ==
SWF::FILL_SOLID) {
-void software_resample(
- int bytes_per_pixel,
- int src_width,
- int src_height,
- int src_pitch,
- uint8_t* src_data,
- int dst_width,
- int dst_height)
-// Code from Alex Streit
-//
-// Creates an OpenGL texture of the specified dst dimensions, from a
-// resampled version of the given src image. Does a bilinear
-// resampling to create the dst image.
-{
-// GNASH_REPORT_FUNCTION;
- assert(bytes_per_pixel == 3 || bytes_per_pixel == 4);
+ glLineWidth(1.0);
- assert(dst_width >= src_width);
- assert(dst_height >= src_height);
+ rgba c = cx.transform(fill_styles[cur_path.m_fill1-1].get_color());
+ glColor4ub(c.m_r, c.m_g, c.m_b, c.m_a);
+ } else {
+ continue;
+ }
- unsigned int internal_format = bytes_per_pixel == 3 ? GL_RGB :
GL_RGBA;
- unsigned int input_format = bytes_per_pixel == 3 ? GL_RGB : GL_RGBA;
+ assert(pathpoints.find(&cur_path) != pathpoints.end());
- // FAST bi-linear filtering
- // the code here is designed to be fast, not readable
- uint8_t* rescaled = new uint8_t[dst_width * dst_height * bytes_per_pixel];
- float Uf, Vf; // fractional parts
- float Ui, Vi; // integral parts
- float w1, w2, w3, w4; // weighting
- uint8_t* psrc;
- uint8_t* pdst = rescaled;
- // i1,i2,i3,i4 are the offsets of the surrounding 4 pixels
- const int i1 = 0;
- const int i2 = bytes_per_pixel;
- int i3 = src_pitch;
- int i4 = src_pitch + bytes_per_pixel;
- // change in source u and v
- float dv = (float)(src_height-2) / dst_height;
- float du = (float)(src_width-2) / dst_width;
- // source u and source v
- float U;
- float V=0;
+ const std::vector<oglVertex>& shape_points =
(*pathpoints.find(&cur_path)).second;
-#define BYTE_SAMPLE(offset) \
- (uint8_t) (w1 * psrc[i1 + (offset)] + w2 * psrc[i2 + (offset)] + w3 *
psrc[i3 + (offset)] + w4 * psrc[i4 + (offset)])
+ //glDisable(GL_TEXTURE_1D);
+ //glDisable(GL_TEXTURE_2D);
+ // Draw outlines.
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(3, GL_DOUBLE, 0 /* tight packing */,
&shape_points.front());
+ glDrawArrays(GL_LINE_STRIP, 0, shape_points.size());
- if (bytes_per_pixel == 3)
- {
- for (int v = 0; v < dst_height; ++v)
- {
- Vf = modff(V, &Vi);
- V+=dv;
- U=0;
+ // Draw a dot on the beginning and end coordinates to round lines.
+ // glVertexPointer: skip all but the first and last coordinates in the
line.
+ glVertexPointer(3, GL_DOUBLE, (sizeof(GLdouble) * 3) *
(shape_points.size() - 1), &shape_points.front());
+ glEnable(GL_POINT_SMOOTH); // Draw a round (antialiased) point.
+ glDrawArrays(GL_POINTS, 0, 2);
+ glDisable(GL_POINT_SMOOTH);
+ glPointSize(1); // return to default
- for (int u = 0; u < dst_width; ++u)
- {
- Uf = modff(U, &Ui);
- U+=du;
- w1 = (1 - Uf) * (1 - Vf);
- w2 = Uf * (1 - Vf);
- w3 = (1 - Uf) * Vf;
- w4 = Uf * Vf;
- psrc = &src_data[(int) (Vi * src_pitch) + (int) (Ui
* bytes_per_pixel)];
+ glDisableClientState(GL_VERTEX_ARRAY);
- *pdst++ = BYTE_SAMPLE(0); // red
- *pdst++ = BYTE_SAMPLE(1); // green
- *pdst++ = BYTE_SAMPLE(2); // blue
- psrc += 3;
- }
}
}
- else
- {
- assert(bytes_per_pixel == 4);
- for (int v = 0; v < dst_height; ++v)
- {
- Vf = modff(V, &Vi);
- V+=dv;
- U=0;
- for (int u = 0; u < dst_width; ++u)
+ std::list<WholeShape> get_whole_shapes(const PathPtrVec &paths)
{
- Uf = modff(U, &Ui);
- U+=du;
-
- w1 = (1 - Uf) * (1 - Vf);
- w2 = Uf * (1 - Vf);
- w3 = (1 - Uf) * Vf;
- w4 = Uf * Vf;
- psrc = &src_data[(int) (Vi * src_pitch) + (int) (Ui
* bytes_per_pixel)];
+ // First, we create a vector of pointers to the individual paths. This is
+ // intended to allow us to keep track of which paths have already been
+ // used inside a shape.
- *pdst++ = BYTE_SAMPLE(0); // red
- *pdst++ = BYTE_SAMPLE(1); // green
- *pdst++ = BYTE_SAMPLE(2); // blue
- *pdst++ = BYTE_SAMPLE(3); // alpha
+ std::list<const path*> path_refs;
+ std::list<WholeShape> shapes;
- psrc += 4;
- }
+ for (PathPtrVec::const_iterator it = paths.begin(), end = paths.end();
+ it != end; ++it) {
+ const path* cur_path = *it;
+ path_refs.push_back(cur_path);
}
+
+ for (std::list<const path*>::const_iterator it = path_refs.begin(), end =
path_refs.end();
+ it != end; ++it) {
+ const path* cur_path = *it;
+
+ if (cur_path->m_edges.empty()) {
+ continue;
}
- glTexImage2D(GL_TEXTURE_2D, 0, internal_format, dst_width, dst_height, 0,
input_format, GL_UNSIGNED_BYTE, rescaled);
+ if (!cur_path->m_fill0 && !cur_path->m_fill1) {
+ continue;
+ }
- delete [] rescaled;
-}
+ WholeShape shape;
+ shape.newPath(*cur_path);
-bitmap_info_ogl::bitmap_info_ogl()
-// Make a placeholder bitmap_info. Must be filled in later before
-// using.
-{
-// GNASH_REPORT_FUNCTION;
- m_texture_id = 0;
- m_original_width = 0;
- m_original_height = 0;
-}
+ const path* connector = find_connecting_path(*cur_path, path_refs);
-void bitmap_info_ogl::layout_image(image::image_base* im)
-{
+ while (connector) {
+ shape.addPath(*connector);
- assert(im);
+ std::list<const path*>::const_iterator iter = it;
+ iter++;
- // Create the texture.
- glEnable(GL_TEXTURE_2D);
- glGenTextures(1, (GLuint*)&m_texture_id);
- glBindTexture(GL_TEXTURE_2D, m_texture_id);
+ connector = find_connecting_path(*connector, std::list<const
path*>(iter, end));
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ // make sure we don't iterate over the connecting path in the for loop.
+ path_refs.remove(connector);
+ }
- m_original_width = im->width();
- m_original_height = im->height();
+ shapes.push_back(shape);
+ }
- switch (im->m_type)
- {
- case image::image_base::RGB:
- {
+ return shapes;
+ }
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR);
- size_t w = 1; while (w < im->width()) { w <<= 1; }
- size_t h = 1; while (h < im->height()) { h <<= 1; }
- if (w != im->width() || h != im->height())
+ std::list<PathPtrVec> get_contours(const PathPtrVec &paths)
{
-#if (RESAMPLE_METHOD == 1)
- int viewport_dim[2] = { 0, 0 };
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS,
&viewport_dim[0]);
- if (w > viewport_dim[0]
- || h > viewport_dim[1]
- || im->m_width * 3 != im->m_pitch)
- {
- // Can't use hardware resample. Either
frame
- // buffer isn't big enough to fit the
source
- // texture, or the source data isn't
padded
- // quite right.
- software_resample(3, im->width(),
im->height(), im->pitch(), im->data(), w, h);
+ std::list<const path*> path_refs;
+ std::list<PathPtrVec> contours;
+
+
+ for (PathPtrVec::const_iterator it = paths.begin(), end = paths.end();
+ it != end; ++it) {
+ const path* cur_path = *it;
+ path_refs.push_back(cur_path);
}
- else
- {
- hardware_resample(3, im->width(),
im->height(), im->data(), w, h);
+
+ for (std::list<const path*>::const_iterator it = path_refs.begin(), end =
path_refs.end();
+ it != end; ++it) {
+ const path* cur_path = *it;
+
+ if (cur_path->m_edges.empty()) {
+ continue;
}
-#elif (RESAMPLE_METHOD == 2)
- {
- // Faster/simpler software bilinear rescale.
- software_resample(3, im->width(), im->height(),
im->pitch(), im->data(), w, h);
+
+ if (!cur_path->m_fill0 && !cur_path->m_fill1) {
+ continue;
}
-#else
- {
- // Fancy but slow software resampling.
- std::auto_ptr<image::rgb> rescaled (
image::create_rgb(w, h) );
- image::resample(rescaled.get(), 0, 0, w - 1, h - 1,
- im, 0, 0, (float) im->m_width, (float)
im->m_height);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
GL_UNSIGNED_BYTE, rescaled->data());
+ PathPtrVec contour;
+
+ contour.push_back(cur_path);
+
+ const path* connector = find_connecting_path(*cur_path, path_refs);
+
+ while (connector) {
+ contour.push_back(connector);
+
+ const path* tmp = connector;
+ connector = find_connecting_path(*connector, std::list<const
path*>(boost::next(it), end));
+
+ // make sure we don't iterate over the connecting path in the for loop.
+ path_refs.remove(tmp);
}
-#endif
- }
- else
- {
- // Use original image directly.
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0,
GL_RGB, GL_UNSIGNED_BYTE, im->data());
+
+ contours.push_back(contour);
}
- break;
+ return contours;
}
- case image::image_base::RGBA:
- {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR);
- size_t w = 1; while (w < im->width()) { w <<= 1; }
- size_t h = 1; while (h < im->height()) { h <<= 1; }
- if (w != im->width() || h != im->height())
- {
-#if (RESAMPLE_METHOD == 1)
- int viewport_dim[2] = { 0, 0 };
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS,
&viewport_dim[0]);
- if (w > viewport_dim[0]
- || h > viewport_dim[1]
- || im->m_width * 4 != im->m_pitch)
+
+ PathPtrVec
+ get_paths_by_style(const PathVec& path_vec, unsigned int style)
{
- // Can't use hardware resample. Either
frame
- // buffer isn't big enough to fit the
source
- // texture, or the source data isn't
padded
- // quite right.
- software_resample(4, im->width(),
im->height(), im->pitch(), im->data(), w, h);
+ PathPtrVec paths;
+ for (PathVec::const_iterator it = path_vec.begin(), end = path_vec.end();
+ it != end; ++it) {
+ const path& cur_path = *it;
+
+ if (cur_path.m_fill0 == style) {
+ paths.push_back(&cur_path);
}
- else
- {
- hardware_resample(4, im->width(),
im->height(), im->data(), w, h);
+
+ if (cur_path.m_fill1 == style) {
+ paths.push_back(&cur_path);
}
-#elif (RESAMPLE_METHOD == 2)
- {
- // Faster/simpler software bilinear rescale.
- software_resample(4, im->width(), im->height(),
im->pitch(), im->data(), w, h);
+
}
-#else
+ return paths;
+ }
+
+
+ std::vector<PathVec::const_iterator>
+ find_subshapes(const PathVec& path_vec)
{
- // Fancy but slow software resampling.
- image::rgba* rescaled = image::create_rgba(w, h);
- image::resample(rescaled, 0, 0, w - 1, h - 1,
- im, 0, 0, (float) im->m_width, (float)
im->m_height);
+ std::vector<PathVec::const_iterator> subshapes;
+
+ PathVec::const_iterator it = path_vec.begin(),
+ end = path_vec.end();
+
+ subshapes.push_back(it);
+ ++it;
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0,
GL_RGBA, GL_UNSIGNED_BYTE, rescaled->m_data);
+ for (;it != end; ++it) {
+ const path& cur_path = *it;
- delete rescaled;
+ if (cur_path.m_new_shape) {
+ subshapes.push_back(it);
}
-#endif
}
- else
- {
- // Use original image directly.
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h,
0, GL_RGBA, GL_UNSIGNED_BYTE, im->data());
+
+ if (subshapes.back() != end) {
+ subshapes.push_back(end);
}
- break;
+ return subshapes;
}
- case image::image_base::ALPHA:
+
+
+ void
+ draw_subshape(const PathVec& path_vec,
+ const matrix& mat,
+ const cxform& cx,
+ float pixel_scale,
+ const std::vector<fill_style>& fill_styles,
+ const std::vector<line_style>& line_styles)
{
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
+ PathVec normalized = normalize_paths(path_vec);
+ PathPointMap pathpoints = getPathPoints(normalized);
+
+
+ for (size_t i = 0; i < fill_styles.size(); ++i) {
+ PathPtrVec paths = get_paths_by_style(normalized, i+1);
-#ifndef NDEBUG
- // You must use power-of-two dimensions!!
- size_t w = 1; while (w < im->width()) { w <<= 1; }
- size_t h = 1; while (h < im->height()) { h <<= 1; }
- assert(w == im->width());
- assert(h == im->height());
-#endif // not NDEBUG
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA,
- im->width(), im->height(), 0, GL_ALPHA,
GL_UNSIGNED_BYTE, im->data());
-
- // Build mips.
- int level = 1;
- while (im->width() > 1 || im->height() > 1)
- {
- im->make_next_miplevel();
- glTexImage2D(GL_TEXTURE_2D, level, GL_ALPHA,
- im->width(), im->height(), 0, GL_ALPHA,
GL_UNSIGNED_BYTE, im->data());
- level++;
+ if (!paths.size()) {
+ continue;
}
- break;
+ std::list<PathPtrVec> contours = get_contours(paths);
+
+ _tesselator.beginPolygon();
+
+ for (std::list<PathPtrVec>::const_iterator iter = contours.begin(),
+ final = contours.end(); iter != final; ++iter) {
+ const PathPtrVec& refs = *iter;
+
+ _tesselator.beginContour();
+
+ for (PathPtrVec::const_iterator it = refs.begin(), end = refs.end();
+ it != end; ++it) {
+ const path& cur_path = *(*it);
+
+ assert(pathpoints.find(&cur_path) != pathpoints.end());
+
+ _tesselator.feed(pathpoints[&cur_path]);
+
}
- default:
-// log_error("Unsupported image type");
- break;
+ _tesselator.endContour();
}
-}
+ glNewList(1, GL_COMPILE);
-inline bool opengl_accessible()
-{
-#if defined(_WIN32) || defined(WIN32)
- return wglGetCurrentContext() != 0;
-#elif defined(__APPLE_CC__)
- return aglGetCurrentContext() != 0;
-#else
- return glXGetCurrentContext() != 0;
-#endif
-}
+ _tesselator.tesselate();
-bitmap_info_ogl::bitmap_info_ogl(int width, int height, uint8_t* data)
-// Initialize this bitmap_info to an alpha image
-// containing the specified data (1 byte per texel).
-//
-// !! Munges *data in order to create mipmaps !!
-{
-// GNASH_REPORT_FUNCTION;
- assert(width > 0);
- assert(height > 0);
- assert(data);
-
- // TODO optimization
- //image::image_base* im = new image::image_base(data, width, height, 1,
image::image_base::ALPHA);
- //memcpy(im->data(), data, width * height);
- std::auto_ptr<image::image_base> im ( image::create_alpha(width,
height) );
- im->update(data);
+ glEndList();
- if (opengl_accessible() == false)
- {
- m_suspended_image = im;
- return;
+ apply_fill_style(fill_styles[i], mat, cx);
+
+
+ if (fill_styles[i].get_type() != SWF::FILL_SOLID) {
+ // Apply alpha premultiplication.
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
- layout_image(im.get());
-}
+ glCallList(1);
-bitmap_info_ogl::bitmap_info_ogl(image::rgb* im)
-// NOTE: This function destroys im's data in the process of making mipmaps.
-{
-// GNASH_REPORT_FUNCTION;
- assert(im);
+ if (fill_styles[i].get_type() != SWF::FILL_SOLID) {
+ // restore to original.
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
- if (opengl_accessible() == false)
- {
- m_suspended_image = im->clone();
- return;
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_TEXTURE_2D);
+
+ // FIXME: free up the display list, or not?
}
- layout_image(im);
-}
+ draw_outlines(normalized, pathpoints, mat, cx, fill_styles, line_styles);
+ }
-bitmap_info_ogl::bitmap_info_ogl(image::rgba* im)
-// Version of the constructor that takes an image with alpha.
-// NOTE: This function destroys im's data in the process of making mipmaps.
-{
-// GNASH_REPORT_FUNCTION;
- assert(im);
+// Drawing procedure:
+// 1. Separate paths by subshape.
+// 2. Separate subshapes by fill style.
+// 3. For every subshape/fill style combo:
+// a. Separate contours: find closed shapes by connecting ends.
+// b. Apply fill style.
+// c. Feed the contours in the tesselator. (Render.)
+// d. Draw outlines for every path in the subshape with a line style.
+//
+// 4. ...
+// 5. Profit!
- if (opengl_accessible() == false)
+ virtual void
+ draw_shape_character(shape_character_def *def,
+ const matrix& mat,
+ const cxform& cx,
+ float pixel_scale,
+ const std::vector<fill_style>& fill_styles,
+ const std::vector<line_style>& line_styles)
{
- m_suspended_image = im->clone();
+
+ const PathVec& path_vec = def->get_paths();
+
+ if (!path_vec.size()) {
+ // No paths. Nothing to draw...
return;
}
- layout_image(im);
-}
+ bool have_shape, have_outline;
-gnash::render_handler* gnash::create_render_handler_ogl()
-// Factory.
-{
-// GNASH_REPORT_FUNCTION;
+ analyze_paths(path_vec, have_shape, have_outline);
- // Do some initialisation.
-#define OVERSIZE 1.0f
+ if (!have_shape && !have_outline) {
+ return; // invisible character
+ }
- //This makes fonts look nice (actually!)
-#if 0
- glEnable(GL_POLYGON_SMOOTH);
- glEnable(GL_POINT_SMOOTH);
-#endif
- // Turn on alpha blending.
- glEnable(GL_BLEND);
- // This blend operation is best for rendering antialiased points and
lines in
- // no particular order.
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glPushMatrix();
+ apply_matrix(mat);
- // Turn on line smoothing. Antialiased lines can be used to
- // smooth the outsides of shapes.
- glEnable(GL_LINE_SMOOTH);
- glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // GL_NICEST,
GL_FASTEST, GL_DONT_CARE
+ std::vector<PathVec::const_iterator> subshapes = find_subshapes(path_vec);
-#if 0
- // Enabling polygon smoothing has a side effect of filled "surfaces"
gain some
- // transparency. So let's disable this for now.
- glEnable (GL_POLYGON_SMOOTH);
-#endif
+ for (size_t i = 0; i < subshapes.size()-1; ++i) {
+ PathVec subshape_paths;
- glMatrixMode(GL_PROJECTION);
- // Flip the image
- glOrtho(-OVERSIZE, OVERSIZE, OVERSIZE, -OVERSIZE, -1, 1);
- // Restore the matrix mode to the default.
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
+ if (subshapes[i] != subshapes[i+1]) {
+ subshape_paths = PathVec(subshapes[i], subshapes[i+1]);
+ } else {
+ subshape_paths.push_back(*subshapes[i]);
+ }
- // glColorPointer(4, GL_UNSIGNED_BYTE, 0, *);
- // glInterleavedArrays(GL_T2F_N3F_V3F, 0, *)
- glPushAttrib (GL_ALL_ATTRIB_BITS);
+ draw_subshape(subshape_paths, mat, cx, pixel_scale, fill_styles,
+ line_styles);
+ }
+ glPopMatrix();
+ }
-#ifdef FIX_I810_LOD_BIAS
- // If 2D textures weren't previously enabled, enable
- // them now and force the driver to notice the update,
- // then disable them again.
- if (!glIsEnabled(GL_TEXTURE_2D)) {
- // Clearing a mask of zero *should* have no
- // side effects, but coupled with enbling
- // GL_TEXTURE_2D it works around a segmentation
- // fault in the driver for the Intel 810 chip.
- glEnable(GL_TEXTURE_2D);
- glClear(0);
- glDisable(GL_TEXTURE_2D);
+ virtual void draw_glyph(shape_character_def *def, const matrix& mat,
+ const rgba& c, float pixel_scale)
+ {
+ cxform dummy_cx;
+ std::vector<fill_style> glyph_fs;
+
+ fill_style coloring;
+ coloring.setSolid(c);
+
+ glyph_fs.push_back(coloring);
+
+ std::vector<line_style> dummy_ls;
+ draw_shape_character(def, mat, dummy_cx, pixel_scale, glyph_fs, dummy_ls);
}
-#endif
+ virtual bool allow_glyph_textures()
+ {
+ return false;
+ }
+
+ virtual void set_scale(float xscale, float yscale) {
+ _xscale = xscale;
+ _yscale = yscale;
+ }
+
+ virtual void get_scale(point& scale) {
+ scale.m_x = _xscale;
+ scale.m_y = _yscale;
+ }
+
+
+private:
+ Tesselator _tesselator;
+ float _xscale;
+ float _yscale;
+}; // class render_handler_ogl
+
+render_handler* create_render_handler_ogl()
+// Factory.
+{
return new render_handler_ogl;
}
-// Local Variables:
-// mode: C++
-// indent-tabs-mode: t
-// End:
+
+} // namespace gnash
+
+
+/*
+
+Markus: A. A. I still miss you and the easter 2006, you know.
+ A. J. I miss you too, but you'll probably not read this code ever... :/
+
+*/
+
Index: gui/gtk_glue_cairo.cpp
===================================================================
RCS file: /sources/gnash/gnash/gui/gtk_glue_cairo.cpp,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -b -r1.13 -r1.14
--- gui/gtk_glue_cairo.cpp 18 Oct 2007 11:23:22 -0000 1.13
+++ gui/gtk_glue_cairo.cpp 1 Nov 2007 08:04:09 -0000 1.14
@@ -86,7 +86,7 @@
// Create offscreen image for rendering
cairo_surface_t* surface = cairo_image_surface_create(
- CAIRO_FORMAT_RGB24, event->width, event->height);
+ CAIRO_FORMAT_ARGB32, event->width, event->height);
_cairo_offscreen = cairo_create(surface);
cairo_surface_destroy(surface);
Index: gui/gtk_glue_gtkglext.cpp
===================================================================
RCS file: /sources/gnash/gnash/gui/gtk_glue_gtkglext.cpp,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -b -r1.12 -r1.13
--- gui/gtk_glue_gtkglext.cpp 1 Jul 2007 10:54:02 -0000 1.12
+++ gui/gtk_glue_gtkglext.cpp 1 Nov 2007 08:04:09 -0000 1.13
@@ -17,7 +17,7 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
-/* $Id: gtk_glue_gtkglext.cpp,v 1.12 2007/07/01 10:54:02 bjacques Exp $ */
+/* $Id: gtk_glue_gtkglext.cpp,v 1.13 2007/11/01 08:04:09 bjacques Exp $ */
#include "gtk_glue_gtkglext.h"
#include "log.h"
@@ -69,7 +69,7 @@
(int)major, (int)minor);
GdkGLConfigMode glcmode = (GdkGLConfigMode)(GDK_GL_MODE_RGB |
- GDK_GL_MODE_DEPTH |
+ GDK_GL_MODE_STENCIL |
GDK_GL_MODE_DOUBLE);
_glconfig = gdk_gl_config_new_by_mode (glcmode);
Index: backend/render_handler_ogl.h
===================================================================
RCS file: backend/render_handler_ogl.h
diff -N backend/render_handler_ogl.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ backend/render_handler_ogl.h 1 Nov 2007 08:04:09 -0000 1.1
@@ -0,0 +1,145 @@
+//
+// Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+#ifndef __RENDER_HANDLER_OGL_H__
+#define __RENDER_HANDLER_OGL_H__
+
+
+#if defined(NOT_SGI_GL) || defined(__APPLE_CC__)
+#include <AGL/agl.h>
+#include <OpenGL/gl.h>
+#include <OpenGL/glu.h>
+#include <OpenGL/glext.h>
+#else
+# include <GL/gl.h>
+# ifdef WIN32
+# define GL_CLAMP_TO_EDGE 0x812F
+# else
+# include <GL/glx.h>
+# endif
+# include <GL/glu.h>
+# ifndef APIENTRY
+# define APIENTRY
+# endif
+#endif
+
+
+
+
+namespace gnash {
+
+
+
+
+typedef std::vector<const path*> PathRefs;
+
+
+
+struct oglVertex {
+ oglVertex(double x, double y, double z = 0.0)
+ : _x(x), _y(y), _z(z)
+ {
+ }
+
+ oglVertex(const point& p)
+ : _x(p.m_x), _y(p.m_y), _z(0.0)
+ {
+ }
+
+ GLdouble _x;
+ GLdouble _y;
+ GLdouble _z;
+};
+
+typedef std::map< const path*, std::vector<oglVertex> > PathPointMap;
+
+class Tesselator
+{
+public:
+ Tesselator();
+ ~Tesselator();
+
+ void beginPolygon();
+
+ void feed(std::vector<oglVertex>& vertices);
+
+ void tesselate();
+
+ void beginContour();
+ void endContour();
+
+ void rememberVertex(GLdouble* v);
+
+ static void
+ error(GLenum error);
+
+ static void combine(GLdouble coords [3], void *vertex_data[4],
+ GLfloat weight[4], void **outData, void* userdata);
+
+
+
+private:
+ std::vector<GLdouble*> _vertices;
+ GLUtesselator* _tessobj;
+};
+
+class WholeShape
+{
+public:
+ void newPath(const path& new_path)
+ {
+ PathRefs refs;
+ refs.push_back(&new_path);
+
+ shape.push_back(refs);
+ }
+
+ void addPath(const path& add_path)
+ {
+ PathRefs& refs = shape.back();
+ refs.push_back(&add_path);
+ }
+
+ void addPathRefs(const PathRefs& pathrefs)
+ {
+
+ PathRefs new_refs(pathrefs.begin(), pathrefs.end());
+
+ shape.push_back(new_refs);
+ }
+
+
+ const std::vector<PathRefs>& get() const
+ {
+ return shape;
+ }
+
+private:
+ std::vector<PathRefs> shape;
+
+};
+
+
+
+
+
+
+} // namespace gnash
+
+
+#endif // __RENDER_HANDLER_OGL_H__
+
- [Gnash-commit] gnash ChangeLog backend/render_handler_ogl.cpp ...,
Bastiaan Jacques <=