summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--TODO25
-rw-r--r--cogl.h290
-rw-r--r--cogl.h.in874
-rw-r--r--common/Makefile.am23
-rw-r--r--common/cogl-bitmap-fallback.c369
-rw-r--r--common/cogl-bitmap-pixbuf.c197
-rw-r--r--common/cogl-bitmap.c150
-rw-r--r--common/cogl-bitmap.h95
-rw-r--r--common/cogl-util.c51
-rw-r--r--common/cogl-util.h32
-rw-r--r--common/stb_image.c3772
-rw-r--r--doc/reference/cogl/Makefile.am93
-rw-r--r--doc/reference/cogl/cogl-docs.sgml57
-rw-r--r--doc/reference/cogl/cogl-sections.txt148
-rw-r--r--gl/Makefile.am37
-rw-r--r--gl/cogl-context.c114
-rw-r--r--gl/cogl-context.h95
-rw-r--r--gl/cogl-fbo.c390
-rw-r--r--gl/cogl-fbo.h38
-rw-r--r--gl/cogl-internal.h61
-rw-r--r--gl/cogl-primitives.c1092
-rw-r--r--gl/cogl-primitives.h61
-rw-r--r--gl/cogl-texture.c2303
-rw-r--r--gl/cogl-texture.h64
-rw-r--r--gl/cogl.c810
-rw-r--r--gles/Makefile.am37
-rw-r--r--gles/cogl-context.c96
-rw-r--r--gles/cogl-context.h78
-rw-r--r--gles/cogl-defines.h.in (renamed from gles/cogl-defines.h)12
-rw-r--r--gles/cogl-fbo.c188
-rw-r--r--gles/cogl-fbo.h38
-rw-r--r--gles/cogl-internal.h63
-rw-r--r--gles/cogl-primitives.c1112
-rw-r--r--gles/cogl-primitives.h54
-rw-r--r--gles/cogl-texture.c2185
-rw-r--r--gles/cogl-texture.h63
-rw-r--r--gles/cogl-util.c51
-rw-r--r--gles/cogl-util.h32
-rw-r--r--gles/cogl.c457
40 files changed, 14671 insertions, 1038 deletions
diff --git a/Makefile.am b/Makefile.am
index f09f6372..9bb6b772 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = $(CLUTTER_COGL)
+SUBDIRS = common $(CLUTTER_COGL)
EXTRA_DIST = cogl.h
diff --git a/TODO b/TODO
new file mode 100644
index 00000000..e1e5d316
--- /dev/null
+++ b/TODO
@@ -0,0 +1,25 @@
+============================
+Cogl overhaul related tasks:
+============================
+
+MISC
+
+- implemenent a 1 to 1 mapping of COGL_FEATURE flags
+ into CLUTTER_FEATURE flags before combining them
+ into final clutter flags value (clutter-feature.c)
+
+TEXTURE
+
+- cogl_texture_get_data, cogl_texture_set_region in GLES
+
+- YUV texture format support (multitexturing + shader)
+
+FBO
+
+- add stencil, depth and other renderbuffers to fbos
+
+- cogl_offscreen_new_multisample
+
+- test cogl_offscreen_blit
+
+- add "filter" argument to cogl_offscreen_blit
diff --git a/cogl.h b/cogl.h
deleted file mode 100644
index beb2955d..00000000
--- a/cogl.h
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Clutter COGL
- *
- * A basic GL/GLES Abstraction/Utility Layer
- *
- * Authored By Matthew Allum <mallum@openedhand.com>
- *
- * Copyright (C) 2007 OpenedHand
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * COGL
- * ====
- *
- * 'cogl' is a very simple abstraction layer which wraps GL and GLES.
- *
- *
- * !!!! DO NOT USE THIS API YET OUTSIDE OF CLUTTER CORE !!!!
- * THE API WILL FLUCTUATE WILDLY
- *
- * TODO:
- * - Use ClutterReal for fixed/float params.
- * - Add Perspective/viewport setup
- * - Add Features..
- */
-
-#ifndef __COGL_H__
-#define __COGL_H__
-
-#include <glib.h>
-#include <clutter/clutter-color.h>
-#include <clutter/clutter-feature.h>
-#include <clutter/clutter-fixed.h>
-#include <clutter/clutter-types.h>
-
-#include "cogl-defines.h"
-
-G_BEGIN_DECLS
-
-#define CGL_ENABLE_BLEND (1<<1)
-#define CGL_ENABLE_TEXTURE_2D (1<<2)
-#define CGL_ENABLE_ALPHA_TEST (1<<3)
-#define CGL_ENABLE_TEXTURE_RECT (1<<4)
-
-typedef void (*CoglFuncPtr) (void);
-
-CoglFuncPtr
-cogl_get_proc_address (const gchar* name);
-
-gboolean
-cogl_check_extension (const gchar *name, const gchar *ext);
-
-void
-cogl_perspective (ClutterFixed fovy,
- ClutterFixed aspect,
- ClutterFixed zNear,
- ClutterFixed zFar);
-
-void
-cogl_setup_viewport (guint width,
- guint height,
- ClutterFixed fovy,
- ClutterFixed aspect,
- ClutterFixed z_near,
- ClutterFixed z_far);
-
-void
-cogl_paint_init (const ClutterColor *color);
-
-void
-cogl_push_matrix (void);
-
-void
-cogl_pop_matrix (void);
-
-void
-cogl_scale (ClutterFixed x, ClutterFixed z);
-
-void
-cogl_translatex (ClutterFixed x, ClutterFixed y, ClutterFixed z);
-
-void
-cogl_translate (gint x, gint y, gint z);
-
-void
-cogl_rotatex (ClutterFixed angle, gint x, gint y, gint z);
-
-void
-cogl_rotate (gint angle, gint x, gint y, gint z);
-
-void
-cogl_color (const ClutterColor *color);
-
-void
-cogl_clip_set (ClutterFixed x_offset,
- ClutterFixed y_offset,
- ClutterFixed width,
- ClutterFixed height);
-
-void
-cogl_clip_unset (void);
-
-void
-cogl_enable (gulong flags);
-
-void
-cogl_enable_depth_test (gboolean setting);
-
-gboolean
-cogl_texture_can_size (COGLenum target,
- COGLenum pixel_format,
- COGLenum pixel_type,
- int width,
- int height);
-
-void
-cogl_texture_quad (gint x1,
- gint x2,
- gint y1,
- gint y2,
- ClutterFixed tx1,
- ClutterFixed ty1,
- ClutterFixed tx2,
- ClutterFixed ty2);
-
-void
-cogl_textures_create (guint num, COGLuint *textures);
-
-void
-cogl_textures_destroy (guint num, const COGLuint *textures);
-
-void
-cogl_texture_bind (COGLenum target, COGLuint texture);
-
-void
-cogl_texture_set_alignment (COGLenum target,
- guint alignment,
- guint row_length);
-
-void
-cogl_texture_set_filters (COGLenum target,
- COGLenum min_filter,
- COGLenum max_filter);
-
-void
-cogl_texture_set_wrap (COGLenum target,
- COGLenum wrap_s,
- COGLenum wrap_t);
-
-void
-cogl_texture_image_2d (COGLenum target,
- COGLint internal_format,
- gint width,
- gint height,
- COGLenum format,
- COGLenum type,
- const guchar* pixels);
-
-void
-cogl_texture_sub_image_2d (COGLenum target,
- gint xoff,
- gint yoff,
- gint width,
- gint height,
- COGLenum format,
- COGLenum type,
- const guchar* pixels);
-
-void
-cogl_rectangle (gint x, gint y, guint width, guint height);
-
-void
-cogl_trapezoid (gint y1,
- gint x11,
- gint x21,
- gint y2,
- gint x12,
- gint x22);
-void
-cogl_alpha_func (COGLenum func,
- ClutterFixed ref);
-
-ClutterFeatureFlags
-cogl_get_features ();
-
-void
-cogl_get_modelview_matrix (ClutterFixed m[16]);
-
-void
-cogl_get_projection_matrix (ClutterFixed m[16]);
-
-void
-cogl_get_viewport (ClutterFixed v[4]);
-
-void
-cogl_get_bitmasks (gint *red, gint *green, gint *blue, gint *alpha);
-
-void
-cogl_fog_set (const ClutterColor *fog_color,
- ClutterFixed density,
- ClutterFixed z_near,
- ClutterFixed z_far);
-
-
-COGLhandle
-cogl_create_shader (COGLenum shaderType);
-
-void
-cogl_shader_destroy (COGLhandle handle);
-
-
-void
-cogl_shader_source (COGLhandle shader,
- const gchar *source);
-void
-cogl_shader_compile (COGLhandle shader_handle);
-
-void
-cogl_shader_get_info_log (COGLhandle handle,
- guint size,
- gchar *buffer);
-
-void
-cogl_shader_get_parameteriv (COGLhandle handle,
- COGLenum pname,
- COGLint *dest);
-
-
-COGLhandle
-cogl_create_program (void);
-
-void
-cogl_program_destroy (COGLhandle handle);
-
-void
-cogl_program_attach_shader (COGLhandle program_handle,
- COGLhandle shader_handle);
-
-/* 0 to use none */
-void
-cogl_program_link (COGLhandle program_handle);
-
-void
-cogl_program_use (COGLhandle program_handle);
-
-COGLint
-cogl_program_get_uniform_location (COGLhandle program_int,
- const gchar *uniform_name);
-
-
-void
-cogl_program_uniform_1f (COGLint uniform_no,
- gfloat value);
-
-/* Offscreen - FBO support */
-
-COGLuint
-cogl_offscreen_create (COGLuint target_texture);
-
-void
-cogl_offscreen_destroy (COGLuint offscreen_handle);
-
-void
-cogl_offscreen_redirect_start (COGLuint offscreen_handle,
- gint width,
- gint height);
-
-void
-cogl_offscreen_redirect_end (COGLuint offscreen_handle,
- gint width,
- gint height);
-
-G_END_DECLS
-
-#endif /* __COGL_H__ */
diff --git a/cogl.h.in b/cogl.h.in
new file mode 100644
index 00000000..47b6b58f
--- /dev/null
+++ b/cogl.h.in
@@ -0,0 +1,874 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * COGL
+ * ====
+ *
+ * 'cogl' is a very simple abstraction layer which wraps GL and GLES.
+ *
+ *
+ * !!!! DO NOT USE THIS API YET OUTSIDE OF CLUTTER CORE !!!!
+ * THE API WILL FLUCTUATE WILDLY
+ *
+ * TODO:
+ * - Use ClutterReal for fixed/float params.
+ * - Add Perspective/viewport setup
+ * - Add Features..
+ */
+
+#ifndef __COGL_H__
+#define __COGL_H__
+
+#include <glib.h>
+#include <clutter/clutter-color.h>
+#include <clutter/clutter-feature.h>
+#include <clutter/clutter-fixed.h>
+#include <clutter/clutter-types.h>
+
+#include <cogl/cogl-defines-@CLUTTER_COGL@.h>
+
+G_BEGIN_DECLS
+
+/* Enum declarations */
+
+#define COGL_PIXEL_FORMAT_24 2
+#define COGL_PIXEL_FORMAT_32 3
+#define COGL_A_BIT (1 << 4)
+#define COGL_BGR_BIT (1 << 5)
+#define COGL_AFIRST_BIT (1 << 6)
+#define COGL_PREMULT_BIT (1 << 7)
+#define COGL_UNORDERED_MASK 0x0F
+#define COGL_UNPREMULT_MASK 0x7F
+
+/**
+ * CoglPixelFormat:
+ * @COGL_PIXEL_FORMAT_ANY:
+ * @COGL_PIXEL_FORMAT_A_8:
+ * @COGL_PIXEL_FORMAT_RGB_888:
+ * @COGL_PIXEL_FORMAT_BGR_888:
+ * @COGL_PIXEL_FORMAT_RGBA_8888:
+ * @COGL_PIXEL_FORMAT_BGRA_8888:
+ * @COGL_PIXEL_FORMAT_ARGB_8888:
+ * @COGL_PIXEL_FORMAT_ABGR_8888:
+ * @COGL_PIXEL_FORMAT_RGBA_8888_PRE:
+ * @COGL_PIXEL_FORMAT_BGRA_8888_PRE:
+ * @COGL_PIXEL_FORMAT_ARGB_8888_PRE:
+ * @COGL_PIXEL_FORMAT_ABGR_8888_PRE:
+ * @COGL_PIXEL_FORMAT_RGB_565:
+ * @COGL_PIXEL_FORMAT_RGBA_4444:
+ * @COGL_PIXEL_FORMAT_RGBA_5551:
+ * @COGL_PIXEL_FORMAT_RGBA_4444_PRE:
+ * @COGL_PIXEL_FORMAT_RGBA_5551_PRE:
+ * @COGL_PIXEL_FORMAT_YUV:
+ * @COGL_PIXEL_FORMAT_G_8:
+ *
+ * Pixel formats used by COGL.
+ */
+typedef enum
+{
+ COGL_PIXEL_FORMAT_ANY = 0,
+ COGL_PIXEL_FORMAT_A_8 = 1 | COGL_A_BIT,
+
+ COGL_PIXEL_FORMAT_RGB_888 = COGL_PIXEL_FORMAT_24,
+ COGL_PIXEL_FORMAT_BGR_888 = (COGL_PIXEL_FORMAT_24 | COGL_BGR_BIT),
+
+ COGL_PIXEL_FORMAT_RGBA_8888 = COGL_PIXEL_FORMAT_32 | COGL_A_BIT,
+ COGL_PIXEL_FORMAT_BGRA_8888 = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_BGR_BIT),
+ COGL_PIXEL_FORMAT_ARGB_8888 = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_AFIRST_BIT),
+ COGL_PIXEL_FORMAT_ABGR_8888 = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT),
+
+ COGL_PIXEL_FORMAT_RGBA_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT),
+ COGL_PIXEL_FORMAT_BGRA_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_BGR_BIT),
+ COGL_PIXEL_FORMAT_ARGB_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_AFIRST_BIT),
+ COGL_PIXEL_FORMAT_ABGR_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT),
+
+ COGL_PIXEL_FORMAT_RGB_565 = 4,
+ COGL_PIXEL_FORMAT_RGBA_4444 = 5 | COGL_A_BIT,
+ COGL_PIXEL_FORMAT_RGBA_5551 = 6 | COGL_A_BIT,
+
+ COGL_PIXEL_FORMAT_RGBA_4444_PRE = (COGL_PIXEL_FORMAT_RGBA_4444 | COGL_A_BIT | COGL_PREMULT_BIT),
+ COGL_PIXEL_FORMAT_RGBA_5551_PRE = (COGL_PIXEL_FORMAT_RGBA_5551 | COGL_A_BIT | COGL_PREMULT_BIT),
+
+ COGL_PIXEL_FORMAT_YUV = 7,
+
+ COGL_PIXEL_FORMAT_G_8 = 8
+
+} CoglPixelFormat;
+
+/**
+ * CoglFeatureFlags:
+ * @COGL_FEATURE_TEXTURE_RECTANGLE:
+ * @COGL_FEATURE_TEXTURE_NPOT:
+ * @COGL_FEATURE_TEXTURE_YUV:
+ * @COGL_FEATURE_TEXTURE_READ_PIXELS:
+ * @COGL_FEATURE_SHADERS_GLSL:
+ * @COGL_FEATURE_OFFSCREEN:
+ * @COGL_FEATURE_OFFSCREEN_MULTISAMPLE:
+ * @COGL_FEATURE_OFFSCREEN_BLIT:
+ * @COGL_FEATURE_FOUR_CLIP_PLANES:
+ * @COGL_FEATURE_STENCIL_BUFFER:
+ *
+ * Flags for the supported features.
+ */
+typedef enum
+{
+ COGL_FEATURE_TEXTURE_RECTANGLE = (1 << 1),
+ COGL_FEATURE_TEXTURE_NPOT = (1 << 2),
+ COGL_FEATURE_TEXTURE_YUV = (1 << 3),
+ COGL_FEATURE_TEXTURE_READ_PIXELS = (1 << 4),
+ COGL_FEATURE_SHADERS_GLSL = (1 << 5),
+ COGL_FEATURE_OFFSCREEN = (1 << 6),
+ COGL_FEATURE_OFFSCREEN_MULTISAMPLE = (1 << 7),
+ COGL_FEATURE_OFFSCREEN_BLIT = (1 << 8),
+ COGL_FEATURE_FOUR_CLIP_PLANES = (1 << 9),
+ COGL_FEATURE_STENCIL_BUFFER = (1 << 10)
+
+} CoglFeatureFlags;
+
+/**
+ * CoglBufferTarget:
+ * @COGL_WINDOW_BUFFER:
+ * @COGL_MASK_BUFFER:
+ * @COGL_OFFSCREEN_BUFFER:
+ *
+ * FIXME
+ */
+typedef enum
+{
+ COGL_WINDOW_BUFFER = (1 << 1),
+ COGL_MASK_BUFFER = (1 << 2),
+ COGL_OFFSCREEN_BUFFER = (1 << 3)
+
+} CoglBufferTarget;
+
+/**
+ * CoglTextureVertex:
+ * @x: Model x-coordinate
+ * @y: Model y-coordinate
+ * @z: Model z-coordinate
+ * @tx: Texture x-coordinate
+ * @ty: Texture y-coordinate
+ * @color: The color to use at this vertex. This is ignored if
+ * @use_color is %FALSE when calling cogl_texture_polygon().
+ *
+ * Used to specify vertex information when calling cogl_texture_polygon().
+ */
+struct _CoglTextureVertex
+{
+ ClutterFixed x, y, z;
+ ClutterFixed tx, ty;
+ ClutterColor color;
+};
+
+typedef struct _CoglTextureVertex CoglTextureVertex;
+
+/* Context manipulation */
+
+gboolean
+cogl_create_context (void);
+
+void
+cogl_destroy_context (void);
+
+/* Misc */
+#define COGL_INVALID_HANDLE NULL
+
+typedef gpointer CoglHandle;
+
+typedef void (* CoglFuncPtr) (void);
+
+/**
+ * cogl_get_features:
+ *
+ * Returns all of the features supported by COGL.
+ *
+ * Return value: A logical OR of all the supported COGL features.
+ *
+ * Since: 0.8
+ */
+ClutterFeatureFlags
+cogl_get_features (void);
+
+/**
+ * cogl_features_available:
+ * @features: A bitmask of features to check for
+ *
+ * Checks whether the given COGL features are available. Multiple
+ * features can be checked for by or-ing them together with the '|'
+ * operator. %TRUE is only returned if all of the requested features
+ * are available.
+ *
+ * Return value: %TRUE if the features are available, %FALSE otherwise.
+ */
+gboolean
+cogl_features_available (CoglFeatureFlags features);
+
+/**
+ * cogl_get_proc_address:
+ * @name: the name of the function.
+ *
+ * Gets a pointer to a given GL or GL ES extension function. This acts
+ * as a wrapper around glXGetProcAddress() or whatever is the
+ * appropriate function for the current backend.
+ *
+ * Return value: a pointer to the requested function or %NULL if the
+ * function is not available.
+ */
+CoglFuncPtr
+cogl_get_proc_address (const gchar* name);
+
+gboolean
+cogl_check_extension (const gchar *name,
+ const gchar *ext);
+
+/**
+ * cogl_get_bitmasks:
+ * @red: Return location for the number of red bits or %NULL
+ * @green: Return location for the number of green bits or %NULL
+ * @blue: Return location for the number of blue bits or %NULL
+ * @alpha: Return location for the number of alpha bits or %NULL
+ *
+ * Gets the number of bitplanes used for each of the color components
+ * in the color buffer. Pass %NULL for any of the arguments if the
+ * value is not required.
+ */
+void
+cogl_get_bitmasks (gint *red,
+ gint *green,
+ gint *blue,
+ gint *alpha);
+
+void
+cogl_perspective (ClutterFixed fovy,
+ ClutterFixed aspect,
+ ClutterFixed zNear,
+ ClutterFixed zFar);
+
+/**
+ * cogl_setup_viewport:
+ * @width: Width of the viewport
+ * @height: Height of the viewport
+ * @fovy: Field of view angle in degrees
+ * @aspect: Aspect ratio to determine the field of view along the x-axis
+ * @z_near: Nearest visible point along the z-axis
+ * @z_far: Furthest visible point along the z-axis
+ *
+ * Replaces the current viewport and projection matrix with the given
+ * values. The viewport is placed at the top left corner of the window
+ * with the given width and height. The projection matrix is replaced
+ * with one that has a viewing angle of @fovy along the y-axis and a
+ * view scaled according to @aspect along the x-axis. The view is
+ * clipped according to @z_near and @z_far on the z-axis.
+ */
+void
+cogl_setup_viewport (guint width,
+ guint height,
+ ClutterFixed fovy,
+ ClutterFixed aspect,
+ ClutterFixed z_near,
+ ClutterFixed z_far);
+
+/**
+ * cogl_push_matrix:
+ *
+ * Store the current model-view matrix on the matrix stack. The matrix
+ * can later be restored with cogl_pop_matrix().
+ */
+void
+cogl_push_matrix (void);
+
+/**
+ * cogl_pop_matrix:
+ *
+ * Restore the current model-view matrix from the matrix stack.
+ */
+void
+cogl_pop_matrix (void);
+
+/**
+ * cogl_scale:
+ * @x: Amount to scale along the x-axis
+ * @y: Amount to scale along the y-axis
+ *
+ * Multiplies the current model-view matrix by one that scales the x
+ * and y axes by the given values.
+ */
+void
+cogl_scale (ClutterFixed x, ClutterFixed y);
+
+/**
+ * cogl_translatex:
+ * @x: Distance to translate along the x-axis
+ * @y: Distance to translate along the y-axis
+ * @z: Distance to translate along the z-axis
+ *
+ * Multiplies the current model-view matrix by one that translates the
+ * model along all three axes according to the given values.
+ */
+void
+cogl_translatex (ClutterFixed x, ClutterFixed y, ClutterFixed z);
+
+/**
+ * cogl_translate:
+ * @x: Distance to translate along the x-axis
+ * @y: Distance to translate along the y-axis
+ * @z: Distance to translate along the z-axis
+ *
+ * Integer version of cogl_translatex(). Multiplies the current
+ * model-view matrix by one that translates the model along all three
+ * axes according to the given values.
+ */
+void
+cogl_translate (gint x, gint y, gint z);
+
+/**
+ * cogl_rotatex:
+ * @angle: Angle in degrees to rotate.
+ * @x: X-component of vertex to rotate around.
+ * @y: Y-component of vertex to rotate around.
+ * @z: Z-component of vertex to rotate around.
+ *
+ * Multiplies the current model-view matrix by one that rotates the
+ * model around the vertex specified by @x, @y and @z. The rotation
+ * follows the right-hand thumb rule so for example rotating by 10
+ * degrees about the vertex (0, 0, 1) causes a small counter-clockwise
+ * rotation.
+ */
+void
+cogl_rotatex (ClutterFixed angle, gint x, gint y, gint z);
+
+/**
+ * cogl_rotate:
+ * @angle: Angle in degrees to rotate.
+ * @x: X-component of vertex to rotate around.
+ * @y: Y-component of vertex to rotate around.
+ * @z: Z-component of vertex to rotate around.
+ *
+ * Integer version of cogl_rotatex(). Multiplies the current
+ * model-view matrix by one that rotates the model around the vertex
+ * specified by @x, @y and @z.
+ */
+void
+cogl_rotate (gint angle, gint x, gint y, gint z);
+
+/**
+ * cogl_get_modelview_matrix:
+ * @m: pointer to a 4x4 array of #ClutterFixed<!-- -->s to receive the matrix
+ *
+ * Stores the current model-view matrix in @m. The matrix is in
+ * column-major order.
+ */
+void
+cogl_get_modelview_matrix (ClutterFixed m[16]);
+
+/**
+ * cogl_get_projection_matrix:
+ * @m: pointer to a 4x4 array of #ClutterFixed<!-- -->s to receive the matrix
+ *
+ * Stores the current projection matrix in @m. The matrix is in
+ * column-major order.
+ */
+void
+cogl_get_projection_matrix (ClutterFixed m[16]);
+
+/**
+ * cogl_get_viewport:
+ * @v: pointer to a 4 element array of #ClutterFixed<!-- -->s to
+ * receive the viewport dimensions.
+ *
+ * Stores the current viewport in @v. @v[0] and @v[1] get the x and y
+ * position of the viewport and @v[2] and @v[3] get the width and
+ * height.
+ */
+void
+cogl_get_viewport (ClutterFixed v[4]);
+
+/**
+ * cogl_clip_set:
+ * @x_offset: left edge of the clip rectangle
+ * @y_offset: top edge of the clip rectangle
+ * @width: width of the clip rectangle
+ * @height: height of the clip rectangle
+ *
+ * Specifies a rectangular clipping area for all subsequent drawing
+ * operations. Any drawing commands that extend outside the rectangle
+ * will be clipped so that only the portion inside the rectangle will
+ * be displayed. The rectangle dimensions are transformed by the
+ * current model-view matrix.
+ */
+void
+cogl_clip_set (ClutterFixed x_offset,
+ ClutterFixed y_offset,
+ ClutterFixed width,
+ ClutterFixed height);
+
+/**
+ * cogl_clip_unset:
+ *
+ * Removes the current clipping rectangle so that all drawing
+ * operations extend to full size of the viewport again.
+ */
+void
+cogl_clip_unset (void);
+
+/**
+ * cogl_enable_depth_test:
+ * @setting: %TRUE to enable depth testing or %FALSE to disable.
+ *
+ * Sets whether depth testing is enabled. If it is disabled then the
+ * order that actors are layered on the screen depends solely on the
+ * order specified using clutter_actor_raise() and
+ * clutter_actor_lower(), otherwise it will also take into account the
+ * actor's depth. Depth testing is disabled by default.
+ */
+void
+cogl_enable_depth_test (gboolean setting);
+
+void
+cogl_alpha_func (COGLenum func,
+ ClutterFixed ref);
+
+/**
+ * cogl_fog_set:
+ * @fog_color: The color of the fog
+ * @density: Ignored
+ * @z_near: Position along z-axis where no fogging should be applied
+ * @z_far: Position along z-axes where full fogging should be applied
+ *
+ * Enables fogging. Fogging causes vertices that are further away from
+ * the eye to be rendered with a different color. The color is
+ * linearly interpolated so that vertices at @z_near are drawn fully
+ * with their original color and vertices at @z_far are drawn fully
+ * with @fog_color. Fogging will remain enabled until the next call to
+ * cogl_paint_init().
+ */
+void
+cogl_fog_set (const ClutterColor *fog_color,
+ ClutterFixed density,
+ ClutterFixed z_near,
+ ClutterFixed z_far);
+
+/**
+ * cogl_paint_init:
+ * @color: Background color to clear to
+ *
+ * Clears the color buffer to @color. The depth buffer and stencil
+ * buffers are also cleared and fogging and lighting are disabled.
+ */
+void
+cogl_paint_init (const ClutterColor *color);
+
+/* Textures api */
+
+CoglHandle
+cogl_texture_new_with_size (guint width,
+ guint height,
+ gint max_waste,
+ CoglPixelFormat internal_format);
+
+CoglHandle
+cogl_texture_new_from_file (const gchar *filename,
+ gint max_waste,
+ CoglPixelFormat internal_format,
+ GError **error);
+
+CoglHandle
+cogl_texture_new_from_data (guint width,
+ guint height,
+ gint max_waste,
+ CoglPixelFormat format,
+ CoglPixelFormat internal_format,
+ guint rowstride,
+ const guchar *data);
+
+CoglHandle
+cogl_texture_new_from_foreign (GLuint gl_handle,
+ GLenum gl_target,
+ GLuint width,
+ GLuint height,
+ GLuint x_pot_waste,
+ GLuint y_pot_waste,
+ CoglPixelFormat format);
+
+/**
+ * cogl_is_texture:
+ * @handle: A CoglHandle
+ *
+ * Gets whether the given handle references an existing texture object.
+ *
+ * Return value: %TRUE if the handle references a texture,
+ * %FALSE otherwise
+ */
+gboolean
+cogl_is_texture (CoglHandle handle);
+
+guint
+cogl_texture_get_width (CoglHandle handle);
+
+guint
+cogl_texture_get_height (CoglHandle handle);
+
+CoglPixelFormat
+cogl_texture_get_format (CoglHandle handle);
+
+guint
+cogl_texture_get_rowstride (CoglHandle handle);
+
+gint
+cogl_texture_get_max_waste (CoglHandle handle);
+
+COGLenum
+cogl_texture_get_min_filter (CoglHandle handle);
+
+COGLenum
+cogl_texture_get_mag_filter (CoglHandle handle);
+
+gboolean
+cogl_texture_is_sliced (CoglHandle handle);
+
+gboolean
+cogl_texture_get_gl_texture (CoglHandle handle,
+ GLuint *out_gl_handle,
+ GLenum *out_gl_target);
+
+gint
+cogl_texture_get_data (CoglHandle handle,
+ CoglPixelFormat format,
+ guint rowstride,
+ guchar *data);
+
+void
+cogl_texture_set_filters (CoglHandle handle,
+ COGLenum min_filter,
+ COGLenum mag_filter);
+
+gboolean
+cogl_texture_set_region (CoglHandle handle,
+ gint src_x,
+ gint src_y,
+ gint dst_x,
+ gint dst_y,
+ guint dst_width,
+ guint dst_height,
+ gint width,
+ gint height,
+ CoglPixelFormat format,
+ guint rowstride,
+ const guchar *data);
+
+CoglHandle
+cogl_texture_ref (CoglHandle handle);
+
+void
+cogl_texture_unref (CoglHandle handle);
+
+void
+cogl_texture_rectangle (CoglHandle handle,
+ ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed tx1,
+ ClutterFixed ty1,
+ ClutterFixed tx2,
+ ClutterFixed ty2);
+
+/**
+ * cogl_texture_polygon:
+ * @handle: A CoglHandle for a texture
+ * @n_vertices: The length of the vertices array
+ * @vertices: An array of #CoglTextureVertex structs
+ * @use_color: %TRUE if the color member of #CoglTextureVertex should be used
+ *
+ * Draws a polygon from a texture with the given model and texture
+ * coordinates. This can be used to draw arbitrary shapes textured
+ * with a COGL texture. If @use_color is %TRUE then the current COGL
+ * color will be changed for each vertex using the value specified in
+ * the color member of #CoglTextureVertex. This can be used for
+ * example to make the texture fade out by setting the alpha value of
+ * the color.
+ *
+ * All of the texture coordinates must be in the range [0,1] and
+ * repeating the texture is not supported.
+ *
+ * Because of the way this function is implemented it will currently
+ * only work if either the texture is not sliced or the backend is not
+ * OpenGL ES and the minifying and magnifying functions are both set
+ * to CGL_NEAREST.
+ */
+void
+cogl_texture_polygon (CoglHandle handle,
+ guint n_vertices,
+ CoglTextureVertex *vertices,
+ gboolean use_color);
+
+/* Primitives API */
+
+void
+cogl_color (const ClutterColor *color);
+
+void
+cogl_fast_fill_rectangle (gint x,
+ gint y,
+ guint width,
+ guint height);
+
+void
+cogl_fast_fill_rectanglex (ClutterFixed x,
+ ClutterFixed y,
+ ClutterFixed width,
+ ClutterFixed height);
+
+void
+cogl_fast_fill_trapezoid (gint y1,
+ gint x11,
+ gint x21,
+ gint y2,
+ gint x12,
+ gint x22);
+
+void
+cogl_fast_fill_trapezoidx (ClutterFixed y1,
+ ClutterFixed x11,
+ ClutterFixed x21,
+ ClutterFixed y2,
+ ClutterFixed x12,
+ ClutterFixed x22);
+
+void
+cogl_fill ();
+
+void
+cogl_stroke ();
+
+void
+cogl_path_move_to (ClutterFixed x,
+ ClutterFixed y);
+
+void
+cogl_path_move_to_rel (ClutterFixed x,
+ ClutterFixed y);
+
+void
+cogl_path_line_to (ClutterFixed x,
+ ClutterFixed y);
+
+void
+cogl_path_line_to_rel (ClutterFixed x,
+ ClutterFixed y);
+
+void
+cogl_path_h_line_to (ClutterFixed x);
+
+void
+cogl_path_v_line_to (ClutterFixed y);
+
+void
+cogl_path_h_line_to_rel (ClutterFixed x);
+
+void
+cogl_path_v_line_to_rel (ClutterFixed y);
+
+void
+cogl_path_arc (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_1,
+ ClutterAngle angle_2,
+ ClutterAngle angle_step);
+
+void
+cogl_path_arc_rel (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_1,
+ ClutterAngle angle_2,
+ ClutterAngle angle_step);
+
+void
+cogl_path_bezier2_to (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2);
+
+void
+cogl_path_bezier2_to_rel (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2);
+
+void
+cogl_path_bezier3_to (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed x3,
+ ClutterFixed y3);
+
+void
+cogl_path_bezier3_to_rel (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed x3,
+ ClutterFixed y3);
+
+void
+cogl_path_close ();
+
+void
+cogl_line (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2);
+
+void
+cogl_polyline (ClutterFixed *coords,
+ gint num_points);
+
+void
+cogl_polygon (ClutterFixed *coords,
+ gint num_points);
+
+void
+cogl_rectangle (ClutterFixed x,
+ ClutterFixed y,
+ ClutterFixed width,
+ ClutterFixed height);
+
+void
+cogl_arc (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_1,
+ ClutterAngle angle_2,
+ ClutterAngle angle_step);
+
+void
+cogl_ellipse (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_step);
+
+void
+cogl_round_rectangle (ClutterFixed x,
+ ClutterFixed y,
+ ClutterFixed width,
+ ClutterFixed height,
+ ClutterFixed radius,
+ ClutterAngle arc_step);
+
+
+COGLhandle
+cogl_create_shader (COGLenum shaderType);
+
+void
+cogl_shader_destroy (COGLhandle handle);
+
+
+void
+cogl_shader_source (COGLhandle shader,
+ const gchar *source);
+void
+cogl_shader_compile (COGLhandle shader_handle);
+
+void
+cogl_shader_get_info_log (COGLhandle handle,
+ guint size,
+ gchar *buffer);
+
+void
+cogl_shader_get_parameteriv (COGLhandle handle,
+ COGLenum pname,
+ COGLint *dest);
+
+
+COGLhandle
+cogl_create_program (void);
+
+void
+cogl_program_destroy (COGLhandle handle);
+
+void
+cogl_program_attach_shader (COGLhandle program_handle,
+ COGLhandle shader_handle);
+
+/* 0 to use none */
+void
+cogl_program_link (COGLhandle program_handle);
+
+void
+cogl_program_use (COGLhandle program_handle);
+
+COGLint
+cogl_program_get_uniform_location (COGLhandle program_int,
+ const gchar *uniform_name);
+
+
+void
+cogl_program_uniform_1f (COGLint uniform_no,
+ gfloat value);
+
+/* Offscreen api */
+
+CoglHandle
+cogl_offscreen_new_to_texture (CoglHandle texhandle);
+
+CoglHandle
+cogl_offscreen_new_multisample ();
+
+CoglHandle
+cogl_offscreen_ref (CoglHandle handle);
+
+void
+cogl_offscreen_unref (CoglHandle handle);
+
+void
+cogl_offscreen_blit (CoglHandle src_buffer,
+ CoglHandle dst_buffer);
+
+void
+cogl_offscreen_blit_region (CoglHandle src_buffer,
+ CoglHandle dst_buffer,
+ int src_x,
+ int src_y,
+ int src_w,
+ int src_h,
+ int dst_x,
+ int dst_y,
+ int dst_w,
+ int dst_h);
+
+void
+cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen);
+
+G_END_DECLS
+
+#endif /* __COGL_H__ */
diff --git a/common/Makefile.am b/common/Makefile.am
new file mode 100644
index 00000000..b6dca2df
--- /dev/null
+++ b/common/Makefile.am
@@ -0,0 +1,23 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/clutter \
+ -I$(top_srcdir)/clutter/cogl \
+ -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \
+ -I$(top_builddir)/clutter \
+ -I$(top_builddir)/clutter/cogl \
+ $(CLUTTER_CFLAGS) \
+ $(CLUTTER_DEBUG_CFLAGS) \
+ $(GCC_FLAGS)
+
+LDADD = $(CLUTTER_LIBS)
+
+noinst_LTLIBRARIES = libclutter-cogl-common.la
+EXTRA_DIST = stb_image.c
+
+libclutter_cogl_common_la_SOURCES = \
+ cogl-util.h \
+ cogl-bitmap.h \
+ cogl-util.c \
+ cogl-bitmap.c \
+ cogl-bitmap-fallback.c \
+ cogl-bitmap-pixbuf.c
diff --git a/common/cogl-bitmap-fallback.c b/common/cogl-bitmap-fallback.c
new file mode 100644
index 00000000..21a55846
--- /dev/null
+++ b/common/cogl-bitmap-fallback.c
@@ -0,0 +1,369 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-bitmap.h"
+
+#include <string.h>
+
+/* TO rgba */
+
+inline static void
+_cogl_g_to_rgba (const guchar *src, guchar *dst)
+{
+ dst[0] = src[0];
+ dst[1] = src[0];
+ dst[2] = src[0];
+ dst[3] = 255;
+}
+
+inline static void
+_cogl_rgb_to_rgba (const guchar *src, guchar *dst)
+{
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = 255;
+}
+
+inline static void
+_cogl_bgr_to_rgba (const guchar *src, guchar *dst)
+{
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst[3] = 255;
+}
+
+inline static void
+_cogl_bgra_to_rgba (const guchar *src, guchar *dst)
+{
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst[3] = src[3];
+}
+
+inline static void
+_cogl_argb_to_rgba (const guchar *src, guchar *dst)
+{
+ dst[0] = src[1];
+ dst[1] = src[2];
+ dst[2] = src[3];
+ dst[3] = src[0];
+}
+
+inline static void
+_cogl_abgr_to_rgba (const guchar *src, guchar *dst)
+{
+ dst[0] = src[3];
+ dst[1] = src[2];
+ dst[2] = src[1];
+ dst[3] = src[0];
+}
+
+inline static void
+_cogl_rgba_to_rgba (const guchar *src, guchar *dst)
+{
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+}
+
+/* FROM rgba */
+
+inline static void
+_cogl_rgba_to_g (const guchar *src, guchar *dst)
+{
+ dst[0] = (src[0] + src[1] + src[2]) / 3;
+}
+
+inline static void
+_cogl_rgba_to_rgb (const guchar *src, guchar *dst)
+{
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+}
+
+inline static void
+_cogl_rgba_to_bgr (const guchar *src, guchar *dst)
+{
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+}
+
+inline static void
+_cogl_rgba_to_bgra (const guchar *src, guchar *dst)
+{
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst[3] = src[3];
+}
+
+inline static void
+_cogl_rgba_to_argb (const guchar *src, guchar *dst)
+{
+ dst[0] = src[3];
+ dst[1] = src[0];
+ dst[2] = src[1];
+ dst[3] = src[2];
+}
+
+inline static void
+_cogl_rgba_to_abgr (const guchar *src, guchar *dst)
+{
+ dst[0] = src[3];
+ dst[1] = src[2];
+ dst[2] = src[1];
+ dst[3] = src[0];
+}
+
+/* (Un)Premultiplication */
+
+inline static void
+_cogl_unpremult_alpha_0 (const guchar *src, guchar *dst)
+{
+ dst[0] = 0;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = 0;
+}
+
+inline static void
+_cogl_unpremult_alpha_last (const guchar *src, guchar *dst)
+{
+ guchar alpha = src[3];
+
+ dst[0] = (((src[0] >> 16) & 0xff) * 255 ) / alpha;
+ dst[1] = (((src[1] >> 8) & 0xff) * 255 ) / alpha;
+ dst[2] = (((src[2] >> 0) & 0xff) * 255 ) / alpha;
+ dst[3] = alpha;
+}
+
+inline static void
+_cogl_unpremult_alpha_first (const guchar *src, guchar *dst)
+{
+ guchar alpha = src[0];
+
+ dst[0] = alpha;
+ dst[1] = (((src[1] >> 16) & 0xff) * 255 ) / alpha;
+ dst[2] = (((src[2] >> 8) & 0xff) * 255 ) / alpha;
+ dst[3] = (((src[3] >> 0) & 0xff) * 255 ) / alpha;
+}
+
+gboolean
+_cogl_bitmap_fallback_can_convert (CoglPixelFormat src, CoglPixelFormat dst)
+{
+ if (src == dst)
+ return FALSE;
+
+ switch (src & COGL_UNORDERED_MASK)
+ {
+ case COGL_PIXEL_FORMAT_G_8:
+ case COGL_PIXEL_FORMAT_24:
+ case COGL_PIXEL_FORMAT_32:
+
+ if ((dst & COGL_UNORDERED_MASK) != COGL_PIXEL_FORMAT_24 &&
+ (dst & COGL_UNORDERED_MASK) != COGL_PIXEL_FORMAT_32 &&
+ (dst & COGL_UNORDERED_MASK) != COGL_PIXEL_FORMAT_G_8)
+ return FALSE;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+_cogl_bitmap_fallback_can_unpremult (CoglPixelFormat format)
+{
+ return ((format & COGL_UNORDERED_MASK) == COGL_PIXEL_FORMAT_32);
+}
+
+gboolean
+_cogl_bitmap_fallback_convert (const CoglBitmap *bmp,
+ CoglBitmap *dst_bmp,
+ CoglPixelFormat dst_format)
+{
+ guchar *src;
+ guchar *dst;
+ gint src_bpp;
+ gint dst_bpp;
+ gint x,y;
+ guchar temp_rgba[4] = {0,0,0,0};
+
+ /* Make sure conversion supported */
+ if (!_cogl_bitmap_fallback_can_convert (bmp->format, dst_format))
+ return FALSE;
+
+ src_bpp = _cogl_get_format_bpp (bmp->format);
+ dst_bpp = _cogl_get_format_bpp (dst_format);
+
+ /* Initialize destination bitmap */
+ *dst_bmp = *bmp;
+ dst_bmp->rowstride = sizeof(guchar) * dst_bpp * dst_bmp->width;
+ dst_bmp->format = ((bmp->format & COGL_PREMULT_BIT) |
+ (dst_format & COGL_UNPREMULT_MASK));
+
+ /* Allocate a new buffer to hold converted data */
+ dst_bmp->data = g_malloc (sizeof(guchar)
+ * dst_bmp->height
+ * dst_bmp->rowstride);
+
+ /* FIXME: Optimize */
+ for (y = 0; y < bmp->height; y++)
+ {
+ src = (guchar*)bmp->data + y * bmp->rowstride;
+ dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride;
+
+ for (x = 0; x < bmp->width; x++)
+ {
+ /* FIXME: Would be nice to at least remove this inner
+ * branching, but not sure it can be done without
+ * rewriting of the whole loop */
+ switch (bmp->format & COGL_UNPREMULT_MASK)
+ {
+ case COGL_PIXEL_FORMAT_G_8:
+ _cogl_g_to_rgba (src, temp_rgba); break;
+ case COGL_PIXEL_FORMAT_RGB_888:
+ _cogl_rgb_to_rgba (src, temp_rgba); break;
+ case COGL_PIXEL_FORMAT_BGR_888:
+ _cogl_bgr_to_rgba (src, temp_rgba); break;
+ case COGL_PIXEL_FORMAT_RGBA_8888:
+ _cogl_rgba_to_rgba (src, temp_rgba); break;
+ case COGL_PIXEL_FORMAT_BGRA_8888:
+ _cogl_bgra_to_rgba (src, temp_rgba); break;
+ case COGL_PIXEL_FORMAT_ARGB_8888:
+ _cogl_argb_to_rgba (src, temp_rgba); break;
+ case COGL_PIXEL_FORMAT_ABGR_8888:
+ _cogl_abgr_to_rgba (src, temp_rgba); break;
+ default:
+ break;
+ }
+
+ switch (dst_format & COGL_UNPREMULT_MASK)
+ {
+ case COGL_PIXEL_FORMAT_G_8:
+ _cogl_rgba_to_g (temp_rgba, dst); break;
+ case COGL_PIXEL_FORMAT_RGB_888:
+ _cogl_rgba_to_rgb (temp_rgba, dst); break;
+ case COGL_PIXEL_FORMAT_BGR_888:
+ _cogl_rgba_to_bgr (temp_rgba, dst); break;
+ case COGL_PIXEL_FORMAT_RGBA_8888:
+ _cogl_rgba_to_rgba (temp_rgba, dst); break;
+ case COGL_PIXEL_FORMAT_BGRA_8888:
+ _cogl_rgba_to_bgra (temp_rgba, dst); break;
+ case COGL_PIXEL_FORMAT_ARGB_8888:
+ _cogl_rgba_to_argb (temp_rgba, dst); break;
+ case COGL_PIXEL_FORMAT_ABGR_8888:
+ _cogl_rgba_to_abgr (temp_rgba, dst); break;
+ default:
+ break;
+ }
+
+ src += src_bpp;
+ dst += dst_bpp;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp,
+ CoglBitmap *dst_bmp)
+{
+ guchar *src;
+ guchar *dst;
+ gint bpp;
+ gint x,y;
+
+ /* Make sure format supported for un-premultiplication */
+ if (!_cogl_bitmap_fallback_can_unpremult (bmp->format))
+ return FALSE;
+
+ bpp = _cogl_get_format_bpp (bmp->format);
+
+ /* Initialize destination bitmap */
+ *dst_bmp = *bmp;
+ dst_bmp->format = (bmp->format & COGL_UNPREMULT_MASK);
+
+ /* Allocate a new buffer to hold converted data */
+ dst_bmp->data = g_malloc (sizeof(guchar)
+ * dst_bmp->height
+ * dst_bmp->rowstride);
+
+ /* FIXME: Optimize */
+ for (y = 0; y < bmp->height; y++)
+ {
+ src = (guchar*)bmp->data + y * bmp->rowstride;
+ dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride;
+
+ for (x = 0; x < bmp->width; x++)
+ {
+ /* FIXME: Would be nice to at least remove this inner
+ * branching, but not sure it can be done without
+ * rewriting of the whole loop */
+ if (bmp->format & COGL_AFIRST_BIT)
+ {
+ if (src[0] == 0)
+ _cogl_unpremult_alpha_0 (src, dst);
+ else
+ _cogl_unpremult_alpha_first (src, dst);
+ }
+ else
+ {
+ if (src[3] == 0)
+ _cogl_unpremult_alpha_0 (src, dst);
+ else
+ _cogl_unpremult_alpha_last (src, dst);
+ }
+
+ src += bpp;
+ dst += bpp;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+_cogl_bitmap_fallback_from_file (CoglBitmap *bmp,
+ const gchar *filename)
+{
+ /* FIXME: use jpeglib, libpng, etc. manually maybe */
+ return FALSE;
+}
diff --git a/common/cogl-bitmap-pixbuf.c b/common/cogl-bitmap-pixbuf.c
new file mode 100644
index 00000000..92378860
--- /dev/null
+++ b/common/cogl-bitmap-pixbuf.c
@@ -0,0 +1,197 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-bitmap.h"
+
+#include <string.h>
+
+#ifdef USE_GDKPIXBUF
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#endif
+
+gboolean
+_cogl_bitmap_can_convert (CoglPixelFormat src, CoglPixelFormat dst)
+{
+ return FALSE;
+}
+
+gboolean
+_cogl_bitmap_can_unpremult (CoglPixelFormat format)
+{
+ return FALSE;
+}
+
+gboolean
+_cogl_bitmap_convert (const CoglBitmap *bmp,
+ CoglBitmap *dst_bmp,
+ CoglPixelFormat dst_format)
+{
+ return FALSE;
+}
+
+gboolean
+_cogl_bitmap_unpremult (const CoglBitmap *bmp,
+ CoglBitmap *dst_bmp)
+{
+ return FALSE;
+}
+
+#ifdef USE_GDKPIXBUF
+
+gboolean
+_cogl_bitmap_from_file (CoglBitmap *bmp,
+ const gchar *filename,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ gboolean has_alpha;
+ GdkColorspace color_space;
+ CoglPixelFormat pixel_format;
+ gint width;
+ gint height;
+ gint rowstride;
+ gint bits_per_sample;
+ gint n_channels;
+ gint last_row_size;
+ guchar *pixels;
+ guchar *out_data;
+ guchar *out;
+ gint r;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (bmp == NULL) return FALSE;
+
+ /* Load from file using GdkPixbuf */
+ pixbuf = gdk_pixbuf_new_from_file (filename, error);
+ if (pixbuf == NULL) return FALSE;
+
+ /* Get pixbuf properties */
+ has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+ color_space = gdk_pixbuf_get_colorspace (pixbuf);
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ bits_per_sample = gdk_pixbuf_get_bits_per_sample (pixbuf);
+ n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+
+ /* The docs say this is the right way */
+ last_row_size = width * ((n_channels * bits_per_sample + 7) / 8);
+
+ /* According to current docs this should be true and so
+ * the translation to cogl pixel format below valid */
+ g_assert (bits_per_sample == 8);
+
+ if (has_alpha)
+ g_assert (n_channels == 4);
+ else
+ g_assert (n_channels == 3);
+
+ /* Translate to cogl pixel format */
+ switch (color_space)
+ {
+ case GDK_COLORSPACE_RGB:
+ /* The only format supported by GdkPixbuf so far */
+ pixel_format = has_alpha ?
+ COGL_PIXEL_FORMAT_RGBA_8888 :
+ COGL_PIXEL_FORMAT_RGB_888;
+ break;
+
+ default:
+ /* Ouch, spec changed! */
+ g_object_unref (pixbuf);
+ return FALSE;
+ }
+
+ /* FIXME: Any way to destroy pixbuf but retain pixel data? */
+
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+ out_data = (guchar*) g_malloc ((height - 1) * rowstride + last_row_size);
+ out = out_data;
+
+ /* Copy up to last row */
+ for (r = 0; r < height-1; ++r)
+ {
+ memcpy (out, pixels, rowstride);
+ pixels += rowstride;
+ out += rowstride;
+ }
+
+ /* Copy last row */
+ memcpy (out, pixels, last_row_size);
+
+ /* Destroy GdkPixbuf object */
+ g_object_unref (pixbuf);
+
+ /* Store bitmap info */
+ bmp->data = out_data;
+ bmp->format = pixel_format;
+ bmp->width = width;
+ bmp->height = height;
+ bmp->rowstride = rowstride;
+
+ return TRUE;
+}
+
+#else
+
+#include "stb_image.c"
+
+gboolean
+_cogl_bitmap_from_file (CoglBitmap *bmp,
+ const gchar *filename,
+ GError **error)
+{
+ gint stb_pixel_format;
+ gint width;
+ gint height;
+ guchar *pixels;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (bmp == NULL) return FALSE;
+
+ /* Load from file using stb */
+ pixels = stbi_load (filename, &width, &height, &stb_pixel_format, STBI_rgb_alpha);
+ if (pixels == NULL) return FALSE;
+
+ /* Store bitmap info */
+ bmp->data = pixels;
+ bmp->format = COGL_PIXEL_FORMAT_RGBA_8888;
+ bmp->width = width;
+ bmp->height = height;
+ bmp->rowstride = width * 4;
+
+ g_print ("we successfully used stb_image to load %s\n", filename);
+
+ return TRUE;
+}
+#endif
diff --git a/common/cogl-bitmap.c b/common/cogl-bitmap.c
new file mode 100644
index 00000000..39341e0b
--- /dev/null
+++ b/common/cogl-bitmap.c
@@ -0,0 +1,150 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-bitmap.h"
+
+#include <string.h>
+
+gint
+_cogl_get_format_bpp (CoglPixelFormat format)
+{
+ gint bpp_lut[] = {
+ 0, /* invalid */
+ 1, /* A_8 */
+ 3, /* 888 */
+ 4, /* 8888 */
+ 2, /* 565 */
+ 2, /* 4444 */
+ 2, /* 5551 */
+ 2, /* YUV */
+ 1 /* G_8 */
+ };
+
+ return bpp_lut [format & COGL_UNORDERED_MASK];
+}
+
+gboolean
+_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp,
+ CoglBitmap *dst_bmp,
+ CoglPixelFormat dst_format)
+{
+ CoglBitmap tmp_bmp = *bmp;
+ CoglBitmap new_bmp = *bmp;
+ gboolean new_bmp_owner = FALSE;
+
+ /* Is base format different (not considering premult status)? */
+ if ((bmp->format & COGL_UNPREMULT_MASK) !=
+ (dst_format & COGL_UNPREMULT_MASK))
+ {
+ /* Try converting using imaging library */
+ if (!_cogl_bitmap_convert (&new_bmp, &tmp_bmp, dst_format))
+ {
+ /* ... or try fallback */
+ if (!_cogl_bitmap_fallback_convert (&new_bmp, &tmp_bmp, dst_format))
+ return FALSE;
+ }
+
+ /* Update bitmap with new data */
+ new_bmp = tmp_bmp;
+ new_bmp_owner = TRUE;
+ }
+
+ /* Do we need to unpremultiply */
+ if ((bmp->format & COGL_PREMULT_BIT) == 0 &&
+ (dst_format & COGL_PREMULT_BIT) > 0)
+ {
+ /* Try unpremultiplying using imaging library */
+ if (!_cogl_bitmap_unpremult (&new_bmp, &tmp_bmp))
+ {
+ /* ... or try fallback */
+ if (!_cogl_bitmap_fallback_unpremult (&new_bmp, &tmp_bmp))
+ {
+ if (new_bmp_owner)
+ g_free (new_bmp.data);
+
+ return FALSE;
+ }
+ }
+
+ /* Update bitmap with new data */
+ if (new_bmp_owner)
+ g_free (new_bmp.data);
+
+ new_bmp = tmp_bmp;
+ new_bmp_owner = TRUE;
+ }
+
+ /* Do we need to premultiply */
+ if ((bmp->format & COGL_PREMULT_BIT) > 0 &&
+ (dst_format & COGL_PREMULT_BIT) == 0)
+ {
+ /* FIXME: implement premultiplication */
+ if (new_bmp_owner)
+ g_free (new_bmp.data);
+
+ return FALSE;
+ }
+
+ /* Output new bitmap info */
+ *dst_bmp = new_bmp;
+
+ return TRUE;
+}
+
+void
+_cogl_bitmap_copy_subregion (CoglBitmap *src,
+ CoglBitmap *dst,
+ gint src_x,
+ gint src_y,
+ gint dst_x,
+ gint dst_y,
+ gint width,
+ gint height)
+{
+ guchar *srcdata;
+ guchar *dstdata;
+ gint bpp;
+ gint line;
+
+ /* Intended only for fast copies when format is equal! */
+ g_assert (src->format == dst->format);
+ bpp = _cogl_get_format_bpp (src->format);
+
+ srcdata = src->data + src_y * src->rowstride + src_x * bpp;
+ dstdata = dst->data + dst_y * dst->rowstride + dst_x * bpp;
+
+ for (line=0; line<height; ++line)
+ {
+ memcpy (dstdata, srcdata, width * bpp);
+ srcdata += src->rowstride;
+ dstdata += dst->rowstride;
+ }
+}
diff --git a/common/cogl-bitmap.h b/common/cogl-bitmap.h
new file mode 100644
index 00000000..ca9f8a9c
--- /dev/null
+++ b/common/cogl-bitmap.h
@@ -0,0 +1,95 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_BITMAP_H
+#define __COGL_BITMAP_H
+
+#include <glib.h>
+
+typedef struct _CoglBitmap CoglBitmap;
+
+struct _CoglBitmap
+{
+ guchar *data;
+ CoglPixelFormat format;
+ gint width;
+ gint height;
+ gint rowstride;
+};
+
+gboolean
+_cogl_bitmap_can_convert (CoglPixelFormat src, CoglPixelFormat dst);
+
+gboolean
+_cogl_bitmap_fallback_can_convert (CoglPixelFormat src, CoglPixelFormat dst);
+
+gboolean
+_cogl_bitmap_can_unpremult (CoglPixelFormat format);
+
+gboolean
+_cogl_bitmap_fallback_can_unpremult (CoglPixelFormat format);
+
+gboolean
+_cogl_bitmap_convert (const CoglBitmap *bmp,
+ CoglBitmap *dst_bmp,
+ CoglPixelFormat dst_format);
+gboolean
+_cogl_bitmap_fallback_convert (const CoglBitmap *bmp,
+ CoglBitmap *dst_bmp,
+ CoglPixelFormat dst_format);
+
+gboolean
+_cogl_bitmap_unpremult (const CoglBitmap *bmp,
+ CoglBitmap *dst_bmp);
+
+gboolean
+_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp,
+ CoglBitmap *dst_bmp);
+
+gboolean
+_cogl_bitmap_from_file (CoglBitmap *bmp,
+ const gchar *filename,
+ GError **error);
+
+gboolean
+_cogl_bitmap_fallback_from_file (CoglBitmap *bmp,
+ const gchar *filename);
+
+gboolean
+_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp,
+ CoglBitmap *dst_bmp,
+ CoglPixelFormat dst_format);
+
+void
+_cogl_bitmap_copy_subregion (CoglBitmap *src,
+ CoglBitmap *dst,
+ gint src_x,
+ gint src_y,
+ gint dst_x,
+ gint dst_y,
+ gint width,
+ gint height);
+
+#endif /* __COGL_BITMAP_H */
diff --git a/common/cogl-util.c b/common/cogl-util.c
new file mode 100644
index 00000000..b024e763
--- /dev/null
+++ b/common/cogl-util.c
@@ -0,0 +1,51 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-util.h"
+
+/**
+ * cogl_util_next_p2:
+ * @a: Value to get the next power
+ *
+ * Calculates the next power greater than @a.
+ *
+ * Return value: The next power after @a.
+ */
+int
+cogl_util_next_p2 (int a)
+{
+ int rval=1;
+
+ while(rval < a)
+ rval <<= 1;
+
+ return rval;
+}
diff --git a/common/cogl-util.h b/common/cogl-util.h
new file mode 100644
index 00000000..759da8cc
--- /dev/null
+++ b/common/cogl-util.h
@@ -0,0 +1,32 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_UTIL_H
+#define __COGL_UTIL_H
+
+int
+cogl_util_next_p2 (int a);
+
+#endif /* __COGL_UTIL_H */
diff --git a/common/stb_image.c b/common/stb_image.c
new file mode 100644
index 00000000..bd3e63b3
--- /dev/null
+++ b/common/stb_image.c
@@ -0,0 +1,3772 @@
+/* stbi-1.12 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
+ when you control the images you're loading
+
+ QUICK NOTES:
+ Primarily of interest to game developers and other people who can
+ avoid problematic images and only need the trivial interface
+
+ JPEG baseline (no JPEG progressive, no oddball channel decimations)
+ PNG non-interlaced
+ BMP non-1bpp, non-RLE
+ TGA (not sure what subset, if a subset)
+ PSD (composited view only, no extra channels)
+ HDR (radiance rgbE format)
+ writes BMP,TGA (define STBI_NO_WRITE to remove code)
+ decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code)
+ supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD)
+
+ TODO:
+ stbi_info_*
+
+ history:
+ 1.12 const qualifiers in the API
+ 1.11 Support installable IDCT, colorspace conversion routines
+ 1.10 Fixes for 64-bit (don't use "unsigned long")
+ optimized upsampling by Fabian "ryg" Giesen
+ 1.09 Fix format-conversion for PSD code (bad global variables!)
+ 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
+ 1.07 attempt to fix C++ warning/errors again
+ 1.06 attempt to fix C++ warning/errors again
+ 1.05 fix TGA loading to return correct *comp and use good luminance calc
+ 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
+ 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
+ 1.02 support for (subset of) HDR files, float interface for preferred access to them
+ 1.01 fix bug: possible bug in handling right-side up bmps... not sure
+ fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all
+ 1.00 interface to zlib that skips zlib header
+ 0.99 correct handling of alpha in palette
+ 0.98 TGA loader by lonesock; dynamically add loaders (untested)
+ 0.97 jpeg errors on too large a file; also catch another malloc failure
+ 0.96 fix detection of invalid v value - particleman@mollyrocket forum
+ 0.95 during header scan, seek to markers in case of padding
+ 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
+ 0.93 handle jpegtran output; verbose errors
+ 0.92 read 4,8,16,24,32-bit BMP files of several formats
+ 0.91 output 24-bit Windows 3.0 BMP files
+ 0.90 fix a few more warnings; bump version number to approach 1.0
+ 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
+ 0.60 fix compiling as c++
+ 0.59 fix warnings: merge Dave Moore's -Wall fixes
+ 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
+ 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less
+ than 16 available
+ 0.56 fix bug: zlib uncompressed mode len vs. nlen
+ 0.55 fix bug: restart_interval not initialized to 0
+ 0.54 allow NULL for 'int *comp'
+ 0.53 fix bug in png 3->4; speedup png decoding
+ 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
+ 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
+ on 'test' only check type, not whether we support this variant
+*/
+
+
+//// begin header file ////////////////////////////////////////////////////
+//
+// Limitations:
+// - no progressive/interlaced support (jpeg, png)
+// - 8-bit samples only (jpeg, png)
+// - not threadsafe
+// - channel subsampling of at most 2 in each dimension (jpeg)
+// - no delayed line count (jpeg) -- IJG doesn't support either
+//
+// Basic usage (see HDR discussion below):
+// int x,y,n;
+// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
+// // ... process data if not NULL ...
+// // ... x = width, y = height, n = # 8-bit components per pixel ...
+// // ... replace '0' with '1'..'4' to force that many components per pixel
+// stbi_image_free(data)
+//
+// Standard parameters:
+// int *x -- outputs image width in pixels
+// int *y -- outputs image height in pixels
+// int *comp -- outputs # of image components in image file
+// int req_comp -- if non-zero, # of image components requested in result
+//
+// The return value from an image loader is an 'unsigned char *' which points
+// to the pixel data. The pixel data consists of *y scanlines of *x pixels,
+// with each pixel consisting of N interleaved 8-bit components; the first
+// pixel pointed to is top-left-most in the image. There is no padding between
+// image scanlines or between pixels, regardless of format. The number of
+// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise.
+// If req_comp is non-zero, *comp has the number of components that _would_
+// have been output otherwise. E.g. if you set req_comp to 4, you will always
+// get RGBA output, but you can check *comp to easily see if it's opaque.
+//
+// An output image with N components has the following components interleaved
+// in this order in each pixel:
+//
+// N=#comp components
+// 1 grey
+// 2 grey, alpha
+// 3 red, green, blue
+// 4 red, green, blue, alpha
+//
+// If image loading fails for any reason, the return value will be NULL,
+// and *x, *y, *comp will be unchanged. The function stbi_failure_reason()
+// can be queried for an extremely brief, end-user unfriendly explanation
+// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid
+// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
+// more user-friendly ones.
+//
+// Paletted PNG and BMP images are automatically depalettized.
+//
+//
+// ===========================================================================
+//
+// HDR image support (disable by defining STBI_NO_HDR)
+//
+// stb_image now supports loading HDR images in general, and currently
+// the Radiance .HDR file format, although the support is provided
+// generically. You can still load any file through the existing interface;
+// if you attempt to load an HDR file, it will be automatically remapped to
+// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
+// both of these constants can be reconfigured through this interface:
+//
+// stbi_hdr_to_ldr_gamma(2.2f);
+// stbi_hdr_to_ldr_scale(1.0f);
+//
+// (note, do not use _inverse_ constants; stbi_image will invert them
+// appropriately).
+//
+// Additionally, there is a new, parallel interface for loading files as
+// (linear) floats to preserve the full dynamic range:
+//
+// float *data = stbi_loadf(filename, &x, &y, &n, 0);
+//
+// If you load LDR images through this interface, those images will
+// be promoted to floating point values, run through the inverse of
+// constants corresponding to the above:
+//
+// stbi_ldr_to_hdr_scale(1.0f);
+// stbi_ldr_to_hdr_gamma(2.2f);
+//
+// Finally, given a filename (or an open file or memory block--see header
+// file for details) containing image data, you can query for the "most
+// appropriate" interface to use (that is, whether the image is HDR or
+// not), using:
+//
+// stbi_is_hdr(char *filename);
+
+
+#ifndef STBI_NO_STDIO
+#include <stdio.h>
+#endif
+
+#ifndef STBI_NO_HDR
+#include <math.h> // ldexp
+#include <string.h> // strcmp
+#endif
+
+enum
+{
+ STBI_default = 0, // only used for req_comp
+
+ STBI_grey = 1,
+ STBI_grey_alpha = 2,
+ STBI_rgb = 3,
+ STBI_rgb_alpha = 4,
+};
+
+typedef unsigned char stbi_uc;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// WRITING API
+
+#if !defined(STBI_NO_WRITE) && !defined(STBI_NO_STDIO)
+// write a BMP/TGA file given tightly packed 'comp' channels (no padding, nor bmp-stride-padding)
+// (you must include the appropriate extension in the filename).
+// returns TRUE on success, FALSE if couldn't open file, error writing file
+extern int stbi_write_bmp (char const *filename, int x, int y, int comp, void *data);
+extern int stbi_write_tga (char const *filename, int x, int y, int comp, void *data);
+#endif
+
+// PRIMARY API - works on images of any type
+
+// load image by filename, open file, or memory buffer
+#ifndef STBI_NO_STDIO
+extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+// for stbi_load_from_file, file pointer is left pointing immediately after image
+
+#ifndef STBI_NO_HDR
+#ifndef STBI_NO_STDIO
+extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+
+extern void stbi_hdr_to_ldr_gamma(float gamma);
+extern void stbi_hdr_to_ldr_scale(float scale);
+
+extern void stbi_ldr_to_hdr_gamma(float gamma);
+extern void stbi_ldr_to_hdr_scale(float scale);
+
+#endif // STBI_NO_HDR
+
+// get a VERY brief reason for failure
+extern char *stbi_failure_reason (void);
+
+// free the loaded image -- this is just free()
+extern void stbi_image_free (void *retval_from_stbi_load);
+
+// get image dimensions & components without fully decoding
+extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
+#ifndef STBI_NO_STDIO
+extern int stbi_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_is_hdr (char const *filename);
+extern int stbi_is_hdr_from_file(FILE *f);
+#endif
+
+// ZLIB client - used by PNG, available for other purposes
+
+extern char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen);
+extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);
+extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
+
+extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
+extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
+
+// TYPE-SPECIFIC ACCESS
+
+// is it a jpeg?
+extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len);
+extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+#ifndef STBI_NO_STDIO
+extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern int stbi_jpeg_test_file (FILE *f);
+extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+
+extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+
+extern int stbi_jpeg_dc_only; // only decode DC component
+
+// is it a png?
+extern int stbi_png_test_memory (stbi_uc const *buffer, int len);
+extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+#ifndef STBI_NO_STDIO
+extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern int stbi_png_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_png_test_file (FILE *f);
+extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+
+// is it a bmp?
+extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len);
+
+extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_bmp_test_file (FILE *f);
+extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// is it a tga?
+extern int stbi_tga_test_memory (stbi_uc const *buffer, int len);
+
+extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_tga_test_file (FILE *f);
+extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// is it a psd?
+extern int stbi_psd_test_memory (stbi_uc const *buffer, int len);
+
+extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_psd_test_file (FILE *f);
+extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// is it an hdr?
+extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len);
+
+extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_hdr_test_file (FILE *f);
+extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// define new loaders
+typedef struct
+{
+ int (*test_memory)(stbi_uc const *buffer, int len);
+ stbi_uc * (*load_from_memory)(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+ #ifndef STBI_NO_STDIO
+ int (*test_file)(FILE *f);
+ stbi_uc * (*load_from_file)(FILE *f, int *x, int *y, int *comp, int req_comp);
+ #endif
+} stbi_loader;
+
+// register a loader by filling out the above structure (you must defined ALL functions)
+// returns 1 if added or already added, 0 if not added (too many loaders)
+extern int stbi_register_loader(stbi_loader *loader);
+
+// define faster low-level operations (typically SIMD support)
+#if STBI_SIMD
+typedef void (*stbi_idct_8x8)(uint8 *out, int out_stride, short data[64], unsigned short *dequantize);
+// compute an integer IDCT on "input"
+// input[x] = data[x] * dequantize[x]
+// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride'
+// CLAMP results to 0..255
+typedef void (*stbi_YCbCr_to_RGB_run)(uint8 *output, uint8 const *y, uint8 const *cb, uint8 const *cr, int count, int step);
+// compute a conversion from YCbCr to RGB
+// 'count' pixels
+// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B
+// y: Y input channel
+// cb: Cb input channel; scale/biased to be 0..255
+// cr: Cr input channel; scale/biased to be 0..255
+
+extern void stbi_install_idct(stbi_idct_8x8 func);
+extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func);
+#endif // STBI_SIMD
+
+#ifdef __cplusplus
+}
+#endif
+
+//
+//
+//// end header file /////////////////////////////////////////////////////
+
+#ifndef STBI_NO_STDIO
+#include <stdio.h>
+#endif
+#include <stdlib.h>
+#include <memory.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#if STBI_SIMD
+#include <emmintrin.h>
+#endif
+
+#ifndef _MSC_VER
+#define __forceinline
+#endif
+
+
+// implementation:
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef signed short int16;
+typedef unsigned int uint32;
+typedef signed int int32;
+typedef unsigned int uint;
+
+// should produce compiler error if size is wrong
+typedef unsigned char validate_uint32[sizeof(uint32)==4];
+
+#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE)
+#define STBI_NO_WRITE
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Generic API that works on all image types
+//
+
+static char *failure_reason;
+
+char *stbi_failure_reason(void)
+{
+ return failure_reason;
+}
+
+static int e(char *str)
+{
+ failure_reason = str;
+ return 0;
+}
+
+#ifdef STBI_NO_FAILURE_STRINGS
+ #define e(x,y) 0
+#elif defined(STBI_FAILURE_USERMSG)
+ #define e(x,y) e(y)
+#else
+ #define e(x,y) e(x)
+#endif
+
+#define epf(x,y) ((float *) (e(x,y)?NULL:NULL))
+#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL))
+
+void stbi_image_free(void *retval_from_stbi_load)
+{
+ free(retval_from_stbi_load);
+}
+
+#define MAX_LOADERS 32
+stbi_loader *loaders[MAX_LOADERS];
+static int max_loaders = 0;
+
+int stbi_register_loader(stbi_loader *loader)
+{
+ int i;
+ for (i=0; i < MAX_LOADERS; ++i) {
+ // already present?
+ if (loaders[i] == loader)
+ return 1;
+ // end of the list?
+ if (loaders[i] == NULL) {
+ loaders[i] = loader;
+ max_loaders = i+1;
+ return 1;
+ }
+ }
+ // no room for it
+ return 0;
+}
+
+#ifndef STBI_NO_HDR
+static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
+static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp);
+#endif
+
+#ifndef STBI_NO_STDIO
+unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ FILE *f = fopen(filename, "rb");
+ unsigned char *result;
+ if (!f) return epuc("can't fopen", "Unable to open file");
+ result = stbi_load_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return result;
+}
+
+unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ int i;
+ if (stbi_jpeg_test_file(f))
+ return stbi_jpeg_load_from_file(f,x,y,comp,req_comp);
+ if (stbi_png_test_file(f))
+ return stbi_png_load_from_file(f,x,y,comp,req_comp);
+ if (stbi_bmp_test_file(f))
+ return stbi_bmp_load_from_file(f,x,y,comp,req_comp);
+ if (stbi_psd_test_file(f))
+ return stbi_psd_load_from_file(f,x,y,comp,req_comp);
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_file(f)) {
+ float *hdr = stbi_hdr_load_from_file(f, x,y,comp,req_comp);
+ return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
+ }
+ #endif
+ for (i=0; i < max_loaders; ++i)
+ if (loaders[i]->test_file(f))
+ return loaders[i]->load_from_file(f,x,y,comp,req_comp);
+ // test tga last because it's a crappy test!
+ if (stbi_tga_test_file(f))
+ return stbi_tga_load_from_file(f,x,y,comp,req_comp);
+ return epuc("unknown image type", "Image not of any known type, or corrupt");
+}
+#endif
+
+unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ int i;
+ if (stbi_jpeg_test_memory(buffer,len))
+ return stbi_jpeg_load_from_memory(buffer,len,x,y,comp,req_comp);
+ if (stbi_png_test_memory(buffer,len))
+ return stbi_png_load_from_memory(buffer,len,x,y,comp,req_comp);
+ if (stbi_bmp_test_memory(buffer,len))
+ return stbi_bmp_load_from_memory(buffer,len,x,y,comp,req_comp);
+ if (stbi_psd_test_memory(buffer,len))
+ return stbi_psd_load_from_memory(buffer,len,x,y,comp,req_comp);
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_memory(buffer, len)) {
+ float *hdr = stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp);
+ return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
+ }
+ #endif
+ for (i=0; i < max_loaders; ++i)
+ if (loaders[i]->test_memory(buffer,len))
+ return loaders[i]->load_from_memory(buffer,len,x,y,comp,req_comp);
+ // test tga last because it's a crappy test!
+ if (stbi_tga_test_memory(buffer,len))
+ return stbi_tga_load_from_memory(buffer,len,x,y,comp,req_comp);
+ return epuc("unknown image type", "Image not of any known type, or corrupt");
+}
+
+#ifndef STBI_NO_HDR
+
+#ifndef STBI_NO_STDIO
+float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ FILE *f = fopen(filename, "rb");
+ float *result;
+ if (!f) return epf("can't fopen", "Unable to open file");
+ result = stbi_loadf_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return result;
+}
+
+float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *data;
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_file(f))
+ return stbi_hdr_load_from_file(f,x,y,comp,req_comp);
+ #endif
+ data = stbi_load_from_file(f, x, y, comp, req_comp);
+ if (data)
+ return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
+ return epf("unknown image type", "Image not of any known type, or corrupt");
+}
+#endif
+
+float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_memory(buffer, len))
+ return stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp);
+ #endif
+ data = stbi_load_from_memory(buffer, len, x, y, comp, req_comp);
+ if (data)
+ return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
+ return epf("unknown image type", "Image not of any known type, or corrupt");
+}
+#endif
+
+// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is
+// defined, for API simplicity; if STBI_NO_HDR is defined, it always
+// reports false!
+
+extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
+{
+ #ifndef STBI_NO_HDR
+ return stbi_hdr_test_memory(buffer, len);
+ #else
+ return 0;
+ #endif
+}
+
+#ifndef STBI_NO_STDIO
+extern int stbi_is_hdr (char const *filename)
+{
+ FILE *f = fopen(filename, "rb");
+ int result=0;
+ if (f) {
+ result = stbi_is_hdr_from_file(f);
+ fclose(f);
+ }
+ return result;
+}
+
+extern int stbi_is_hdr_from_file(FILE *f)
+{
+ #ifndef STBI_NO_HDR
+ return stbi_hdr_test_file(f);
+ #else
+ return 0;
+ #endif
+}
+
+#endif
+
+// @TODO: get image dimensions & components without fully decoding
+#ifndef STBI_NO_STDIO
+extern int stbi_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+#ifndef STBI_NO_HDR
+static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f;
+static float l2h_gamma=2.2f, l2h_scale=1.0f;
+
+void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; }
+void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; }
+
+void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; }
+void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; }
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Common code used by all image loaders
+//
+
+// image width, height, # components
+static uint32 img_x, img_y;
+static int img_n, img_out_n;
+
+enum
+{
+ SCAN_load=0,
+ SCAN_type,
+ SCAN_header,
+};
+
+// An API for reading either from memory or file.
+#ifndef STBI_NO_STDIO
+static FILE *img_file;
+#endif
+static uint8 const *img_buffer, *img_buffer_end;
+
+#ifndef STBI_NO_STDIO
+static void start_file(FILE *f)
+{
+ img_file = f;
+}
+#endif
+
+static void start_mem(uint8 const *buffer, int len)
+{
+#ifndef STBI_NO_STDIO
+ img_file = NULL;
+#endif
+ img_buffer = buffer;
+ img_buffer_end = buffer+len;
+}
+
+static int get8(void)
+{
+#ifndef STBI_NO_STDIO
+ if (img_file) {
+ int c = fgetc(img_file);
+ return c == EOF ? 0 : c;
+ }
+#endif
+ if (img_buffer < img_buffer_end)
+ return *img_buffer++;
+ return 0;
+}
+
+static int at_eof(void)
+{
+#ifndef STBI_NO_STDIO
+ if (img_file)
+ return feof(img_file);
+#endif
+ return img_buffer >= img_buffer_end;
+}
+
+static uint8 get8u(void)
+{
+ return (uint8) get8();
+}
+
+static void skip(int n)
+{
+#ifndef STBI_NO_STDIO
+ if (img_file)
+ fseek(img_file, n, SEEK_CUR);
+ else
+#endif
+ img_buffer += n;
+}
+
+static int get16(void)
+{
+ int z = get8();
+ return (z << 8) + get8();
+}
+
+static uint32 get32(void)
+{
+ uint32 z = get16();
+ return (z << 16) + get16();
+}
+
+static int get16le(void)
+{
+ int z = get8();
+ return z + (get8() << 8);
+}
+
+static uint32 get32le(void)
+{
+ uint32 z = get16le();
+ return z + (get16le() << 16);
+}
+
+static void getn(stbi_uc *buffer, int n)
+{
+#ifndef STBI_NO_STDIO
+ if (img_file) {
+ fread(buffer, 1, n, img_file);
+ return;
+ }
+#endif
+ memcpy(buffer, img_buffer, n);
+ img_buffer += n;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// generic converter from built-in img_n to req_comp
+// individual types do this automatically as much as possible (e.g. jpeg
+// does all cases internally since it needs to colorspace convert anyway,
+// and it never has alpha, so very few cases ). png can automatically
+// interleave an alpha=255 channel, but falls back to this for other cases
+//
+// assume data buffer is malloced, so malloc a new one and free that one
+// only failure mode is malloc failing
+
+static uint8 compute_y(int r, int g, int b)
+{
+ return (uint8) (((r*77) + (g*150) + (29*b)) >> 8);
+}
+
+static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp)
+{
+ uint i,j;
+ unsigned char *good;
+
+ if (req_comp == img_n) return data;
+ assert(req_comp >= 1 && req_comp <= 4);
+
+ good = (unsigned char *) malloc(req_comp * img_x * img_y);
+ if (good == NULL) {
+ free(data);
+ return epuc("outofmem", "Out of memory");
+ }
+
+ for (j=0; j < img_y; ++j) {
+ unsigned char *src = data + j * img_x * img_n ;
+ unsigned char *dest = good + j * img_x * req_comp;
+
+ #define COMBO(a,b) ((a)*8+(b))
+ #define CASE(a,b) case COMBO(a,b): for(i=0; i < img_x; ++i, src += a, dest += b)
+
+ // convert source image with img_n components to one with req_comp components;
+ // avoid switch per pixel, so use switch per scanline and massive macros
+ switch(COMBO(img_n, req_comp)) {
+ CASE(1,2) dest[0]=src[0], dest[1]=255; break;
+ CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break;
+ CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break;
+ CASE(2,1) dest[0]=src[0]; break;
+ CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break;
+ CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break;
+ CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break;
+ CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break;
+ CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break;
+ CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break;
+ CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break;
+ CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break;
+ default: assert(0);
+ }
+ #undef CASE
+ }
+
+ free(data);
+ img_out_n = req_comp;
+ return good;
+}
+
+#ifndef STBI_NO_HDR
+static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp)
+{
+ int i,k,n;
+ float *output = (float *) malloc(x * y * comp * sizeof(float));
+ if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); }
+ // compute number of non-alpha components
+ if (comp & 1) n = comp; else n = comp-1;
+ for (i=0; i < x*y; ++i) {
+ for (k=0; k < n; ++k) {
+ output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale;
+ }
+ if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f;
+ }
+ free(data);
+ return output;
+}
+
+#define float2int(x) ((int) (x))
+static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp)
+{
+ int i,k,n;
+ stbi_uc *output = (stbi_uc *) malloc(x * y * comp);
+ if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); }
+ // compute number of non-alpha components
+ if (comp & 1) n = comp; else n = comp-1;
+ for (i=0; i < x*y; ++i) {
+ for (k=0; k < n; ++k) {
+ float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f;
+ if (z < 0) z = 0;
+ if (z > 255) z = 255;
+ output[i*comp + k] = float2int(z);
+ }
+ if (k < comp) {
+ float z = data[i*comp+k] * 255 + 0.5f;
+ if (z < 0) z = 0;
+ if (z > 255) z = 255;
+ output[i*comp + k] = float2int(z);
+ }
+ }
+ free(data);
+ return output;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation)
+//
+// simple implementation
+// - channel subsampling of at most 2 in each dimension
+// - doesn't support delayed output of y-dimension
+// - simple interface (only one output format: 8-bit interleaved RGB)
+// - doesn't try to recover corrupt jpegs
+// - doesn't allow partial loading, loading multiple at once
+// - still fast on x86 (copying globals into locals doesn't help x86)
+// - allocates lots of intermediate memory (full size of all components)
+// - non-interleaved case requires this anyway
+// - allows good upsampling (see next)
+// high-quality
+// - upsampled channels are bilinearly interpolated, even across blocks
+// - quality integer IDCT derived from IJG's 'slow'
+// performance
+// - fast huffman; reasonable integer IDCT
+// - uses a lot of intermediate memory, could cache poorly
+// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4
+// stb_jpeg: 1.34 seconds (MSVC6, default release build)
+// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro)
+// IJL11.dll: 1.08 seconds (compiled by intel)
+// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG)
+// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro)
+
+int stbi_jpeg_dc_only;
+
+// huffman decoding acceleration
+#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache
+
+typedef struct
+{
+ uint8 fast[1 << FAST_BITS];
+ // weirdly, repacking this into AoS is a 10% speed loss, instead of a win
+ uint16 code[256];
+ uint8 values[256];
+ uint8 size[257];
+ unsigned int maxcode[18];
+ int delta[17]; // old 'firstsymbol' - old 'firstcode'
+} huffman;
+
+static huffman huff_dc[4]; // baseline is 2 tables, extended is 4
+static huffman huff_ac[4];
+static uint8 dequant[4][64];
+#if STBI_SIMD
+static __declspec(align(16)) unsigned short dequant2[4][64];
+#endif
+
+static int build_huffman(huffman *h, int *count)
+{
+ int i,j,k=0,code;
+ // build size list for each symbol (from JPEG spec)
+ for (i=0; i < 16; ++i)
+ for (j=0; j < count[i]; ++j)
+ h->size[k++] = (uint8) (i+1);
+ h->size[k] = 0;
+
+ // compute actual symbols (from jpeg spec)
+ code = 0;
+ k = 0;
+ for(j=1; j <= 16; ++j) {
+ // compute delta to add to code to compute symbol id
+ h->delta[j] = k - code;
+ if (h->size[k] == j) {
+ while (h->size[k] == j)
+ h->code[k++] = (uint16) (code++);
+ if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG");
+ }
+ // compute largest code + 1 for this size, preshifted as needed later
+ h->maxcode[j] = code << (16-j);
+ code <<= 1;
+ }
+ h->maxcode[j] = 0xffffffff;
+
+ // build non-spec acceleration table; 255 is flag for not-accelerated
+ memset(h->fast, 255, 1 << FAST_BITS);
+ for (i=0; i < k; ++i) {
+ int s = h->size[i];
+ if (s <= FAST_BITS) {
+ int c = h->code[i] << (FAST_BITS-s);
+ int m = 1 << (FAST_BITS-s);
+ for (j=0; j < m; ++j) {
+ h->fast[c+j] = (uint8) i;
+ }
+ }
+ }
+ return 1;
+}
+
+// sizes for components, interleaved MCUs
+static int img_h_max, img_v_max;
+static int img_mcu_x, img_mcu_y;
+static int img_mcu_w, img_mcu_h;
+
+// definition of jpeg image component
+static struct
+{
+ int id;
+ int h,v;
+ int tq;
+ int hd,ha;
+ int dc_pred;
+
+ int x,y,w2,h2;
+ uint8 *data;
+ void *raw_data;
+ uint8 *linebuf;
+} img_comp[4];
+
+static uint32 code_buffer; // jpeg entropy-coded buffer
+static int code_bits; // number of valid bits
+static unsigned char marker; // marker seen while filling entropy buffer
+static int nomore; // flag if we saw a marker so must stop
+
+static void grow_buffer_unsafe(void)
+{
+ do {
+ int b = nomore ? 0 : get8();
+ if (b == 0xff) {
+ int c = get8();
+ if (c != 0) {
+ marker = (unsigned char) c;
+ nomore = 1;
+ return;
+ }
+ }
+ code_buffer = (code_buffer << 8) | b;
+ code_bits += 8;
+ } while (code_bits <= 24);
+}
+
+// (1 << n) - 1
+static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};
+
+// decode a jpeg huffman value from the bitstream
+__forceinline static int decode(huffman *h)
+{
+ unsigned int temp;
+ int c,k;
+
+ if (code_bits < 16) grow_buffer_unsafe();
+
+ // look at the top FAST_BITS and determine what symbol ID it is,
+ // if the code is <= FAST_BITS
+ c = (code_buffer >> (code_bits - FAST_BITS)) & ((1 << FAST_BITS)-1);
+ k = h->fast[c];
+ if (k < 255) {
+ if (h->size[k] > code_bits)
+ return -1;
+ code_bits -= h->size[k];
+ return h->values[k];
+ }
+
+ // naive test is to shift the code_buffer down so k bits are
+ // valid, then test against maxcode. To speed this up, we've
+ // preshifted maxcode left so that it has (16-k) 0s at the
+ // end; in other words, regardless of the number of bits, it
+ // wants to be compared against something shifted to have 16;
+ // that way we don't need to shift inside the loop.
+ if (code_bits < 16)
+ temp = (code_buffer << (16 - code_bits)) & 0xffff;
+ else
+ temp = (code_buffer >> (code_bits - 16)) & 0xffff;
+ for (k=FAST_BITS+1 ; ; ++k)
+ if (temp < h->maxcode[k])
+ break;
+ if (k == 17) {
+ // error! code not found
+ code_bits -= 16;
+ return -1;
+ }
+
+ if (k > code_bits)
+ return -1;
+
+ // convert the huffman code to the symbol id
+ c = ((code_buffer >> (code_bits - k)) & bmask[k]) + h->delta[k];
+ assert((((code_buffer) >> (code_bits - h->size[c])) & bmask[h->size[c]]) == h->code[c]);
+
+ // convert the id to a symbol
+ code_bits -= k;
+ return h->values[c];
+}
+
+// combined JPEG 'receive' and JPEG 'extend', since baseline
+// always extends everything it receives.
+__forceinline static int extend_receive(int n)
+{
+ unsigned int m = 1 << (n-1);
+ unsigned int k;
+ if (code_bits < n) grow_buffer_unsafe();
+ k = (code_buffer >> (code_bits - n)) & bmask[n];
+ code_bits -= n;
+ // the following test is probably a random branch that won't
+ // predict well. I tried to table accelerate it but failed.
+ // maybe it's compiling as a conditional move?
+ if (k < m)
+ return (-1 << n) + k + 1;
+ else
+ return k;
+}
+
+// given a value that's at position X in the zigzag stream,
+// where does it appear in the 8x8 matrix coded as row-major?
+static uint8 dezigzag[64+15] =
+{
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63,
+ // let corrupt input sample past end
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63
+};
+
+// decode one 64-entry block--
+static int decode_block(short data[64], huffman *hdc, huffman *hac, int b)
+{
+ int diff,dc,k;
+ int t = decode(hdc);
+ if (t < 0) return e("bad huffman code","Corrupt JPEG");
+
+ // 0 all the ac values now so we can do it 32-bits at a time
+ memset(data,0,64*sizeof(data[0]));
+
+ diff = t ? extend_receive(t) : 0;
+ dc = img_comp[b].dc_pred + diff;
+ img_comp[b].dc_pred = dc;
+ data[0] = (short) dc;
+
+ // decode AC components, see JPEG spec
+ k = 1;
+ do {
+ int r,s;
+ int rs = decode(hac);
+ if (rs < 0) return e("bad huffman code","Corrupt JPEG");
+ s = rs & 15;
+ r = rs >> 4;
+ if (s == 0) {
+ if (rs != 0xf0) break; // end block
+ k += 16;
+ } else {
+ k += r;
+ // decode into unzigzag'd location
+ data[dezigzag[k++]] = (short) extend_receive(s);
+ }
+ } while (k < 64);
+ return 1;
+}
+
+// take a -128..127 value and clamp it and convert to 0..255
+__forceinline static uint8 clamp(int x)
+{
+ x += 128;
+ // trick to use a single test to catch both cases
+ if ((unsigned int) x > 255) {
+ if (x < 0) return 0;
+ if (x > 255) return 255;
+ }
+ return (uint8) x;
+}
+
+#define f2f(x) (int) (((x) * 4096 + 0.5))
+#define fsh(x) ((x) << 12)
+
+// derived from jidctint -- DCT_ISLOW
+#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \
+ int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \
+ p2 = s2; \
+ p3 = s6; \
+ p1 = (p2+p3) * f2f(0.5411961f); \
+ t2 = p1 + p3*f2f(-1.847759065f); \
+ t3 = p1 + p2*f2f( 0.765366865f); \
+ p2 = s0; \
+ p3 = s4; \
+ t0 = fsh(p2+p3); \
+ t1 = fsh(p2-p3); \
+ x0 = t0+t3; \
+ x3 = t0-t3; \
+ x1 = t1+t2; \
+ x2 = t1-t2; \
+ t0 = s7; \
+ t1 = s5; \
+ t2 = s3; \
+ t3 = s1; \
+ p3 = t0+t2; \
+ p4 = t1+t3; \
+ p1 = t0+t3; \
+ p2 = t1+t2; \
+ p5 = (p3+p4)*f2f( 1.175875602f); \
+ t0 = t0*f2f( 0.298631336f); \
+ t1 = t1*f2f( 2.053119869f); \
+ t2 = t2*f2f( 3.072711026f); \
+ t3 = t3*f2f( 1.501321110f); \
+ p1 = p5 + p1*f2f(-0.899976223f); \
+ p2 = p5 + p2*f2f(-2.562915447f); \
+ p3 = p3*f2f(-1.961570560f); \
+ p4 = p4*f2f(-0.390180644f); \
+ t3 += p1+p4; \
+ t2 += p2+p3; \
+ t1 += p2+p4; \
+ t0 += p1+p3;
+
+#if !STBI_SIMD
+// .344 seconds on 3*anemones.jpg
+static void idct_block(uint8 *out, int out_stride, short data[64], uint8 *dequantize)
+{
+ int i,val[64],*v=val;
+ uint8 *o,*dq = dequantize;
+ short *d = data;
+
+ if (stbi_jpeg_dc_only) {
+ // ok, I don't really know why this is right, but it seems to be:
+ int z = 128 + ((d[0] * dq[0]) >> 3);
+ for (i=0; i < 8; ++i) {
+ out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = z;
+ out += out_stride;
+ }
+ return;
+ }
+
+ // columns
+ for (i=0; i < 8; ++i,++d,++dq, ++v) {
+ // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
+ if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
+ && d[40]==0 && d[48]==0 && d[56]==0) {
+ // no shortcut 0 seconds
+ // (1|2|3|4|5|6|7)==0 0 seconds
+ // all separate -0.047 seconds
+ // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
+ int dcterm = d[0] * dq[0] << 2;
+ v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
+ } else {
+ IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24],
+ d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56])
+ // constants scaled things up by 1<<12; let's bring them back
+ // down, but keep 2 extra bits of precision
+ x0 += 512; x1 += 512; x2 += 512; x3 += 512;
+ v[ 0] = (x0+t3) >> 10;
+ v[56] = (x0-t3) >> 10;
+ v[ 8] = (x1+t2) >> 10;
+ v[48] = (x1-t2) >> 10;
+ v[16] = (x2+t1) >> 10;
+ v[40] = (x2-t1) >> 10;
+ v[24] = (x3+t0) >> 10;
+ v[32] = (x3-t0) >> 10;
+ }
+ }
+
+ for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
+ // no fast case since the first 1D IDCT spread components out
+ IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
+ // constants scaled things up by 1<<12, plus we had 1<<2 from first
+ // loop, plus horizontal and vertical each scale by sqrt(8) so together
+ // we've got an extra 1<<3, so 1<<17 total we need to remove.
+ x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536;
+ o[0] = clamp((x0+t3) >> 17);
+ o[7] = clamp((x0-t3) >> 17);
+ o[1] = clamp((x1+t2) >> 17);
+ o[6] = clamp((x1-t2) >> 17);
+ o[2] = clamp((x2+t1) >> 17);
+ o[5] = clamp((x2-t1) >> 17);
+ o[3] = clamp((x3+t0) >> 17);
+ o[4] = clamp((x3-t0) >> 17);
+ }
+}
+#else
+static void idct_block(uint8 *out, int out_stride, short data[64], unsigned short *dequantize)
+{
+ int i,val[64],*v=val;
+ uint8 *o;
+ unsigned short *dq = dequantize;
+ short *d = data;
+
+ if (stbi_jpeg_dc_only) {
+ // ok, I don't really know why this is right, but it seems to be:
+ int z = 128 + ((d[0] * dq[0]) >> 3);
+ for (i=0; i < 8; ++i) {
+ out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = z;
+ out += out_stride;
+ }
+ return;
+ }
+
+ // columns
+ for (i=0; i < 8; ++i,++d,++dq, ++v) {
+ // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
+ if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
+ && d[40]==0 && d[48]==0 && d[56]==0) {
+ // no shortcut 0 seconds
+ // (1|2|3|4|5|6|7)==0 0 seconds
+ // all separate -0.047 seconds
+ // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
+ int dcterm = d[0] * dq[0] << 2;
+ v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
+ } else {
+ IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24],
+ d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56])
+ // constants scaled things up by 1<<12; let's bring them back
+ // down, but keep 2 extra bits of precision
+ x0 += 512; x1 += 512; x2 += 512; x3 += 512;
+ v[ 0] = (x0+t3) >> 10;
+ v[56] = (x0-t3) >> 10;
+ v[ 8] = (x1+t2) >> 10;
+ v[48] = (x1-t2) >> 10;
+ v[16] = (x2+t1) >> 10;
+ v[40] = (x2-t1) >> 10;
+ v[24] = (x3+t0) >> 10;
+ v[32] = (x3-t0) >> 10;
+ }
+ }
+
+ for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
+ // no fast case since the first 1D IDCT spread components out
+ IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
+ // constants scaled things up by 1<<12, plus we had 1<<2 from first
+ // loop, plus horizontal and vertical each scale by sqrt(8) so together
+ // we've got an extra 1<<3, so 1<<17 total we need to remove.
+ x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536;
+ o[0] = clamp((x0+t3) >> 17);
+ o[7] = clamp((x0-t3) >> 17);
+ o[1] = clamp((x1+t2) >> 17);
+ o[6] = clamp((x1-t2) >> 17);
+ o[2] = clamp((x2+t1) >> 17);
+ o[5] = clamp((x2-t1) >> 17);
+ o[3] = clamp((x3+t0) >> 17);
+ o[4] = clamp((x3-t0) >> 17);
+ }
+}
+static stbi_idct_8x8 stbi_idct_installed = idct_block;
+
+extern void stbi_install_idct(stbi_idct_8x8 func)
+{
+ stbi_idct_installed = func;
+}
+#endif
+
+#define MARKER_none 0xff
+// if there's a pending marker from the entropy stream, return that
+// otherwise, fetch from the stream and get a marker. if there's no
+// marker, return 0xff, which is never a valid marker value
+static uint8 get_marker(void)
+{
+ uint8 x;
+ if (marker != MARKER_none) { x = marker; marker = MARKER_none; return x; }
+ x = get8u();
+ if (x != 0xff) return MARKER_none;
+ while (x == 0xff)
+ x = get8u();
+ return x;
+}
+
+// in each scan, we'll have scan_n components, and the order
+// of the components is specified by order[]
+static int scan_n, order[4];
+static int restart_interval, todo;
+#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7)
+
+// after a restart interval, reset the entropy decoder and
+// the dc prediction
+static void reset(void)
+{
+ code_bits = 0;
+ code_buffer = 0;
+ nomore = 0;
+ img_comp[0].dc_pred = img_comp[1].dc_pred = img_comp[2].dc_pred = 0;
+ marker = MARKER_none;
+ todo = restart_interval ? restart_interval : 0x7fffffff;
+ // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,
+ // since we don't even allow 1<<30 pixels
+}
+
+static int parse_entropy_coded_data(void)
+{
+ reset();
+ if (scan_n == 1) {
+ int i,j;
+ #if STBI_SIMD
+ __declspec(align(16))
+ #endif
+ short data[64];
+ int n = order[0];
+ // non-interleaved data, we just need to process one block at a time,
+ // in trivial scanline order
+ // number of blocks to do just depends on how many actual "pixels" this
+ // component has, independent of interleaved MCU blocking and such
+ int w = (img_comp[n].x+7) >> 3;
+ int h = (img_comp[n].y+7) >> 3;
+ for (j=0; j < h; ++j) {
+ for (i=0; i < w; ++i) {
+ if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0;
+ #if STBI_SIMD
+ stbi_idct_installed(img_comp[n].data+img_comp[n].w2*j*8+i*8, img_comp[n].w2, data, dequant2[img_comp[n].tq]);
+ #else
+ idct_block(img_comp[n].data+img_comp[n].w2*j*8+i*8, img_comp[n].w2, data, dequant[img_comp[n].tq]);
+ #endif
+ // every data block is an MCU, so countdown the restart interval
+ if (--todo <= 0) {
+ if (code_bits < 24) grow_buffer_unsafe();
+ // if it's NOT a restart, then just bail, so we get corrupt data
+ // rather than no data
+ if (!RESTART(marker)) return 1;
+ reset();
+ }
+ }
+ }
+ } else { // interleaved!
+ int i,j,k,x,y;
+ short data[64];
+ for (j=0; j < img_mcu_y; ++j) {
+ for (i=0; i < img_mcu_x; ++i) {
+ // scan an interleaved mcu... process scan_n components in order
+ for (k=0; k < scan_n; ++k) {
+ int n = order[k];
+ // scan out an mcu's worth of this component; that's just determined
+ // by the basic H and V specified for the component
+ for (y=0; y < img_comp[n].v; ++y) {
+ for (x=0; x < img_comp[n].h; ++x) {
+ int x2 = (i*img_comp[n].h + x)*8;
+ int y2 = (j*img_comp[n].v + y)*8;
+ if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0;
+ #if STBI_SIMD
+ stbi_idct_installed(img_comp[n].data+img_comp[n].w2*y2+x2, img_comp[n].w2, data, dequant2[img_comp[n].tq]);
+ #else
+ idct_block(img_comp[n].data+img_comp[n].w2*y2+x2, img_comp[n].w2, data, dequant[img_comp[n].tq]);
+ #endif
+ }
+ }
+ }
+ // after all interleaved components, that's an interleaved MCU,
+ // so now count down the restart interval
+ if (--todo <= 0) {
+ if (code_bits < 24) grow_buffer_unsafe();
+ // if it's NOT a restart, then just bail, so we get corrupt data
+ // rather than no data
+ if (!RESTART(marker)) return 1;
+ reset();
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+static int process_marker(int m)
+{
+ int L;
+ switch (m) {
+ case MARKER_none: // no marker found
+ return e("expected marker","Corrupt JPEG");
+
+ case 0xC2: // SOF - progressive
+ return e("progressive jpeg","JPEG format not supported (progressive)");
+
+ case 0xDD: // DRI - specify restart interval
+ if (get16() != 4) return e("bad DRI len","Corrupt JPEG");
+ restart_interval = get16();
+ return 1;
+
+ case 0xDB: // DQT - define quantization table
+ L = get16()-2;
+ while (L > 0) {
+ int z = get8();
+ int p = z >> 4;
+ int t = z & 15,i;
+ if (p != 0) return e("bad DQT type","Corrupt JPEG");
+ if (t > 3) return e("bad DQT table","Corrupt JPEG");
+ for (i=0; i < 64; ++i)
+ dequant[t][dezigzag[i]] = get8u();
+ #if STBI_SIMD
+ for (i=0; i < 64; ++i)
+ dequant2[t][i] = dequant[t][i];
+ #endif
+ L -= 65;
+ }
+ return L==0;
+
+ case 0xC4: // DHT - define huffman table
+ L = get16()-2;
+ while (L > 0) {
+ uint8 *v;
+ int sizes[16],i,m=0;
+ int z = get8();
+ int tc = z >> 4;
+ int th = z & 15;
+ if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG");
+ for (i=0; i < 16; ++i) {
+ sizes[i] = get8();
+ m += sizes[i];
+ }
+ L -= 17;
+ if (tc == 0) {
+ if (!build_huffman(huff_dc+th, sizes)) return 0;
+ v = huff_dc[th].values;
+ } else {
+ if (!build_huffman(huff_ac+th, sizes)) return 0;
+ v = huff_ac[th].values;
+ }
+ for (i=0; i < m; ++i)
+ v[i] = get8u();
+ L -= m;
+ }
+ return L==0;
+ }
+ // check for comment block or APP blocks
+ if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {
+ skip(get16()-2);
+ return 1;
+ }
+ return 0;
+}
+
+// after we see SOS
+static int process_scan_header(void)
+{
+ int i;
+ int Ls = get16();
+ scan_n = get8();
+ if (scan_n < 1 || scan_n > 4 || scan_n > (int) img_n) return e("bad SOS component count","Corrupt JPEG");
+ if (Ls != 6+2*scan_n) return e("bad SOS len","Corrupt JPEG");
+ for (i=0; i < scan_n; ++i) {
+ int id = get8(), which;
+ int z = get8();
+ for (which = 0; which < img_n; ++which)
+ if (img_comp[which].id == id)
+ break;
+ if (which == img_n) return 0;
+ img_comp[which].hd = z >> 4; if (img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG");
+ img_comp[which].ha = z & 15; if (img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG");
+ order[i] = which;
+ }
+ if (get8() != 0) return e("bad SOS","Corrupt JPEG");
+ get8(); // should be 63, but might be 0
+ if (get8() != 0) return e("bad SOS","Corrupt JPEG");
+
+ return 1;
+}
+
+static int process_frame_header(int scan)
+{
+ int Lf,p,i,z, h_max=1,v_max=1;
+ Lf = get16(); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG
+ p = get8(); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline
+ img_y = get16(); if (img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG
+ img_x = get16(); if (img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires
+ img_n = get8();
+ if (img_n != 3 && img_n != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires
+
+ if (Lf != 8+3*img_n) return e("bad SOF len","Corrupt JPEG");
+
+ for (i=0; i < img_n; ++i) {
+ img_comp[i].id = get8();
+ if (img_comp[i].id != i+1) // JFIF requires
+ if (img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files!
+ return e("bad component ID","Corrupt JPEG");
+ z = get8();
+ img_comp[i].h = (z >> 4); if (!img_comp[i].h || img_comp[i].h > 4) return e("bad H","Corrupt JPEG");
+ img_comp[i].v = z & 15; if (!img_comp[i].v || img_comp[i].v > 4) return e("bad V","Corrupt JPEG");
+ img_comp[i].tq = get8(); if (img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG");
+ }
+
+ if (scan != SCAN_load) return 1;
+
+ if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode");
+
+ for (i=0; i < img_n; ++i) {
+ if (img_comp[i].h > h_max) h_max = img_comp[i].h;
+ if (img_comp[i].v > v_max) v_max = img_comp[i].v;
+ }
+
+ // compute interleaved mcu info
+ img_h_max = h_max;
+ img_v_max = v_max;
+ img_mcu_w = h_max * 8;
+ img_mcu_h = v_max * 8;
+ img_mcu_x = (img_x + img_mcu_w-1) / img_mcu_w;
+ img_mcu_y = (img_y + img_mcu_h-1) / img_mcu_h;
+
+ for (i=0; i < img_n; ++i) {
+ // number of effective pixels (e.g. for non-interleaved MCU)
+ img_comp[i].x = (img_x * img_comp[i].h + h_max-1) / h_max;
+ img_comp[i].y = (img_y * img_comp[i].v + v_max-1) / v_max;
+ // to simplify generation, we'll allocate enough memory to decode
+ // the bogus oversized data from using interleaved MCUs and their
+ // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't
+ // discard the extra data until colorspace conversion
+ img_comp[i].w2 = img_mcu_x * img_comp[i].h * 8;
+ img_comp[i].h2 = img_mcu_y * img_comp[i].v * 8;
+ img_comp[i].raw_data = malloc(img_comp[i].w2 * img_comp[i].h2+15);
+ if (img_comp[i].raw_data == NULL) {
+ for(--i; i >= 0; --i) {
+ free(img_comp[i].raw_data);
+ img_comp[i].data = NULL;
+ }
+ return e("outofmem", "Out of memory");
+ }
+ img_comp[i].data = (uint8*) (((int) img_comp[i].raw_data + 15) & ~15);
+ img_comp[i].linebuf = NULL;
+ }
+
+ return 1;
+}
+
+// use comparisons since in some cases we handle more than one case (e.g. SOF)
+#define DNL(x) ((x) == 0xdc)
+#define SOI(x) ((x) == 0xd8)
+#define EOI(x) ((x) == 0xd9)
+#define SOF(x) ((x) == 0xc0 || (x) == 0xc1)
+#define SOS(x) ((x) == 0xda)
+
+static int decode_jpeg_header(int scan)
+{
+ int m;
+ marker = MARKER_none; // initialize cached marker to empty
+ m = get_marker();
+ if (!SOI(m)) return e("no SOI","Corrupt JPEG");
+ if (scan == SCAN_type) return 1;
+ m = get_marker();
+ while (!SOF(m)) {
+ if (!process_marker(m)) return 0;
+ m = get_marker();
+ while (m == MARKER_none) {
+ // some files have extra padding after their blocks, so ok, we'll scan
+ if (at_eof()) return e("no SOF", "Corrupt JPEG");
+ m = get_marker();
+ }
+ }
+ if (!process_frame_header(scan)) return 0;
+ return 1;
+}
+
+static int decode_jpeg_image(void)
+{
+ int m;
+ restart_interval = 0;
+ if (!decode_jpeg_header(SCAN_load)) return 0;
+ m = get_marker();
+ while (!EOI(m)) {
+ if (SOS(m)) {
+ if (!process_scan_header()) return 0;
+ if (!parse_entropy_coded_data()) return 0;
+ } else {
+ if (!process_marker(m)) return 0;
+ }
+ m = get_marker();
+ }
+ return 1;
+}
+
+// static jfif-centered resampling (across block boundaries)
+
+typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1,
+ int w, int hs);
+
+#define div4(x) ((uint8) ((x) >> 2))
+
+static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ return in_near;
+}
+
+static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // need to generate two samples vertically for every one in input
+ int i;
+ for (i=0; i < w; ++i)
+ out[i] = div4(3*in_near[i] + in_far[i] + 2);
+ return out;
+}
+
+static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // need to generate two samples horizontally for every one in input
+ int i;
+ uint8 *input = in_near;
+ if (w == 1) {
+ // if only one sample, can't do any interpolation
+ out[0] = out[1] = input[0];
+ return out;
+ }
+
+ out[0] = input[0];
+ out[1] = div4(input[0]*3 + input[1] + 2);
+ for (i=1; i < w-1; ++i) {
+ int n = 3*input[i]+2;
+ out[i*2+0] = div4(n+input[i-1]);
+ out[i*2+1] = div4(n+input[i+1]);
+ }
+ out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2);
+ out[i*2+1] = input[w-1];
+ return out;
+}
+
+#define div16(x) ((uint8) ((x) >> 4))
+
+static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // need to generate 2x2 samples for every one in input
+ int i,t0,t1;
+ if (w == 1) {
+ out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2);
+ return out;
+ }
+
+ t1 = 3*in_near[0] + in_far[0];
+ out[0] = div4(t1+2);
+ for (i=1; i < w; ++i) {
+ t0 = t1;
+ t1 = 3*in_near[i]+in_far[i];
+ out[i*2-1] = div16(3*t0 + t1 + 8);
+ out[i*2 ] = div16(3*t1 + t0 + 8);
+ }
+ out[w*2-1] = div4(t1+2);
+ return out;
+}
+
+static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // resample with nearest-neighbor
+ int i,j;
+ for (i=0; i < w; ++i)
+ for (j=0; j < hs; ++j)
+ out[i*hs+j] = in_near[i];
+ return out;
+}
+
+#define float2fixed(x) ((int) ((x) * 65536 + 0.5))
+
+// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro)
+// VC6 without processor=Pro is generating multiple LEAs per multiply!
+static void YCbCr_to_RGB_row(uint8 *out, uint8 *y, uint8 *pcb, uint8 *pcr, int count, int step)
+{
+ int i;
+ for (i=0; i < count; ++i) {
+ int y_fixed = (y[i] << 16) + 32768; // rounding
+ int r,g,b;
+ int cr = pcr[i] - 128;
+ int cb = pcb[i] - 128;
+ r = y_fixed + cr*float2fixed(1.40200f);
+ g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f);
+ b = y_fixed + cb*float2fixed(1.77200f);
+ r >>= 16;
+ g >>= 16;
+ b >>= 16;
+ if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
+ if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
+ if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
+ out[0] = (uint8)r;
+ out[1] = (uint8)g;
+ out[2] = (uint8)b;
+ out[3] = 255;
+ out += step;
+ }
+}
+
+#if STBI_SIMD
+static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row;
+
+void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func)
+{
+ stbi_YCbCr_installed = func;
+}
+#endif
+
+
+// clean up the temporary component buffers
+static void cleanup_jpeg(void)
+{
+ int i;
+ for (i=0; i < img_n; ++i) {
+ if (img_comp[i].data) {
+ free(img_comp[i].raw_data);
+ img_comp[i].data = NULL;
+ }
+ if (img_comp[i].linebuf) {
+ free(img_comp[i].linebuf);
+ img_comp[i].linebuf = NULL;
+ }
+ }
+}
+
+typedef struct
+{
+ resample_row_func resample;
+ uint8 *line0,*line1;
+ int hs,vs; // expansion factor in each axis
+ int w_lores; // horizontal pixels pre-expansion
+ int ystep; // how far through vertical expansion we are
+ int ypos; // which pre-expansion row we're on
+} stbi_resample;
+
+static uint8 *load_jpeg_image(int *out_x, int *out_y, int *comp, int req_comp)
+{
+ int n, decode_n;
+ // validate req_comp
+ if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error");
+
+ // load a jpeg image from whichever source
+ if (!decode_jpeg_image()) { cleanup_jpeg(); return NULL; }
+
+ // determine actual number of components to generate
+ n = req_comp ? req_comp : img_n;
+
+ if (img_n == 3 && n < 3)
+ decode_n = 1;
+ else
+ decode_n = img_n;
+
+ // resample and color-convert
+ {
+ int k;
+ uint i,j;
+ uint8 *output;
+ uint8 *coutput[4];
+
+ stbi_resample res_comp[4];
+
+ for (k=0; k < decode_n; ++k) {
+ stbi_resample *r = &res_comp[k];
+
+ // allocate line buffer big enough for upsampling off the edges
+ // with upsample factor of 4
+ img_comp[k].linebuf = (uint8 *) malloc(img_x + 3);
+ if (!img_comp[k].linebuf) { cleanup_jpeg(); return epuc("outofmem", "Out of memory"); }
+
+ r->hs = img_h_max / img_comp[k].h;
+ r->vs = img_v_max / img_comp[k].v;
+ r->ystep = r->vs >> 1;
+ r->w_lores = (img_x + r->hs-1) / r->hs;
+ r->ypos = 0;
+ r->line0 = r->line1 = img_comp[k].data;
+
+ if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;
+ else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2;
+ else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2;
+ else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2;
+ else r->resample = resample_row_generic;
+ }
+
+ // can't error after this so, this is safe
+ output = (uint8 *) malloc(n * img_x * img_y + 1);
+ if (!output) { cleanup_jpeg(); return epuc("outofmem", "Out of memory"); }
+
+ // now go ahead and resample
+ for (j=0; j < img_y; ++j) {
+ uint8 *out = output + n * img_x * j;
+ for (k=0; k < decode_n; ++k) {
+ stbi_resample *r = &res_comp[k];
+ int y_bot = r->ystep >= (r->vs >> 1);
+ coutput[k] = r->resample(img_comp[k].linebuf,
+ y_bot ? r->line1 : r->line0,
+ y_bot ? r->line0 : r->line1,
+ r->w_lores, r->hs);
+ if (++r->ystep >= r->vs) {
+ r->ystep = 0;
+ r->line0 = r->line1;
+ if (++r->ypos < img_comp[k].y)
+ r->line1 += img_comp[k].w2;
+ }
+ }
+ if (n >= 3) {
+ uint8 *y = coutput[0];
+ if (img_n == 3) {
+ #if STBI_SIMD
+ stbi_YCbCr_installed(out, y, coutput[1], coutput[2], img_x, n);
+ #else
+ YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], img_x, n);
+ #endif
+ } else
+ for (i=0; i < img_x; ++i) {
+ out[0] = out[1] = out[2] = y[i];
+ out[3] = 255; // not used if n==3
+ out += n;
+ }
+ } else {
+ uint8 *y = coutput[0];
+ if (n == 1)
+ for (i=0; i < img_x; ++i) out[i] = y[i];
+ else
+ for (i=0; i < img_x; ++i) *out++ = y[i], *out++ = 255;
+ }
+ }
+ cleanup_jpeg();
+ *out_x = img_x;
+ *out_y = img_y;
+ if (comp) *comp = img_n; // report original components, not output
+ return output;
+ }
+}
+
+#ifndef STBI_NO_STDIO
+unsigned char *stbi_jpeg_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return load_jpeg_image(x,y,comp,req_comp);
+}
+
+unsigned char *stbi_jpeg_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_jpeg_load_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+#endif
+
+unsigned char *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer,len);
+ return load_jpeg_image(x,y,comp,req_comp);
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_jpeg_test_file(FILE *f)
+{
+ int n,r;
+ n = ftell(f);
+ start_file(f);
+ r = decode_jpeg_header(SCAN_type);
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_jpeg_test_memory(stbi_uc const *buffer, int len)
+{
+ start_mem(buffer,len);
+ return decode_jpeg_header(SCAN_type);
+}
+
+// @TODO:
+#ifndef STBI_NO_STDIO
+extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+// public domain zlib decode v0.2 Sean Barrett 2006-11-18
+// simple implementation
+// - all input must be provided in an upfront buffer
+// - all output is written to a single output buffer (can malloc/realloc)
+// performance
+// - fast huffman
+
+// fast-way is faster to check than jpeg huffman, but slow way is slower
+#define ZFAST_BITS 9 // accelerate all cases in default tables
+#define ZFAST_MASK ((1 << ZFAST_BITS) - 1)
+
+// zlib-style huffman encoding
+// (jpegs packs from left, zlib from right, so can't share code)
+typedef struct
+{
+ uint16 fast[1 << ZFAST_BITS];
+ uint16 firstcode[16];
+ int maxcode[17];
+ uint16 firstsymbol[16];
+ uint8 size[288];
+ uint16 value[288];
+} zhuffman;
+
+__forceinline static int bitreverse16(int n)
+{
+ n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
+ n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
+ n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4);
+ n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8);
+ return n;
+}
+
+__forceinline static int bit_reverse(int v, int bits)
+{
+ assert(bits <= 16);
+ // to bit reverse n bits, reverse 16 and shift
+ // e.g. 11 bits, bit reverse and shift away 5
+ return bitreverse16(v) >> (16-bits);
+}
+
+static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num)
+{
+ int i,k=0;
+ int code, next_code[16], sizes[17];
+
+ // DEFLATE spec for generating codes
+ memset(sizes, 0, sizeof(sizes));
+ memset(z->fast, 255, sizeof(z->fast));
+ for (i=0; i < num; ++i)
+ ++sizes[sizelist[i]];
+ sizes[0] = 0;
+ for (i=1; i < 16; ++i)
+ assert(sizes[i] <= (1 << i));
+ code = 0;
+ for (i=1; i < 16; ++i) {
+ next_code[i] = code;
+ z->firstcode[i] = (uint16) code;
+ z->firstsymbol[i] = (uint16) k;
+ code = (code + sizes[i]);
+ if (sizes[i])
+ if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG");
+ z->maxcode[i] = code << (16-i); // preshift for inner loop
+ code <<= 1;
+ k += sizes[i];
+ }
+ z->maxcode[16] = 0x10000; // sentinel
+ for (i=0; i < num; ++i) {
+ int s = sizelist[i];
+ if (s) {
+ int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
+ z->size[c] = (uint8)s;
+ z->value[c] = (uint16)i;
+ if (s <= ZFAST_BITS) {
+ int k = bit_reverse(next_code[s],s);
+ while (k < (1 << ZFAST_BITS)) {
+ z->fast[k] = (uint16) c;
+ k += (1 << s);
+ }
+ }
+ ++next_code[s];
+ }
+ }
+ return 1;
+}
+
+// zlib-from-memory implementation for PNG reading
+// because PNG allows splitting the zlib stream arbitrarily,
+// and it's annoying structurally to have PNG call ZLIB call PNG,
+// we require PNG read all the IDATs and combine them into a single
+// memory buffer
+
+static uint8 *zbuffer, *zbuffer_end;
+
+__forceinline static int zget8(void)
+{
+ if (zbuffer >= zbuffer_end) return 0;
+ return *zbuffer++;
+}
+
+//static uint32 code_buffer;
+static int num_bits;
+
+static void fill_bits(void)
+{
+ do {
+ assert(code_buffer < (1U << num_bits));
+ code_buffer |= zget8() << num_bits;
+ num_bits += 8;
+ } while (num_bits <= 24);
+}
+
+__forceinline static unsigned int zreceive(int n)
+{
+ unsigned int k;
+ if (num_bits < n) fill_bits();
+ k = code_buffer & ((1 << n) - 1);
+ code_buffer >>= n;
+ num_bits -= n;
+ return k;
+}
+
+__forceinline static int zhuffman_decode(zhuffman *z)
+{
+ int b,s,k;
+ if (num_bits < 16) fill_bits();
+ b = z->fast[code_buffer & ZFAST_MASK];
+ if (b < 0xffff) {
+ s = z->size[b];
+ code_buffer >>= s;
+ num_bits -= s;
+ return z->value[b];
+ }
+
+ // not resolved by fast table, so compute it the slow way
+ // use jpeg approach, which requires MSbits at top
+ k = bit_reverse(code_buffer, 16);
+ for (s=ZFAST_BITS+1; ; ++s)
+ if (k < z->maxcode[s])
+ break;
+ if (s == 16) return -1; // invalid code!
+ // code size is s, so:
+ b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
+ assert(z->size[b] == s);
+ code_buffer >>= s;
+ num_bits -= s;
+ return z->value[b];
+}
+
+static char *zout;
+static char *zout_start;
+static char *zout_end;
+static int z_expandable;
+
+static int expand(int n) // need to make room for n bytes
+{
+ char *q;
+ int cur, limit;
+ if (!z_expandable) return e("output buffer limit","Corrupt PNG");
+ cur = (int) (zout - zout_start);
+ limit = (int) (zout_end - zout_start);
+ while (cur + n > limit)
+ limit *= 2;
+ q = (char *) realloc(zout_start, limit);
+ if (q == NULL) return e("outofmem", "Out of memory");
+ zout_start = q;
+ zout = q + cur;
+ zout_end = q + limit;
+ return 1;
+}
+
+static zhuffman z_length, z_distance;
+
+static int length_base[31] = {
+ 3,4,5,6,7,8,9,10,11,13,
+ 15,17,19,23,27,31,35,43,51,59,
+ 67,83,99,115,131,163,195,227,258,0,0 };
+
+static int length_extra[31]=
+{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
+
+static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
+257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
+
+static int dist_extra[32] =
+{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+static int parse_huffman_block(void)
+{
+ for(;;) {
+ int z = zhuffman_decode(&z_length);
+ if (z < 256) {
+ if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes
+ if (zout >= zout_end) if (!expand(1)) return 0;
+ *zout++ = (char) z;
+ } else {
+ uint8 *p;
+ int len,dist;
+ if (z == 256) return 1;
+ z -= 257;
+ len = length_base[z];
+ if (length_extra[z]) len += zreceive(length_extra[z]);
+ z = zhuffman_decode(&z_distance);
+ if (z < 0) return e("bad huffman code","Corrupt PNG");
+ dist = dist_base[z];
+ if (dist_extra[z]) dist += zreceive(dist_extra[z]);
+ if (zout - zout_start < dist) return e("bad dist","Corrupt PNG");
+ if (zout + len > zout_end) if (!expand(len)) return 0;
+ p = (uint8 *) (zout - dist);
+ while (len--)
+ *zout++ = *p++;
+ }
+ }
+}
+
+static int compute_huffman_codes(void)
+{
+ static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
+ static zhuffman z_codelength; // static just to save stack space
+ uint8 lencodes[286+32+137];//padding for maximum single op
+ uint8 codelength_sizes[19];
+ int i,n;
+
+ int hlit = zreceive(5) + 257;
+ int hdist = zreceive(5) + 1;
+ int hclen = zreceive(4) + 4;
+
+ memset(codelength_sizes, 0, sizeof(codelength_sizes));
+ for (i=0; i < hclen; ++i) {
+ int s = zreceive(3);
+ codelength_sizes[length_dezigzag[i]] = (uint8) s;
+ }
+ if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;
+
+ n = 0;
+ while (n < hlit + hdist) {
+ int c = zhuffman_decode(&z_codelength);
+ assert(c >= 0 && c < 19);
+ if (c < 16)
+ lencodes[n++] = (uint8) c;
+ else if (c == 16) {
+ c = zreceive(2)+3;
+ memset(lencodes+n, lencodes[n-1], c);
+ n += c;
+ } else if (c == 17) {
+ c = zreceive(3)+3;
+ memset(lencodes+n, 0, c);
+ n += c;
+ } else {
+ assert(c == 18);
+ c = zreceive(7)+11;
+ memset(lencodes+n, 0, c);
+ n += c;
+ }
+ }
+ if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG");
+ if (!zbuild_huffman(&z_length, lencodes, hlit)) return 0;
+ if (!zbuild_huffman(&z_distance, lencodes+hlit, hdist)) return 0;
+ return 1;
+}
+
+static int parse_uncompressed_block(void)
+{
+ uint8 header[4];
+ int len,nlen,k;
+ if (num_bits & 7)
+ zreceive(num_bits & 7); // discard
+ // drain the bit-packed data into header
+ k = 0;
+ while (num_bits > 0) {
+ header[k++] = (uint8) (code_buffer & 255); // wtf this warns?
+ code_buffer >>= 8;
+ num_bits -= 8;
+ }
+ assert(num_bits == 0);
+ // now fill header the normal way
+ while (k < 4)
+ header[k++] = (uint8) zget8();
+ len = header[1] * 256 + header[0];
+ nlen = header[3] * 256 + header[2];
+ if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG");
+ if (zbuffer + len > zbuffer_end) return e("read past buffer","Corrupt PNG");
+ if (zout + len > zout_end)
+ if (!expand(len)) return 0;
+ memcpy(zout, zbuffer, len);
+ zbuffer += len;
+ zout += len;
+ return 1;
+}
+
+static int parse_zlib_header(void)
+{
+ int cmf = zget8();
+ int cm = cmf & 15;
+ /* int cinfo = cmf >> 4; */
+ int flg = zget8();
+ if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec
+ if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png
+ if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png
+ // window = 1 << (8 + cinfo)... but who cares, we fully buffer output
+ return 1;
+}
+
+static uint8 default_length[288], default_distance[32];
+static void init_defaults(void)
+{
+ int i; // use <= to match clearly with spec
+ for (i=0; i <= 143; ++i) default_length[i] = 8;
+ for ( ; i <= 255; ++i) default_length[i] = 9;
+ for ( ; i <= 279; ++i) default_length[i] = 7;
+ for ( ; i <= 287; ++i) default_length[i] = 8;
+
+ for (i=0; i <= 31; ++i) default_distance[i] = 5;
+}
+
+static int parse_zlib(int parse_header)
+{
+ int final, type;
+ if (parse_header)
+ if (!parse_zlib_header()) return 0;
+ num_bits = 0;
+ code_buffer = 0;
+ do {
+ final = zreceive(1);
+ type = zreceive(2);
+ if (type == 0) {
+ if (!parse_uncompressed_block()) return 0;
+ } else if (type == 3) {
+ return 0;
+ } else {
+ if (type == 1) {
+ // use fixed code lengths
+ if (!default_length[0]) init_defaults();
+ if (!zbuild_huffman(&z_length , default_length , 288)) return 0;
+ if (!zbuild_huffman(&z_distance, default_distance, 32)) return 0;
+ } else {
+ if (!compute_huffman_codes()) return 0;
+ }
+ if (!parse_huffman_block()) return 0;
+ }
+ } while (!final);
+ return 1;
+}
+
+static int do_zlib(char *obuf, int olen, int exp, int parse_header)
+{
+ zout_start = obuf;
+ zout = obuf;
+ zout_end = obuf + olen;
+ z_expandable = exp;
+
+ return parse_zlib(parse_header);
+}
+
+char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen)
+{
+ char *p = (char *) malloc(initial_size);
+ if (p == NULL) return NULL;
+ if (do_zlib(p, initial_size, 1, 1)) {
+ *outlen = (int) (zout - zout_start);
+ return zout_start;
+ } else {
+ free(zout_start);
+ return NULL;
+ }
+}
+
+char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)
+{
+ zbuffer = (uint8 *) buffer;
+ zbuffer_end = (uint8 *) buffer+len;
+ return stbi_zlib_decode_malloc_guesssize(16384, outlen);
+}
+
+int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)
+{
+ zbuffer = (uint8 *) ibuffer;
+ zbuffer_end = (uint8 *) ibuffer + ilen;
+ if (do_zlib(obuffer, olen, 0, 1))
+ return (int) (zout - zout_start);
+ else
+ return -1;
+}
+
+char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)
+{
+ char *p = (char *) malloc(16384);
+ if (p == NULL) return NULL;
+ zbuffer = (uint8 *) buffer;
+ zbuffer_end = (uint8 *) buffer+len;
+ if (do_zlib(p, 16384, 1, 0)) {
+ *outlen = (int) (zout - zout_start);
+ return zout_start;
+ } else {
+ free(zout_start);
+ return NULL;
+ }
+}
+
+int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)
+{
+ zbuffer = (uint8 *) ibuffer;
+ zbuffer_end = (uint8 *) ibuffer + ilen;
+ if (do_zlib(obuffer, olen, 0, 0))
+ return (int) (zout - zout_start);
+ else
+ return -1;
+}
+
+// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18
+// simple implementation
+// - only 8-bit samples
+// - no CRC checking
+// - allocates lots of intermediate memory
+// - avoids problem of streaming data between subsystems
+// - avoids explicit window management
+// performance
+// - uses stb_zlib, a PD zlib implementation with fast huffman decoding
+
+
+typedef struct
+{
+ uint32 length;
+ uint32 type;
+} chunk;
+
+#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
+
+static chunk get_chunk_header(void)
+{
+ chunk c;
+ c.length = get32();
+ c.type = get32();
+ return c;
+}
+
+static int check_png_header(void)
+{
+ static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 };
+ int i;
+ for (i=0; i < 8; ++i)
+ if (get8() != png_sig[i]) return e("bad png sig","Not a PNG");
+ return 1;
+}
+
+static uint8 *idata, *expanded, *out;
+
+enum {
+ F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4,
+ F_avg_first, F_paeth_first,
+};
+
+static uint8 first_row_filter[5] =
+{
+ F_none, F_sub, F_none, F_avg_first, F_paeth_first
+};
+
+static int paeth(int a, int b, int c)
+{
+ int p = a + b - c;
+ int pa = abs(p-a);
+ int pb = abs(p-b);
+ int pc = abs(p-c);
+ if (pa <= pb && pa <= pc) return a;
+ if (pb <= pc) return b;
+ return c;
+}
+
+// create the png data from post-deflated data
+static int create_png_image(uint8 *raw, uint32 raw_len, int out_n)
+{
+ uint32 i,j,stride = img_x*out_n;
+ int k;
+ assert(out_n == img_n || out_n == img_n+1);
+ out = (uint8 *) malloc(img_x * img_y * out_n);
+ if (!out) return e("outofmem", "Out of memory");
+ if (raw_len != (img_n * img_x + 1) * img_y) return e("not enough pixels","Corrupt PNG");
+ for (j=0; j < img_y; ++j) {
+ uint8 *cur = out + stride*j;
+ uint8 *prior = cur - stride;
+ int filter = *raw++;
+ if (filter > 4) return e("invalid filter","Corrupt PNG");
+ // if first row, use special filter that doesn't sample previous row
+ if (j == 0) filter = first_row_filter[filter];
+ // handle first pixel explicitly
+ for (k=0; k < img_n; ++k) {
+ switch(filter) {
+ case F_none : cur[k] = raw[k]; break;
+ case F_sub : cur[k] = raw[k]; break;
+ case F_up : cur[k] = raw[k] + prior[k]; break;
+ case F_avg : cur[k] = raw[k] + (prior[k]>>1); break;
+ case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break;
+ case F_avg_first : cur[k] = raw[k]; break;
+ case F_paeth_first: cur[k] = raw[k]; break;
+ }
+ }
+ if (img_n != out_n) cur[img_n] = 255;
+ raw += img_n;
+ cur += out_n;
+ prior += out_n;
+ // this is a little gross, so that we don't switch per-pixel or per-component
+ if (img_n == out_n) {
+ #define CASE(f) \
+ case f: \
+ for (i=1; i < img_x; ++i, raw+=img_n,cur+=img_n,prior+=img_n) \
+ for (k=0; k < img_n; ++k)
+ switch(filter) {
+ CASE(F_none) cur[k] = raw[k]; break;
+ CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break;
+ CASE(F_up) cur[k] = raw[k] + prior[k]; break;
+ CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break;
+ CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break;
+ CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break;
+ CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break;
+ }
+ #undef CASE
+ } else {
+ assert(img_n+1 == out_n);
+ #define CASE(f) \
+ case f: \
+ for (i=1; i < img_x; ++i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \
+ for (k=0; k < img_n; ++k)
+ switch(filter) {
+ CASE(F_none) cur[k] = raw[k]; break;
+ CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break;
+ CASE(F_up) cur[k] = raw[k] + prior[k]; break;
+ CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break;
+ CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break;
+ CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break;
+ CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break;
+ }
+ #undef CASE
+ }
+ }
+ return 1;
+}
+
+static int compute_transparency(uint8 tc[3], int out_n)
+{
+ uint32 i, pixel_count = img_x * img_y;
+ uint8 *p = out;
+
+ // compute color-based transparency, assuming we've
+ // already got 255 as the alpha value in the output
+ assert(out_n == 2 || out_n == 4);
+
+ p = out;
+ if (out_n == 2) {
+ for (i=0; i < pixel_count; ++i) {
+ p[1] = (p[0] == tc[0] ? 0 : 255);
+ p += 2;
+ }
+ } else {
+ for (i=0; i < pixel_count; ++i) {
+ if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
+ p[3] = 0;
+ p += 4;
+ }
+ }
+ return 1;
+}
+
+static int expand_palette(uint8 *palette, int len, int pal_img_n)
+{
+ uint32 i, pixel_count = img_x * img_y;
+ uint8 *p, *temp_out, *orig = out;
+
+ p = (uint8 *) malloc(pixel_count * pal_img_n);
+ if (p == NULL) return e("outofmem", "Out of memory");
+
+ // between here and free(out) below, exitting would leak
+ temp_out = p;
+
+ if (pal_img_n == 3) {
+ for (i=0; i < pixel_count; ++i) {
+ int n = orig[i]*4;
+ p[0] = palette[n ];
+ p[1] = palette[n+1];
+ p[2] = palette[n+2];
+ p += 3;
+ }
+ } else {
+ for (i=0; i < pixel_count; ++i) {
+ int n = orig[i]*4;
+ p[0] = palette[n ];
+ p[1] = palette[n+1];
+ p[2] = palette[n+2];
+ p[3] = palette[n+3];
+ p += 4;
+ }
+ }
+ free(out);
+ out = temp_out;
+ return 1;
+}
+
+static int parse_png_file(int scan, int req_comp)
+{
+ uint8 palette[1024], pal_img_n=0;
+ uint8 has_trans=0, tc[3];
+ uint32 ioff=0, idata_limit=0, i, pal_len=0;
+ int first=1,k;
+
+ if (!check_png_header()) return 0;
+
+ if (scan == SCAN_type) return 1;
+
+ for(;;first=0) {
+ chunk c = get_chunk_header();
+ if (first && c.type != PNG_TYPE('I','H','D','R'))
+ return e("first not IHDR","Corrupt PNG");
+ switch (c.type) {
+ case PNG_TYPE('I','H','D','R'): {
+ int depth,color,interlace,comp,filter;
+ if (!first) return e("multiple IHDR","Corrupt PNG");
+ if (c.length != 13) return e("bad IHDR len","Corrupt PNG");
+ img_x = get32(); if (img_x > (1 << 24)) return e("too large","Very large image (corrupt?)");
+ img_y = get32(); if (img_y > (1 << 24)) return e("too large","Very large image (corrupt?)");
+ depth = get8(); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only");
+ color = get8(); if (color > 6) return e("bad ctype","Corrupt PNG");
+ if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG");
+ comp = get8(); if (comp) return e("bad comp method","Corrupt PNG");
+ filter= get8(); if (filter) return e("bad filter method","Corrupt PNG");
+ interlace = get8(); if (interlace) return e("interlaced","PNG not supported: interlaced mode");
+ if (!img_x || !img_y) return e("0-pixel image","Corrupt PNG");
+ if (!pal_img_n) {
+ img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
+ if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode");
+ if (scan == SCAN_header) return 1;
+ } else {
+ // if paletted, then pal_n is our final components, and
+ // img_n is # components to decompress/filter.
+ img_n = 1;
+ if ((1 << 30) / img_x / 4 < img_y) return e("too large","Corrupt PNG");
+ // if SCAN_header, have to scan to see if we have a tRNS
+ }
+ break;
+ }
+
+ case PNG_TYPE('P','L','T','E'): {
+ if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG");
+ pal_len = c.length / 3;
+ if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG");
+ for (i=0; i < pal_len; ++i) {
+ palette[i*4+0] = get8u();
+ palette[i*4+1] = get8u();
+ palette[i*4+2] = get8u();
+ palette[i*4+3] = 255;
+ }
+ break;
+ }
+
+ case PNG_TYPE('t','R','N','S'): {
+ if (idata) return e("tRNS after IDAT","Corrupt PNG");
+ if (pal_img_n) {
+ if (scan == SCAN_header) { img_n = 4; return 1; }
+ if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG");
+ if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG");
+ pal_img_n = 4;
+ for (i=0; i < c.length; ++i)
+ palette[i*4+3] = get8u();
+ } else {
+ if (!(img_n & 1)) return e("tRNS with alpha","Corrupt PNG");
+ if (c.length != (uint32) img_n*2) return e("bad tRNS len","Corrupt PNG");
+ has_trans = 1;
+ for (k=0; k < img_n; ++k)
+ tc[k] = (uint8) get16(); // non 8-bit images will be larger
+ }
+ break;
+ }
+
+ case PNG_TYPE('I','D','A','T'): {
+ if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG");
+ if (scan == SCAN_header) { img_n = pal_img_n; return 1; }
+ if (ioff + c.length > idata_limit) {
+ uint8 *p;
+ if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
+ while (ioff + c.length > idata_limit)
+ idata_limit *= 2;
+ p = (uint8 *) realloc(idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory");
+ idata = p;
+ }
+ #ifndef STBI_NO_STDIO
+ if (img_file)
+ {
+ if (fread(idata+ioff,1,c.length,img_file) != c.length) return e("outofdata","Corrupt PNG");
+ }
+ else
+ #endif
+ {
+ memcpy(idata+ioff, img_buffer, c.length);
+ img_buffer += c.length;
+ }
+ ioff += c.length;
+ break;
+ }
+
+ case PNG_TYPE('I','E','N','D'): {
+ uint32 raw_len;
+ if (scan != SCAN_load) return 1;
+ if (idata == NULL) return e("no IDAT","Corrupt PNG");
+ expanded = (uint8 *) stbi_zlib_decode_malloc((char *) idata, ioff, (int *) &raw_len);
+ if (expanded == NULL) return 0; // zlib should set error
+ free(idata); idata = NULL;
+ if ((req_comp == img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
+ img_out_n = img_n+1;
+ else
+ img_out_n = img_n;
+ if (!create_png_image(expanded, raw_len, img_out_n)) return 0;
+ if (has_trans)
+ if (!compute_transparency(tc, img_out_n)) return 0;
+ if (pal_img_n) {
+ // pal_img_n == 3 or 4
+ img_n = pal_img_n; // record the actual colors we had
+ img_out_n = pal_img_n;
+ if (req_comp >= 3) img_out_n = req_comp;
+ if (!expand_palette(palette, pal_len, img_out_n))
+ return 0;
+ }
+ free(expanded); expanded = NULL;
+ return 1;
+ }
+
+ default:
+ // if critical, fail
+ if ((c.type & (1 << 29)) == 0) {
+ #ifndef STBI_NO_FAILURE_STRINGS
+ static char invalid_chunk[] = "XXXX chunk not known";
+ invalid_chunk[0] = (uint8) (c.type >> 24);
+ invalid_chunk[1] = (uint8) (c.type >> 16);
+ invalid_chunk[2] = (uint8) (c.type >> 8);
+ invalid_chunk[3] = (uint8) (c.type >> 0);
+ #endif
+ return e(invalid_chunk, "PNG not supported: unknown chunk type");
+ }
+ skip(c.length);
+ break;
+ }
+ // end of chunk, read and skip CRC
+ get8(); get8(); get8(); get8();
+ }
+}
+
+static unsigned char *do_png(int *x, int *y, int *n, int req_comp)
+{
+ unsigned char *result=NULL;
+ if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error");
+ if (parse_png_file(SCAN_load, req_comp)) {
+ result = out;
+ out = NULL;
+ if (req_comp && req_comp != img_out_n) {
+ result = convert_format(result, img_out_n, req_comp);
+ if (result == NULL) return result;
+ }
+ *x = img_x;
+ *y = img_y;
+ if (n) *n = img_n;
+ }
+ free(out); out = NULL;
+ free(expanded); expanded = NULL;
+ free(idata); idata = NULL;
+
+ return result;
+}
+
+#ifndef STBI_NO_STDIO
+unsigned char *stbi_png_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return do_png(x,y,comp,req_comp);
+}
+
+unsigned char *stbi_png_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_png_load_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+#endif
+
+unsigned char *stbi_png_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer,len);
+ return do_png(x,y,comp,req_comp);
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_png_test_file(FILE *f)
+{
+ int n,r;
+ n = ftell(f);
+ start_file(f);
+ r = parse_png_file(SCAN_type,STBI_default);
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_png_test_memory(stbi_uc const *buffer, int len)
+{
+ start_mem(buffer, len);
+ return parse_png_file(SCAN_type,STBI_default);
+}
+
+// TODO: load header from png
+#ifndef STBI_NO_STDIO
+extern int stbi_png_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+// Microsoft/Windows BMP image
+
+static int bmp_test(void)
+{
+ int sz;
+ if (get8() != 'B') return 0;
+ if (get8() != 'M') return 0;
+ get32le(); // discard filesize
+ get16le(); // discard reserved
+ get16le(); // discard reserved
+ get32le(); // discard data offset
+ sz = get32le();
+ if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1;
+ return 0;
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_bmp_test_file (FILE *f)
+{
+ int r,n = ftell(f);
+ start_file(f);
+ r = bmp_test();
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_bmp_test_memory (stbi_uc const *buffer, int len)
+{
+ start_mem(buffer, len);
+ return bmp_test();
+}
+
+// returns 0..31 for the highest set bit
+static int high_bit(unsigned int z)
+{
+ int n=0;
+ if (z == 0) return -1;
+ if (z >= 0x10000) n += 16, z >>= 16;
+ if (z >= 0x00100) n += 8, z >>= 8;
+ if (z >= 0x00010) n += 4, z >>= 4;
+ if (z >= 0x00004) n += 2, z >>= 2;
+ if (z >= 0x00002) n += 1, z >>= 1;
+ return n;
+}
+
+static int bitcount(unsigned int a)
+{
+ a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
+ a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
+ a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
+ a = (a + (a >> 8)); // max 16 per 8 bits
+ a = (a + (a >> 16)); // max 32 per 8 bits
+ return a & 0xff;
+}
+
+static int shiftsigned(int v, int shift, int bits)
+{
+ int result;
+ int z=0;
+
+ if (shift < 0) v <<= -shift;
+ else v >>= shift;
+ result = v;
+
+ z = bits;
+ while (z < 8) {
+ result += v >> z;
+ z += bits;
+ }
+ return result;
+}
+
+static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp)
+{
+ unsigned int mr=0,mg=0,mb=0,ma=0;
+ stbi_uc pal[256][4];
+ int psize=0,i,j,compress=0,width;
+ int bpp, flip_vertically, pad, target, offset, hsz;
+ if (get8() != 'B' || get8() != 'M') return epuc("not BMP", "Corrupt BMP");
+ get32le(); // discard filesize
+ get16le(); // discard reserved
+ get16le(); // discard reserved
+ offset = get32le();
+ hsz = get32le();
+ if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown");
+ failure_reason = "bad BMP";
+ if (hsz == 12) {
+ img_x = get16le();
+ img_y = get16le();
+ } else {
+ img_x = get32le();
+ img_y = get32le();
+ }
+ if (get16le() != 1) return 0;
+ bpp = get16le();
+ if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit");
+ flip_vertically = ((int) img_y) > 0;
+ img_y = abs((int) img_y);
+ if (hsz == 12) {
+ if (bpp < 24)
+ psize = (offset - 14 - 24) / 3;
+ } else {
+ compress = get32le();
+ if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE");
+ get32le(); // discard sizeof
+ get32le(); // discard hres
+ get32le(); // discard vres
+ get32le(); // discard colorsused
+ get32le(); // discard max important
+ if (hsz == 40 || hsz == 56) {
+ if (hsz == 56) {
+ get32le();
+ get32le();
+ get32le();
+ get32le();
+ }
+ if (bpp == 16 || bpp == 32) {
+ mr = mg = mb = 0;
+ if (compress == 0) {
+ if (bpp == 32) {
+ mr = 0xff << 16;
+ mg = 0xff << 8;
+ mb = 0xff << 0;
+ } else {
+ mr = 31 << 10;
+ mg = 31 << 5;
+ mb = 31 << 0;
+ }
+ } else if (compress == 3) {
+ mr = get32le();
+ mg = get32le();
+ mb = get32le();
+ // not documented, but generated by photoshop and handled by mspaint
+ if (mr == mg && mg == mb) {
+ // ?!?!?
+ return NULL;
+ }
+ } else
+ return NULL;
+ }
+ } else {
+ assert(hsz == 108);
+ mr = get32le();
+ mg = get32le();
+ mb = get32le();
+ ma = get32le();
+ get32le(); // discard color space
+ for (i=0; i < 12; ++i)
+ get32le(); // discard color space parameters
+ }
+ if (bpp < 16)
+ psize = (offset - 14 - hsz) >> 2;
+ }
+ img_n = ma ? 4 : 3;
+ if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
+ target = req_comp;
+ else
+ target = img_n; // if they want monochrome, we'll post-convert
+ out = (stbi_uc *) malloc(target * img_x * img_y);
+ if (!out) return epuc("outofmem", "Out of memory");
+ if (bpp < 16) {
+ int z=0;
+ if (psize == 0 || psize > 256) return epuc("invalid", "Corrupt BMP");
+ for (i=0; i < psize; ++i) {
+ pal[i][2] = get8();
+ pal[i][1] = get8();
+ pal[i][0] = get8();
+ if (hsz != 12) get8();
+ pal[i][3] = 255;
+ }
+ skip(offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4));
+ if (bpp == 4) width = (img_x + 1) >> 1;
+ else if (bpp == 8) width = img_x;
+ else return epuc("bad bpp", "Corrupt BMP");
+ pad = (-width)&3;
+ for (j=0; j < (int) img_y; ++j) {
+ for (i=0; i < (int) img_x; i += 2) {
+ int v=get8(),v2=0;
+ if (bpp == 4) {
+ v2 = v & 15;
+ v >>= 4;
+ }
+ out[z++] = pal[v][0];
+ out[z++] = pal[v][1];
+ out[z++] = pal[v][2];
+ if (target == 4) out[z++] = 255;
+ if (i+1 == (int) img_x) break;
+ v = (bpp == 8) ? get8() : v2;
+ out[z++] = pal[v][0];
+ out[z++] = pal[v][1];
+ out[z++] = pal[v][2];
+ if (target == 4) out[z++] = 255;
+ }
+ skip(pad);
+ }
+ } else {
+ int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
+ int z = 0;
+ int easy=0;
+ skip(offset - 14 - hsz);
+ if (bpp == 24) width = 3 * img_x;
+ else if (bpp == 16) width = 2*img_x;
+ else /* bpp = 32 and pad = 0 */ width=0;
+ pad = (-width) & 3;
+ if (bpp == 24) {
+ easy = 1;
+ } else if (bpp == 32) {
+ if (mb == 0xff && mg == 0xff00 && mr == 0xff000000 && ma == 0xff000000)
+ easy = 2;
+ }
+ if (!easy) {
+ if (!mr || !mg || !mb) return epuc("bad masks", "Corrupt BMP");
+ // right shift amt to put high bit in position #7
+ rshift = high_bit(mr)-7; rcount = bitcount(mr);
+ gshift = high_bit(mg)-7; gcount = bitcount(mr);
+ bshift = high_bit(mb)-7; bcount = bitcount(mr);
+ ashift = high_bit(ma)-7; acount = bitcount(mr);
+ }
+ for (j=0; j < (int) img_y; ++j) {
+ if (easy) {
+ for (i=0; i < (int) img_x; ++i) {
+ int a;
+ out[z+2] = get8();
+ out[z+1] = get8();
+ out[z+0] = get8();
+ z += 3;
+ a = (easy == 2 ? get8() : 255);
+ if (target == 4) out[z++] = a;
+ }
+ } else {
+ for (i=0; i < (int) img_x; ++i) {
+ uint32 v = (bpp == 16 ? get16le() : get32le());
+ int a;
+ out[z++] = shiftsigned(v & mr, rshift, rcount);
+ out[z++] = shiftsigned(v & mg, gshift, gcount);
+ out[z++] = shiftsigned(v & mb, bshift, bcount);
+ a = (ma ? shiftsigned(v & ma, ashift, acount) : 255);
+ if (target == 4) out[z++] = a;
+ }
+ }
+ skip(pad);
+ }
+ }
+ if (flip_vertically) {
+ stbi_uc t;
+ for (j=0; j < (int) img_y>>1; ++j) {
+ stbi_uc *p1 = out + j *img_x*target;
+ stbi_uc *p2 = out + (img_y-1-j)*img_x*target;
+ for (i=0; i < (int) img_x*target; ++i) {
+ t = p1[i], p1[i] = p2[i], p2[i] = t;
+ }
+ }
+ }
+
+ if (req_comp && req_comp != target) {
+ out = convert_format(out, target, req_comp);
+ if (out == NULL) return out; // convert_format frees input on failure
+ }
+
+ *x = img_x;
+ *y = img_y;
+ if (comp) *comp = target;
+ return out;
+}
+
+#ifndef STBI_NO_STDIO
+stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_bmp_load_from_file(f, x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+
+stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return bmp_load(x,y,comp,req_comp);
+}
+#endif
+
+stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer, len);
+ return bmp_load(x,y,comp,req_comp);
+}
+
+// Targa Truevision - TGA
+// by Jonathan Dummer
+
+static int tga_test(void)
+{
+ int sz;
+ get8u(); // discard Offset
+ sz = get8u(); // color type
+ if( sz > 1 ) return 0; // only RGB or indexed allowed
+ sz = get8u(); // image type
+ if( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE
+ get16(); // discard palette start
+ get16(); // discard palette length
+ get8(); // discard bits per palette color entry
+ get16(); // discard x origin
+ get16(); // discard y origin
+ if( get16() < 1 ) return 0; // test width
+ if( get16() < 1 ) return 0; // test height
+ sz = get8(); // bits per pixel
+ if( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed
+ return 1; // seems to have passed everything
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_tga_test_file (FILE *f)
+{
+ int r,n = ftell(f);
+ start_file(f);
+ r = tga_test();
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_tga_test_memory (stbi_uc const *buffer, int len)
+{
+ start_mem(buffer, len);
+ return tga_test();
+}
+
+static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp)
+{
+ // read in the TGA header stuff
+ int tga_offset = get8u();
+ int tga_indexed = get8u();
+ int tga_image_type = get8u();
+ int tga_is_RLE = 0;
+ int tga_palette_start = get16le();
+ int tga_palette_len = get16le();
+ int tga_palette_bits = get8u();
+ int tga_x_origin = get16le();
+ int tga_y_origin = get16le();
+ int tga_width = get16le();
+ int tga_height = get16le();
+ int tga_bits_per_pixel = get8u();
+ int tga_inverted = get8u();
+ // image data
+ unsigned char *tga_data;
+ unsigned char *tga_palette = NULL;
+ int i, j;
+ unsigned char raw_data[4];
+ unsigned char trans_data[4];
+ int RLE_count = 0;
+ int RLE_repeating = 0;
+ int read_next_pixel = 1;
+ // do a tiny bit of precessing
+ if( tga_image_type >= 8 )
+ {
+ tga_image_type -= 8;
+ tga_is_RLE = 1;
+ }
+ /* int tga_alpha_bits = tga_inverted & 15; */
+ tga_inverted = 1 - ((tga_inverted >> 5) & 1);
+
+ // error check
+ if( //(tga_indexed) ||
+ (tga_width < 1) || (tga_height < 1) ||
+ (tga_image_type < 1) || (tga_image_type > 3) ||
+ ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) &&
+ (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32))
+ )
+ {
+ return NULL;
+ }
+
+ // If I'm paletted, then I'll use the number of bits from the palette
+ if( tga_indexed )
+ {
+ tga_bits_per_pixel = tga_palette_bits;
+ }
+
+ // tga info
+ *x = tga_width;
+ *y = tga_height;
+ if( (req_comp < 1) || (req_comp > 4) )
+ {
+ // just use whatever the file was
+ req_comp = tga_bits_per_pixel / 8;
+ *comp = req_comp;
+ } else
+ {
+ // force a new number of components
+ *comp = tga_bits_per_pixel/8;
+ }
+ tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp );
+
+ // skip to the data's starting position (offset usually = 0)
+ skip( tga_offset );
+ // do I need to load a palette?
+ if( tga_indexed )
+ {
+ // any data to skip? (offset usually = 0)
+ skip( tga_palette_start );
+ // load the palette
+ tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 );
+ getn( tga_palette, tga_palette_len * tga_palette_bits / 8 );
+ }
+ // load the data
+ for( i = 0; i < tga_width * tga_height; ++i )
+ {
+ // if I'm in RLE mode, do I need to get a RLE chunk?
+ if( tga_is_RLE )
+ {
+ if( RLE_count == 0 )
+ {
+ // yep, get the next byte as a RLE command
+ int RLE_cmd = get8u();
+ RLE_count = 1 + (RLE_cmd & 127);
+ RLE_repeating = RLE_cmd >> 7;
+ read_next_pixel = 1;
+ } else if( !RLE_repeating )
+ {
+ read_next_pixel = 1;
+ }
+ } else
+ {
+ read_next_pixel = 1;
+ }
+ // OK, if I need to read a pixel, do it now
+ if( read_next_pixel )
+ {
+ // load however much data we did have
+ if( tga_indexed )
+ {
+ // read in 1 byte, then perform the lookup
+ int pal_idx = get8u();
+ if( pal_idx >= tga_palette_len )
+ {
+ // invalid index
+ pal_idx = 0;
+ }
+ pal_idx *= tga_bits_per_pixel / 8;
+ for( j = 0; j*8 < tga_bits_per_pixel; ++j )
+ {
+ raw_data[j] = tga_palette[pal_idx+j];
+ }
+ } else
+ {
+ // read in the data raw
+ for( j = 0; j*8 < tga_bits_per_pixel; ++j )
+ {
+ raw_data[j] = get8u();
+ }
+ }
+ // convert raw to the intermediate format
+ switch( tga_bits_per_pixel )
+ {
+ case 8:
+ // Luminous => RGBA
+ trans_data[0] = raw_data[0];
+ trans_data[1] = raw_data[0];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = 255;
+ break;
+ case 16:
+ // Luminous,Alpha => RGBA
+ trans_data[0] = raw_data[0];
+ trans_data[1] = raw_data[0];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = raw_data[1];
+ break;
+ case 24:
+ // BGR => RGBA
+ trans_data[0] = raw_data[2];
+ trans_data[1] = raw_data[1];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = 255;
+ break;
+ case 32:
+ // BGRA => RGBA
+ trans_data[0] = raw_data[2];
+ trans_data[1] = raw_data[1];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = raw_data[3];
+ break;
+ }
+ // clear the reading flag for the next pixel
+ read_next_pixel = 0;
+ } // end of reading a pixel
+ // convert to final format
+ switch( req_comp )
+ {
+ case 1:
+ // RGBA => Luminance
+ tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]);
+ break;
+ case 2:
+ // RGBA => Luminance,Alpha
+ tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]);
+ tga_data[i*req_comp+1] = trans_data[3];
+ break;
+ case 3:
+ // RGBA => RGB
+ tga_data[i*req_comp+0] = trans_data[0];
+ tga_data[i*req_comp+1] = trans_data[1];
+ tga_data[i*req_comp+2] = trans_data[2];
+ break;
+ case 4:
+ // RGBA => RGBA
+ tga_data[i*req_comp+0] = trans_data[0];
+ tga_data[i*req_comp+1] = trans_data[1];
+ tga_data[i*req_comp+2] = trans_data[2];
+ tga_data[i*req_comp+3] = trans_data[3];
+ break;
+ }
+ // in case we're in RLE mode, keep counting down
+ --RLE_count;
+ }
+ // do I need to invert the image?
+ if( tga_inverted )
+ {
+ for( j = 0; j*2 < tga_height; ++j )
+ {
+ int index1 = j * tga_width * req_comp;
+ int index2 = (tga_height - 1 - j) * tga_width * req_comp;
+ for( i = tga_width * req_comp; i > 0; --i )
+ {
+ unsigned char temp = tga_data[index1];
+ tga_data[index1] = tga_data[index2];
+ tga_data[index2] = temp;
+ ++index1;
+ ++index2;
+ }
+ }
+ }
+ // clear my palette, if I had one
+ if( tga_palette != NULL )
+ {
+ free( tga_palette );
+ }
+ // the things I do to get rid of an error message, and yet keep
+ // Microsoft's C compilers happy... [8^(
+ tga_palette_start = tga_palette_len = tga_palette_bits =
+ tga_x_origin = tga_y_origin = 0;
+ // OK, done
+ return tga_data;
+}
+
+#ifndef STBI_NO_STDIO
+stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_tga_load_from_file(f, x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+
+stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return tga_load(x,y,comp,req_comp);
+}
+#endif
+
+stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer, len);
+ return tga_load(x,y,comp,req_comp);
+}
+
+
+// *************************************************************************************************
+// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicholas Schulz, tweaked by STB
+
+static int psd_test(void)
+{
+ if (get32() != 0x38425053) return 0; // "8BPS"
+ else return 1;
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_psd_test_file(FILE *f)
+{
+ int r,n = ftell(f);
+ start_file(f);
+ r = psd_test();
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_psd_test_memory(stbi_uc const *buffer, int len)
+{
+ start_mem(buffer, len);
+ return psd_test();
+}
+
+static stbi_uc *psd_load(int *x, int *y, int *comp, int req_comp)
+{
+ int pixelCount;
+ int channelCount, compression;
+ int channel, i, count, len;
+ int w,h;
+
+ // Check identifier
+ if (get32() != 0x38425053) // "8BPS"
+ return epuc("not PSD", "Corrupt PSD image");
+
+ // Check file type version.
+ if (get16() != 1)
+ return epuc("wrong version", "Unsupported version of PSD image");
+
+ // Skip 6 reserved bytes.
+ skip( 6 );
+
+ // Read the number of channels (R, G, B, A, etc).
+ channelCount = get16();
+ if (channelCount < 0 || channelCount > 16)
+ return epuc("wrong channel count", "Unsupported number of channels in PSD image");
+
+ // Read the rows and columns of the image.
+ h = get32();
+ w = get32();
+
+ // Make sure the depth is 8 bits.
+ if (get16() != 8)
+ return epuc("unsupported bit depth", "PSD bit depth is not 8 bit");
+
+ // Make sure the color mode is RGB.
+ // Valid options are:
+ // 0: Bitmap
+ // 1: Grayscale
+ // 2: Indexed color
+ // 3: RGB color
+ // 4: CMYK color
+ // 7: Multichannel
+ // 8: Duotone
+ // 9: Lab color
+ if (get16() != 3)
+ return epuc("wrong color format", "PSD is not in RGB color format");
+
+ // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
+ skip(get32() );
+
+ // Skip the image resources. (resolution, pen tool paths, etc)
+ skip( get32() );
+
+ // Skip the reserved data.
+ skip( get32() );
+
+ // Find out if the data is compressed.
+ // Known values:
+ // 0: no compression
+ // 1: RLE compressed
+ compression = get16();
+ if (compression > 1)
+ return epuc("unknown compression type", "PSD has an unknown compression format");
+
+ // Create the destination image.
+ out = (stbi_uc *) malloc(4 * w*h);
+ if (!out) return epuc("outofmem", "Out of memory");
+ pixelCount = w*h;
+
+ // Initialize the data to zero.
+ //memset( out, 0, pixelCount * 4 );
+
+ // Finally, the image data.
+ if (compression) {
+ // RLE as used by .PSD and .TIFF
+ // Loop until you get the number of unpacked bytes you are expecting:
+ // Read the next source byte into n.
+ // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
+ // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
+ // Else if n is 128, noop.
+ // Endloop
+
+ // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data,
+ // which we're going to just skip.
+ skip( h * channelCount * 2 );
+
+ // Read the RLE data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ uint8 *p;
+
+ p = out+channel;
+ if (channel >= channelCount) {
+ // Fill this channel with default data.
+ for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4;
+ } else {
+ // Read the RLE data.
+ count = 0;
+ while (count < pixelCount) {
+ len = get8();
+ if (len == 128) {
+ // No-op.
+ } else if (len < 128) {
+ // Copy next len+1 bytes literally.
+ len++;
+ count += len;
+ while (len) {
+ *p = get8();
+ p += 4;
+ len--;
+ }
+ } else if (len > 128) {
+ uint32 val;
+ // Next -len+1 bytes in the dest are replicated from next source byte.
+ // (Interpret len as a negative 8-bit int.)
+ len ^= 0x0FF;
+ len += 2;
+ val = get8();
+ count += len;
+ while (len) {
+ *p = val;
+ p += 4;
+ len--;
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+ // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
+ // where each channel consists of an 8-bit value for each pixel in the image.
+
+ // Read the data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ uint8 *p;
+
+ p = out + channel;
+ if (channel > channelCount) {
+ // Fill this channel with default data.
+ for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4;
+ } else {
+ // Read the data.
+ count = 0;
+ for (i = 0; i < pixelCount; i++)
+ *p = get8(), p += 4;
+ }
+ }
+ }
+
+ if (req_comp && req_comp != 4) {
+ img_x = w;
+ img_y = h;
+ out = convert_format(out, 4, req_comp);
+ if (out == NULL) return out; // convert_format frees input on failure
+ }
+
+ if (comp) *comp = channelCount;
+ *y = h;
+ *x = w;
+
+ return out;
+}
+
+#ifndef STBI_NO_STDIO
+stbi_uc *stbi_psd_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_psd_load_from_file(f, x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+
+stbi_uc *stbi_psd_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return psd_load(x,y,comp,req_comp);
+}
+#endif
+
+stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer, len);
+ return psd_load(x,y,comp,req_comp);
+}
+
+
+// *************************************************************************************************
+// Radiance RGBE HDR loader
+// originally by Nicolas Schulz
+#ifndef STBI_NO_HDR
+static int hdr_test(void)
+{
+ char *signature = "#?RADIANCE\n";
+ int i;
+ for (i=0; signature[i]; ++i)
+ if (get8() != signature[i])
+ return 0;
+ return 1;
+}
+
+int stbi_hdr_test_memory(stbi_uc const *buffer, int len)
+{
+ start_mem(buffer, len);
+ return hdr_test();
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_hdr_test_file(FILE *f)
+{
+ int r,n = ftell(f);
+ start_file(f);
+ r = hdr_test();
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+#define HDR_BUFLEN 1024
+static char *hdr_gettoken(char *buffer)
+{
+ int len=0;
+ char *s = buffer, c = '\0';
+
+ c = get8();
+
+ while (!at_eof() && c != '\n') {
+ buffer[len++] = c;
+ if (len == HDR_BUFLEN-1) {
+ // flush to end of line
+ while (!at_eof() && get8() != '\n')
+ ;
+ break;
+ }
+ c = get8();
+ }
+
+ buffer[len] = 0;
+ return buffer;
+}
+
+static void hdr_convert(float *output, stbi_uc *input, int req_comp)
+{
+ if( input[3] != 0 ) {
+ float f1;
+ // Exponent
+ f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
+ if (req_comp <= 2)
+ output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
+ else {
+ output[0] = input[0] * f1;
+ output[1] = input[1] * f1;
+ output[2] = input[2] * f1;
+ }
+ if (req_comp == 2) output[1] = 1;
+ if (req_comp == 4) output[3] = 1;
+ } else {
+ switch (req_comp) {
+ case 4: output[3] = 1; /* fallthrough */
+ case 3: output[0] = output[1] = output[2] = 0;
+ break;
+ case 2: output[1] = 1; /* fallthrough */
+ case 1: output[0] = 0;
+ break;
+ }
+ }
+}
+
+
+static float *hdr_load(int *x, int *y, int *comp, int req_comp)
+{
+ char buffer[HDR_BUFLEN];
+ char *token;
+ int valid = 0;
+ int width, height;
+ stbi_uc *scanline;
+ float *hdr_data;
+ int len;
+ unsigned char count, value;
+ int i, j, k, c1,c2, z;
+
+
+ // Check identifier
+ if (strcmp(hdr_gettoken(buffer), "#?RADIANCE") != 0)
+ return epf("not HDR", "Corrupt HDR image");
+
+ // Parse header
+ while(1) {
+ token = hdr_gettoken(buffer);
+ if (token[0] == 0) break;
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
+ }
+
+ if (!valid) return epf("unsupported format", "Unsupported HDR format");
+
+ // Parse width and height
+ // can't use sscanf() if we're not using stdio!
+ token = hdr_gettoken(buffer);
+ if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ height = strtol(token, &token, 10);
+ while (*token == ' ') ++token;
+ if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ width = strtol(token, NULL, 10);
+
+ *x = width;
+ *y = height;
+
+ *comp = 3;
+ if (req_comp == 0) req_comp = 3;
+
+ // Read data
+ hdr_data = (float *) malloc(height * width * req_comp * sizeof(float));
+
+ // Load image data
+ // image data is stored as some number of sca
+ if( width < 8 || width >= 32768) {
+ // Read flat data
+ for (j=0; j < height; ++j) {
+ for (i=0; i < width; ++i) {
+ stbi_uc rgbe[4];
+ main_decode_loop:
+ getn(rgbe, 4);
+ hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
+ }
+ }
+ } else {
+ // Read RLE-encoded data
+ scanline = NULL;
+
+ for (j = 0; j < height; ++j) {
+ c1 = get8();
+ c2 = get8();
+ len = get8();
+ if (c1 != 2 || c2 != 2 || (len & 0x80)) {
+ // not run-length encoded, so we have to actually use THIS data as a decoded
+ // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
+ stbi_uc rgbe[4] = { c1,c2,len, get8() };
+ hdr_convert(hdr_data, rgbe, req_comp);
+ i = 1;
+ j = 0;
+ free(scanline);
+ goto main_decode_loop; // yes, this is fucking insane; blame the fucking insane format
+ }
+ len <<= 8;
+ len |= get8();
+ if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); }
+ if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4);
+
+ for (k = 0; k < 4; ++k) {
+ i = 0;
+ while (i < width) {
+ count = get8();
+ if (count > 128) {
+ // Run
+ value = get8();
+ count -= 128;
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = value;
+ } else {
+ // Dump
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = get8();
+ }
+ }
+ }
+ for (i=0; i < width; ++i)
+ hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);
+ }
+ free(scanline);
+ }
+
+ return hdr_data;
+}
+
+#ifndef STBI_NO_STDIO
+float *stbi_hdr_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return hdr_load(x,y,comp,req_comp);
+}
+#endif
+
+float *stbi_hdr_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer, len);
+ return hdr_load(x,y,comp,req_comp);
+}
+
+#endif // STBI_NO_HDR
+
+/////////////////////// write image ///////////////////////
+
+#ifndef STBI_NO_WRITE
+
+static void write8(FILE *f, int x) { uint8 z = (uint8) x; fwrite(&z,1,1,f); }
+
+static void writefv(FILE *f, char *fmt, va_list v)
+{
+ while (*fmt) {
+ switch (*fmt++) {
+ case ' ': break;
+ case '1': { uint8 x = va_arg(v, int); write8(f,x); break; }
+ case '2': { int16 x = va_arg(v, int); write8(f,x); write8(f,x>>8); break; }
+ case '4': { int32 x = va_arg(v, int); write8(f,x); write8(f,x>>8); write8(f,x>>16); write8(f,x>>24); break; }
+ default:
+ assert(0);
+ va_end(v);
+ return;
+ }
+ }
+}
+
+static void writef(FILE *f, char *fmt, ...)
+{
+ va_list v;
+ va_start(v, fmt);
+ writefv(f,fmt,v);
+ va_end(v);
+}
+
+static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad)
+{
+ uint8 bg[3] = { 255, 0, 255}, px[3];
+ uint32 zero = 0;
+ int i,j,k, j_end;
+
+ if (vdir < 0)
+ j_end = -1, j = y-1;
+ else
+ j_end = y, j = 0;
+
+ for (; j != j_end; j += vdir) {
+ for (i=0; i < x; ++i) {
+ uint8 *d = (uint8 *) data + (j*x+i)*comp;
+ if (write_alpha < 0)
+ fwrite(&d[comp-1], 1, 1, f);
+ switch (comp) {
+ case 1:
+ case 2: writef(f, "111", d[0],d[0],d[0]);
+ break;
+ case 4:
+ if (!write_alpha) {
+ for (k=0; k < 3; ++k)
+ px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;
+ writef(f, "111", px[1-rgb_dir],px[1],px[1+rgb_dir]);
+ break;
+ }
+ /* FALLTHROUGH */
+ case 3:
+ writef(f, "111", d[1-rgb_dir],d[1],d[1+rgb_dir]);
+ break;
+ }
+ if (write_alpha > 0)
+ fwrite(&d[comp-1], 1, 1, f);
+ }
+ fwrite(&zero,scanline_pad,1,f);
+ }
+}
+
+static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, char *fmt, ...)
+{
+ FILE *f = fopen(filename, "wb");
+ if (f) {
+ va_list v;
+ va_start(v, fmt);
+ writefv(f, fmt, v);
+ va_end(v);
+ write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
+ fclose(f);
+ }
+ return f != NULL;
+}
+
+int stbi_write_bmp(char const *filename, int x, int y, int comp, void *data)
+{
+ int pad = (-x*3) & 3;
+ return outfile(filename,-1,-1,x,y,comp,data,0,pad,
+ "11 4 22 4" "4 44 22 444444",
+ 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
+ 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
+}
+
+int stbi_write_tga(char const *filename, int x, int y, int comp, void *data)
+{
+ int has_alpha = !(comp & 1);
+ return outfile(filename, -1,-1, x, y, comp, data, has_alpha, 0,
+ "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha);
+}
+
+// any other image formats that do interleaved rgb data?
+// PNG: requires adler32,crc32 -- significant amount of code
+// PSD: no, channels output separately
+// TIFF: no, stripwise-interleaved... i think
+
+#endif // STBI_NO_WRITE
diff --git a/doc/reference/cogl/Makefile.am b/doc/reference/cogl/Makefile.am
new file mode 100644
index 00000000..39daa63d
--- /dev/null
+++ b/doc/reference/cogl/Makefile.am
@@ -0,0 +1,93 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.6 at least.
+AUTOMAKE_OPTIONS = 1.6
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=cogl
+
+# The top-level SGML file. You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR=../../../clutter/cogl
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+SCAN_OPTIONS=--deprecated-guards="CLUTTER_DISABLE_DEPRECATED"
+
+# Extra options to supply to gtkdoc-mkdb.
+# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml
+MKDB_OPTIONS=--sgml-mode --output-format=xml
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS=\
+ --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/glib \
+ --extra-dir=$(GDPIXBUF_PREFIX)/share/gtk-doc/html/gdk-pixbuf \
+ --extra-dir=$(top_srcdir)/doc/reference/clutter/html
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB=\
+ $(top_srcdir)/clutter/cogl/*.h \
+ $(top_srcdir)/clutter/cogl/common/*.h
+CFILE_GLOB=\
+ $(top_srcdir)/clutter/cogl/common/*.c
+
+# Header files to ignore when scanning.
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
+IGNORE_HFILES=\
+ cogl-bitmap.h \
+ cogl-context.h \
+ cogl-defines.h \
+ cogl-internal.h \
+ cogl-primitives.h \
+ cogl-texture.h
+
+EXTRA_HFILES=
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES=
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files=
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+
+INCLUDES=-I$(top_srcdir) -I$(top_srcdir)/clutter $(CLUTTER_CFLAGS)
+GTKDOC_LIBS=$(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la $(CLUTTER_LIBS)
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST =
diff --git a/doc/reference/cogl/cogl-docs.sgml b/doc/reference/cogl/cogl-docs.sgml
new file mode 100644
index 00000000..bac6e42b
--- /dev/null
+++ b/doc/reference/cogl/cogl-docs.sgml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>COGL Reference Manual</title>
+ <releaseinfo>
+ </releaseinfo>
+
+ <copyright>
+ <year>2008</year>
+ <holder>OpenedHand LTD</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the <citetitle>GNU Free
+ Documentation License</citetitle>, Version 1.1 or any later
+ version published by the Free Software Foundation with no
+ Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ Texts. You may obtain a copy of the <citetitle>GNU Free
+ Documentation License</citetitle> from the Free Software
+ Foundation by visiting <ulink type="http"
+ url="http://www.fsf.org">their Web site</ulink> or by writing
+ to:
+
+ <address>
+ The Free Software Foundation, Inc.,
+ <street>59 Temple Place</street> - Suite 330,
+ <city>Boston</city>, <state>MA</state> <postcode>02111-1307</postcode>
+,
+ <country>USA</country>
+ </address>
+ </para>
+ </legalnotice>
+
+ </bookinfo>
+
+ <chapter>
+ <title>COGL - GL Abstraction API</title>
+
+ <section id="cogl-intro">
+ <title>About COGL</title>
+ <para>
+ FIXME
+ </para>
+ </section>
+
+ <xi:include href="xml/cogl.xml"/>
+ <xi:include href="xml/cogl-primitives.xml"/>
+ <xi:include href="xml/cogl-util.xml"/>
+ <xi:include href="xml/cogl-texture.xml"/>
+ <xi:include href="xml/cogl-shaders.xml"/>
+ <xi:include href="xml/cogl-offscreen.xml"/>
+ </chapter>
+</book>
diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt
new file mode 100644
index 00000000..33d49c9b
--- /dev/null
+++ b/doc/reference/cogl/cogl-sections.txt
@@ -0,0 +1,148 @@
+<SECTION>
+<FILE>cogl</FILE>
+<TITLE>General API</TITLE>
+COGL_PIXEL_FORMAT_24
+COGL_PIXEL_FORMAT_32
+COGL_A_BIT
+COGL_BGR_BIT
+COGL_AFIRST_BIT
+COGL_PREMULT_BIT
+COGL_UNORDERED_MASK
+COGL_UNPREMULT_MASK
+CoglPixelFormat
+CoglBufferTarget
+<SUBSECTION>
+cogl_perspective
+cogl_setup_viewport
+cogl_get_modelview_matrix
+cogl_get_projection_matrix
+cogl_get_viewport
+<SUBSECTION>
+cogl_push_matrix
+cogl_pop_matrix
+cogl_scale
+cogl_translatex
+cogl_translate
+cogl_rotatex
+cogl_rotate
+<SUBSECTION>
+cogl_clip_set
+cogl_clip_unset
+<SUBSECTION>
+cogl_enable_depth_test
+cogl_alpha_func
+cogl_fog_set
+</SECTION>
+
+<SECTION>
+<FILE>cogl-util</FILE>
+<TITLE>Utility API</TITLE>
+cogl_create_context
+cogl_destroy_context
+COGL_INVALID_HANDLE
+CoglHandle
+CoglFuncPtr
+<SUBSECTION>
+CoglFeatureFlags
+cogl_get_features
+cogl_features_available
+<SUBSECTION>
+cogl_get_proc_address
+cogl_check_extension
+cogl_get_bitmasks
+cogl_paint_init
+<SUBSECTION>
+cogl_util_next_p2
+</SECTION>
+
+<SECTION>
+<FILE>cogl-primitives</FILE>
+<TITLE>Primitives</TITLE>
+cogl_color
+cogl_fast_fill_rectangle
+cogl_fast_fill_rectanglex
+cogl_fast_fill_trapezoid
+cogl_fast_fill_trapezoidx
+cogl_fill
+cogl_stroke
+cogl_path_move_to
+cogl_path_move_to_rel
+cogl_path_line_to
+cogl_path_line_to_rel
+cogl_path_h_line_to
+cogl_path_v_line_to
+cogl_path_h_line_to_rel
+cogl_path_v_line_to_rel
+cogl_path_arc
+cogl_path_arc_rel
+cogl_path_bezier2_to
+cogl_path_bezier2_to_rel
+cogl_path_bezier3_to
+cogl_path_bezier3_to_rel
+cogl_path_close
+cogl_line
+cogl_polyline
+cogl_polygon
+cogl_rectangle
+cogl_arc
+cogl_ellipse
+cogl_round_rectangle
+</SECTION>
+
+<SECTION>
+<FILE>cogl-texture</FILE>
+<TITLE>Textures</TITLE>
+CoglTextureVertex
+cogl_texture_new_with_size
+cogl_texture_new_from_file
+cogl_texture_new_from_data
+cogl_texture_new_from_foreign
+cogl_is_texture
+cogl_texture_get_width
+cogl_texture_get_height
+cogl_texture_get_format
+cogl_texture_get_rowstride
+cogl_texture_get_max_waste
+cogl_texture_get_min_filter
+cogl_texture_get_mag_filter
+cogl_texture_is_sliced
+cogl_texture_get_gl_texture
+cogl_texture_get_data
+cogl_texture_set_filters
+cogl_texture_set_region
+cogl_texture_ref
+cogl_texture_unref
+cogl_texture_rectangle
+cogl_texture_polygon
+</SECTION>
+
+<SECTION>
+<FILE>cogl-shaders</FILE>
+<TITLE>Shaders and Programmable Pipeline</TITLE>
+cogl_create_shader
+cogl_shader_destroy
+cogl_shader_source
+cogl_shader_compile
+cogl_shader_get_info_log
+cogl_shader_get_parameteriv
+<SUBSECTION>
+cogl_create_program
+cogl_program_destroy
+cogl_program_attach_shader
+cogl_program_link
+cogl_program_use
+cogl_program_get_uniform_location
+cogl_program_uniform_1f
+</SECTION>
+
+<SECTION>
+<FILE>cogl-offscreen</FILE>
+<TITLE>Offscreen Buffers</TITLE>
+cogl_offscreen_new_to_texture
+cogl_offscreen_new_multisample
+cogl_offscreen_ref
+cogl_offscreen_unref
+cogl_offscreen_blit
+cogl_offscreen_blit_region
+cogl_draw_buffer
+</SECTION>
diff --git a/gl/Makefile.am b/gl/Makefile.am
index 98949b26..17e5ed46 100644
--- a/gl/Makefile.am
+++ b/gl/Makefile.am
@@ -1,19 +1,36 @@
-libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/clutter
-libclutterinclude_HEADERS = $(top_srcdir)/clutter/cogl/cogl.h \
- $(top_srcdir)/clutter/cogl/gl/cogl-defines.h
+libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/cogl
+libclutterinclude_HEADERS = $(top_builddir)/clutter/cogl/cogl.h \
+ $(top_builddir)/clutter/cogl/cogl-defines-gl.h
INCLUDES = \
- -I$(top_srcdir) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/clutter \
-I$(top_srcdir)/clutter/cogl \
- $(CLUTTER_CFLAGS) \
- $(CLUTTER_DEBUG_CFLAGS) \
+ -I$(top_srcdir)/clutter/cogl/common \
+ -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \
+ -I$(top_builddir)/clutter \
+ -I$(top_builddir)/clutter/cogl \
+ $(CLUTTER_CFLAGS) \
+ $(CLUTTER_DEBUG_CFLAGS) \
$(GCC_FLAGS)
LDADD = $(CLUTTER_LIBS)
noinst_LTLIBRARIES = libclutter-cogl.la
-libclutter_cogl_la_SOURCES = \
- $(top_srcdir)/clutter/cogl/cogl.h \
- $(top_srcdir)/clutter/cogl/gl/cogl-defines.h \
- cogl.c
+libclutter_cogl_la_SOURCES = \
+ $(top_builddir)/clutter/cogl/cogl.h \
+ $(top_builddir)/clutter/cogl/gl/cogl-defines.h \
+ cogl-defines.h \
+ cogl-internal.h \
+ cogl-primitives.h \
+ cogl-texture.h \
+ cogl-fbo.h \
+ cogl-context.h \
+ cogl.c \
+ cogl-primitives.c \
+ cogl-texture.c \
+ cogl-fbo.c \
+ cogl-context.c
+
+libclutter_cogl_la_LIBADD = $(top_builddir)/clutter/cogl/common/libclutter-cogl-common.la
diff --git a/gl/cogl-context.c b/gl/cogl-context.c
new file mode 100644
index 00000000..936d7e9b
--- /dev/null
+++ b/gl/cogl-context.c
@@ -0,0 +1,114 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-util.h"
+#include "cogl-context.h"
+
+#include <string.h>
+
+static CoglContext *_context = NULL;
+
+gboolean
+cogl_create_context ()
+{
+ if (_context != NULL)
+ return FALSE;
+
+ /* Allocate context memory */
+ _context = (CoglContext*) g_malloc (sizeof (CoglContext));
+
+ /* Init default values */
+ _context->feature_flags = 0;
+ _context->features_cached = FALSE;
+
+ _context->enable_flags = 0;
+ _context->color_alpha = 255;
+
+ _context->path_nodes = NULL;
+ _context->path_nodes_cap = 0;
+ _context->path_nodes_size = 0;
+
+ _context->texture_handles = NULL;
+
+ _context->fbo_handles = NULL;
+ _context->draw_buffer = COGL_WINDOW_BUFFER;
+
+ _context->pf_glGenRenderbuffersEXT = NULL;
+ _context->pf_glBindRenderbufferEXT = NULL;
+ _context->pf_glRenderbufferStorageEXT = NULL;
+ _context->pf_glGenFramebuffersEXT = NULL;
+ _context->pf_glBindFramebufferEXT = NULL;
+ _context->pf_glFramebufferTexture2DEXT = NULL;
+ _context->pf_glFramebufferRenderbufferEXT = NULL;
+ _context->pf_glCheckFramebufferStatusEXT = NULL;
+ _context->pf_glDeleteFramebuffersEXT = NULL;
+ _context->pf_glBlitFramebufferEXT = NULL;
+ _context->pf_glRenderbufferStorageMultisampleEXT = NULL;
+
+ _context->pf_glCreateProgramObjectARB = NULL;
+ _context->pf_glCreateShaderObjectARB = NULL;
+ _context->pf_glShaderSourceARB = NULL;
+ _context->pf_glCompileShaderARB = NULL;
+ _context->pf_glAttachObjectARB = NULL;
+ _context->pf_glLinkProgramARB = NULL;
+ _context->pf_glUseProgramObjectARB = NULL;
+ _context->pf_glGetUniformLocationARB = NULL;
+ _context->pf_glDeleteObjectARB = NULL;
+ _context->pf_glGetInfoLogARB = NULL;
+ _context->pf_glGetObjectParameterivARB = NULL;
+ _context->pf_glUniform1fARB = NULL;
+
+ /* Init OpenGL state */
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glColorMask (TRUE, TRUE, TRUE, FALSE);
+ cogl_enable (0);
+
+ return TRUE;
+}
+
+void
+cogl_destroy_context ()
+{
+ if (_context == NULL)
+ return;
+
+ g_free (_context);
+}
+
+CoglContext *
+_cogl_context_get_default ()
+{
+ /* Create if doesn't exist yet */
+ if (_context == NULL)
+ cogl_create_context ();
+
+ return _context;
+}
diff --git a/gl/cogl-context.h b/gl/cogl-context.h
new file mode 100644
index 00000000..4e952cc5
--- /dev/null
+++ b/gl/cogl-context.h
@@ -0,0 +1,95 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_CONTEXT_H
+#define __COGL_CONTEXT_H
+
+#include "cogl-primitives.h"
+
+typedef struct
+{
+ /* Features cache */
+ CoglFeatureFlags feature_flags;
+ gboolean features_cached;
+
+ /* Enable cache */
+ gulong enable_flags;
+ guint8 color_alpha;
+
+ /* Primitives */
+ CoglFixedVec2 path_start;
+ CoglFixedVec2 path_pen;
+ CoglFloatVec2 *path_nodes;
+ guint path_nodes_cap;
+ guint path_nodes_size;
+ CoglFixedVec2 path_nodes_min;
+ CoglFixedVec2 path_nodes_max;
+
+ /* Textures */
+ GArray *texture_handles;
+
+ /* Framebuffer objects */
+ GArray *fbo_handles;
+ CoglBufferTarget draw_buffer;
+
+ /* Relying on glext.h to define these */
+ PFNGLGENRENDERBUFFERSEXTPROC pf_glGenRenderbuffersEXT;
+ PFNGLBINDRENDERBUFFEREXTPROC pf_glBindRenderbufferEXT;
+ PFNGLRENDERBUFFERSTORAGEEXTPROC pf_glRenderbufferStorageEXT;
+ PFNGLGENFRAMEBUFFERSEXTPROC pf_glGenFramebuffersEXT;
+ PFNGLBINDFRAMEBUFFEREXTPROC pf_glBindFramebufferEXT;
+ PFNGLFRAMEBUFFERTEXTURE2DEXTPROC pf_glFramebufferTexture2DEXT;
+ PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC pf_glFramebufferRenderbufferEXT;
+ PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC pf_glCheckFramebufferStatusEXT;
+ PFNGLDELETEFRAMEBUFFERSEXTPROC pf_glDeleteFramebuffersEXT;
+ PFNGLBLITFRAMEBUFFEREXTPROC pf_glBlitFramebufferEXT;
+ PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC pf_glRenderbufferStorageMultisampleEXT;
+
+ PFNGLCREATEPROGRAMOBJECTARBPROC pf_glCreateProgramObjectARB;
+ PFNGLCREATESHADEROBJECTARBPROC pf_glCreateShaderObjectARB;
+ PFNGLSHADERSOURCEARBPROC pf_glShaderSourceARB;
+ PFNGLCOMPILESHADERARBPROC pf_glCompileShaderARB;
+ PFNGLATTACHOBJECTARBPROC pf_glAttachObjectARB;
+ PFNGLLINKPROGRAMARBPROC pf_glLinkProgramARB;
+ PFNGLUSEPROGRAMOBJECTARBPROC pf_glUseProgramObjectARB;
+ PFNGLGETUNIFORMLOCATIONARBPROC pf_glGetUniformLocationARB;
+ PFNGLDELETEOBJECTARBPROC pf_glDeleteObjectARB;
+ PFNGLGETINFOLOGARBPROC pf_glGetInfoLogARB;
+ PFNGLGETOBJECTPARAMETERIVARBPROC pf_glGetObjectParameterivARB;
+ PFNGLUNIFORM1FARBPROC pf_glUniform1fARB;
+
+} CoglContext;
+
+CoglContext *
+_cogl_context_get_default ();
+
+/* Obtains the context and returns retval if NULL */
+#define _COGL_GET_CONTEXT(ctxvar, retval) \
+CoglContext *ctxvar = _cogl_context_get_default (); \
+if (ctxvar == NULL) return retval;
+
+#define NO_RETVAL
+
+#endif /* __COGL_CONTEXT_H */
diff --git a/gl/cogl-fbo.c b/gl/cogl-fbo.c
new file mode 100644
index 00000000..f4fb968d
--- /dev/null
+++ b/gl/cogl-fbo.c
@@ -0,0 +1,390 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-util.h"
+#include "cogl-texture.h"
+#include "cogl-fbo.h"
+#include "cogl-context.h"
+
+/* Expecting EXT functions not to be defined - redirect to pointers in context */
+#define glGenRenderbuffersEXT ctx->pf_glGenRenderbuffersEXT
+#define glBindRenderbufferEXT ctx->pf_glBindRenderbufferEXT
+#define glRenderbufferStorageEXT ctx->pf_glRenderbufferStorageEXT
+#define glGenFramebuffersEXT ctx->pf_glGenFramebuffersEXT
+#define glBindFramebufferEXT ctx->pf_glBindFramebufferEXT
+#define glFramebufferTexture2DEXT ctx->pf_glFramebufferTexture2DEXT
+#define glFramebufferRenderbufferEXT ctx->pf_glFramebufferRenderbufferEXT
+#define glCheckFramebufferStatusEXT ctx->pf_glCheckFramebufferStatusEXT
+#define glDeleteFramebuffersEXT ctx->pf_glDeleteFramebuffersEXT
+#define glBlitFramebufferEXT ctx->pf_glBlitFramebufferEXT
+#define glRenderbufferStorageMultisampleEXT ctx->pf_glRenderbufferStorageMultisampleEXT
+
+static gint
+_cogl_fbo_handle_find (CoglHandle handle)
+{
+ _COGL_GET_CONTEXT (ctx, -1);
+
+ gint i;
+
+ if (ctx->fbo_handles == NULL)
+ return -1;
+
+ for (i=0; i < ctx->fbo_handles->len; ++i)
+ if (g_array_index (ctx->fbo_handles, CoglHandle, i) == handle)
+ return i;
+
+ return -1;
+}
+
+static CoglHandle
+_cogl_fbo_handle_new (CoglFbo *fbo)
+{
+ _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
+
+ CoglHandle handle = (CoglHandle)fbo;
+
+ if (ctx->fbo_handles == NULL)
+ ctx->fbo_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle));
+
+ g_array_append_val (ctx->fbo_handles, handle);
+
+ return handle;
+}
+
+static void
+_cogl_fbo_handle_release (CoglHandle handle)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ gint i;
+
+ if ( (i = _cogl_fbo_handle_find (handle)) == -1)
+ return;
+
+ g_array_remove_index_fast (ctx->fbo_handles, i);
+}
+
+static CoglFbo*
+_cogl_fbo_pointer_from_handle (CoglHandle handle)
+{
+ return (CoglFbo*) handle;
+}
+
+gboolean
+cogl_is_offscreen_buffer (CoglHandle handle)
+{
+ if (handle == COGL_INVALID_HANDLE)
+ return FALSE;
+
+ return _cogl_fbo_handle_find (handle) >= 0;
+}
+
+CoglHandle
+cogl_offscreen_new_to_texture (CoglHandle texhandle)
+{
+ _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
+
+ if (!cogl_features_available (COGL_FEATURE_OFFSCREEN))
+ return COGL_INVALID_HANDLE;
+
+ CoglTexture *tex;
+ CoglFbo *fbo;
+ CoglTexSliceSpan *x_span;
+ CoglTexSliceSpan *y_span;
+ GLuint tex_gl_handle;
+ GLuint fbo_gl_handle;
+ GLenum status;
+
+ /* Make texhandle is a valid texture object */
+ if (!cogl_is_texture (texhandle))
+ return COGL_INVALID_HANDLE;
+
+ tex = _cogl_texture_pointer_from_handle (texhandle);
+
+ /* The texture must not be sliced */
+ if (tex->slice_gl_handles == NULL)
+ return COGL_INVALID_HANDLE;
+
+ if (tex->slice_gl_handles->len != 1)
+ return COGL_INVALID_HANDLE;
+
+ /* Pick the single texture slice width, height and GL id */
+ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0);
+ y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0);
+ tex_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0);
+
+ /* Generate framebuffer */
+ glGenFramebuffersEXT (1, &fbo_gl_handle);
+ GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo_gl_handle) );
+ GE( glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ tex->gl_target, tex_gl_handle, 0) );
+
+ /* Make sure it's complete */
+ status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
+
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ GE( glDeleteFramebuffersEXT (1, &fbo_gl_handle) );
+ GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) );
+ return COGL_INVALID_HANDLE;
+ }
+
+ GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) );
+
+ /* Allocate and init a CoglFbo object (store non-wasted size
+ for subsequent blits and viewport setup) */
+ fbo = (CoglFbo*) g_malloc (sizeof (CoglFbo));
+ fbo->ref_count = 1;
+ fbo->width = x_span->size - x_span->waste;
+ fbo->height = y_span->size - y_span->waste;
+ fbo->gl_handle = fbo_gl_handle;
+
+ return _cogl_fbo_handle_new (fbo);
+}
+
+CoglHandle
+cogl_offscreen_new_multisample ()
+{
+ if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_MULTISAMPLE))
+ return COGL_INVALID_HANDLE;
+
+ return COGL_INVALID_HANDLE;
+}
+
+CoglHandle
+cogl_offscreen_ref (CoglHandle handle)
+{
+ CoglFbo *fbo;
+
+ if (!cogl_is_offscreen_buffer (handle))
+ return COGL_INVALID_HANDLE;
+
+ fbo = _cogl_fbo_pointer_from_handle (handle);
+
+ fbo->ref_count++;
+
+ return handle;
+}
+
+void
+cogl_offscreen_unref (CoglHandle handle)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ CoglFbo *fbo;
+
+ /* Make sure this is a valid fbo handle */
+ if (!cogl_is_offscreen_buffer (handle))
+ return;
+
+ fbo = _cogl_fbo_pointer_from_handle (handle);
+
+ if (--fbo->ref_count < 1)
+ {
+ /* Release handle and destroy object */
+ GE( glDeleteFramebuffersEXT (1, &fbo->gl_handle) );
+
+ _cogl_fbo_handle_release (handle);
+
+ g_free (fbo);
+ }
+}
+
+void
+cogl_offscreen_blit_region (CoglHandle src_buffer,
+ CoglHandle dst_buffer,
+ int src_x,
+ int src_y,
+ int src_w,
+ int src_h,
+ int dst_x,
+ int dst_y,
+ int dst_w,
+ int dst_h)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_BLIT))
+ return;
+
+ CoglFbo *src_fbo;
+ CoglFbo *dst_fbo;
+
+ /* Make sure these are valid fbo handles */
+ if (!cogl_is_offscreen_buffer (src_buffer))
+ return;
+
+ if (!cogl_is_offscreen_buffer (dst_buffer))
+ return;
+
+ src_fbo = _cogl_fbo_pointer_from_handle (src_buffer);
+ dst_fbo = _cogl_fbo_pointer_from_handle (dst_buffer);
+
+ /* Copy (and scale) a region from one to another framebuffer */
+ GE( glBindFramebufferEXT (GL_READ_FRAMEBUFFER_EXT, src_fbo->gl_handle) );
+ GE( glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, dst_fbo->gl_handle) );
+ GE( glBlitFramebufferEXT (src_x, src_y, src_x + src_w, src_y + src_h,
+ dst_x, dst_y, dst_x + dst_w, dst_y + dst_h,
+ GL_COLOR_BUFFER_BIT, GL_LINEAR) );
+}
+
+void
+cogl_offscreen_blit (CoglHandle src_buffer,
+ CoglHandle dst_buffer)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_BLIT))
+ return;
+
+ CoglFbo *src_fbo;
+ CoglFbo *dst_fbo;
+
+ /* Make sure these are valid fbo handles */
+ if (!cogl_is_offscreen_buffer (src_buffer))
+ return;
+
+ if (!cogl_is_offscreen_buffer (dst_buffer))
+ return;
+
+ src_fbo = _cogl_fbo_pointer_from_handle (src_buffer);
+ dst_fbo = _cogl_fbo_pointer_from_handle (dst_buffer);
+
+ /* Copy (and scale) whole image from one to another framebuffer */
+ GE( glBindFramebufferEXT (GL_READ_FRAMEBUFFER_EXT, src_fbo->gl_handle) );
+ GE( glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, dst_fbo->gl_handle) );
+ GE( glBlitFramebufferEXT (0, 0, src_fbo->width, src_fbo->height,
+ 0, 0, dst_fbo->width, dst_fbo->height,
+ GL_COLOR_BUFFER_BIT, GL_LINEAR) );
+}
+
+void
+cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ CoglFbo *fbo = NULL;
+
+ if (target == COGL_OFFSCREEN_BUFFER)
+ {
+ /* Make sure it is a valid fbo handle */
+ if (!cogl_is_offscreen_buffer (offscreen))
+ return;
+
+ fbo = _cogl_fbo_pointer_from_handle (offscreen);
+
+ /* Check current draw buffer target */
+ if (ctx->draw_buffer != COGL_OFFSCREEN_BUFFER)
+ {
+ /* Push the viewport and matrix setup if redirecting
+ from a non-screen buffer */
+ GE( glPushAttrib (GL_VIEWPORT_BIT) );
+
+ GE( glMatrixMode (GL_PROJECTION) );
+ GE( glPushMatrix () );
+ GE( glLoadIdentity () );
+
+ GE( glMatrixMode (GL_MODELVIEW) );
+ GE( glPushMatrix () );
+ GE( glLoadIdentity () );
+ }
+ else
+ {
+ /* Override viewport and matrix setup if redirecting
+ from another offscreen buffer */
+ GE( glMatrixMode (GL_PROJECTION) );
+ GE( glLoadIdentity () );
+
+ GE( glMatrixMode (GL_MODELVIEW) );
+ GE( glLoadIdentity () );
+ }
+
+ /* Setup new viewport and matrices */
+ GE( glViewport (0, 0, fbo->width, fbo->height) );
+ GE( glTranslatef (-1.0f, -1.0f, 0.0f) );
+ GE( glScalef (2.0f / fbo->width, 2.0f / fbo->height, 1.0f) );
+
+ /* Bind offscreen framebuffer object */
+ GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo->gl_handle) );
+ GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) );
+
+ /* Some implementation require a clear before drawing
+ to an fbo. Luckily it is affected by scissor test. */
+ /* FIXME: test where exactly this is needed end whether
+ a glClear with 0 argument is enough */
+ GE( glPushAttrib (GL_SCISSOR_BIT) );
+ GE( glScissor (0,0,0,0) );
+ GE( glEnable (GL_SCISSOR_TEST) );
+ GE( glClear (GL_COLOR_BUFFER_BIT) );
+ GE( glPopAttrib () );
+
+ }
+ else if ((target & COGL_WINDOW_BUFFER) ||
+ (target & COGL_MASK_BUFFER))
+ {
+ /* Check current draw buffer target */
+ if (ctx->draw_buffer == COGL_OFFSCREEN_BUFFER)
+ {
+ /* Pop viewport and matrices if redirecting back
+ from an offscreen buffer */
+ GE( glPopAttrib () );
+
+ GE( glMatrixMode (GL_PROJECTION) );
+ GE( glPopMatrix () );
+
+ GE( glMatrixMode (GL_MODELVIEW) );
+ GE( glPopMatrix () );
+ }
+
+ /* Bind window framebuffer object */
+ GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) );
+
+
+ if (target == COGL_WINDOW_BUFFER)
+ {
+ /* Draw to RGB channels */
+ GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE) );
+ }
+ else if (target == COGL_MASK_BUFFER)
+ {
+ /* Draw only to ALPHA channel */
+ GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE) );
+ }
+ else
+ {
+ /* Draw to all channels */
+ GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) );
+ }
+ }
+
+ /* Store new target */
+ ctx->draw_buffer = target;
+}
diff --git a/gl/cogl-fbo.h b/gl/cogl-fbo.h
new file mode 100644
index 00000000..f2dd5298
--- /dev/null
+++ b/gl/cogl-fbo.h
@@ -0,0 +1,38 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_FBO_H
+#define __COGL_FBO_H
+
+typedef struct
+{
+ guint ref_count;
+ int width;
+ int height;
+ GLuint gl_handle;
+
+} CoglFbo;
+
+#endif /* __COGL_FBO_H */
diff --git a/gl/cogl-internal.h b/gl/cogl-internal.h
new file mode 100644
index 00000000..1f6b8639
--- /dev/null
+++ b/gl/cogl-internal.h
@@ -0,0 +1,61 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_INTERNAL_H
+#define __COGL_INTERNAL_H
+
+#define COGL_DEBUG 0
+
+#if COGL_DEBUG
+#define GE(x...) G_STMT_START { \
+ GLenum err; \
+ (x); \
+ while ((err = glGetError()) != GL_NO_ERROR) { \
+ fprintf(stderr, "glError: %s caught at %s:%u\n", \
+ (char *)error_string(err), \
+ __FILE__, __LINE__); \
+ } \
+} G_STMT_END
+#else
+#define GE(x) (x);
+#endif
+
+#define COGL_ENABLE_BLEND (1<<1)
+#define COGL_ENABLE_TEXTURE_2D (1<<2)
+#define COGL_ENABLE_ALPHA_TEST (1<<3)
+#define COGL_ENABLE_TEXTURE_RECT (1<<4)
+#define COGL_ENABLE_VERTEX_ARRAY (1<<5)
+#define COGL_ENABLE_TEXCOORD_ARRAY (1<<6)
+
+gint
+_cogl_get_format_bpp (CoglPixelFormat format);
+
+void
+cogl_enable (gulong flags);
+
+gulong
+cogl_get_enable ();
+
+#endif /* __COGL_INTERNAL_H */
diff --git a/gl/cogl-primitives.c b/gl/cogl-primitives.c
new file mode 100644
index 00000000..352ad46d
--- /dev/null
+++ b/gl/cogl-primitives.c
@@ -0,0 +1,1092 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-context.h"
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _COGL_MAX_BEZ_RECURSE_DEPTH 16
+
+/**
+ * SECTION:cogl-primitives
+ * @short_description: Functions that draw various primitive shapes and
+ * allow for construction of more complex paths.
+ *
+ * There are three levels on which drawing with cogl can be used. The
+ * highest level functions construct various simple primitive shapes
+ * to be either filled or stroked. Using a lower-level set of functions
+ * more complex and arbitrary paths can be constructed by concatenating
+ * straight line, bezier curve and arc segments. Additionally there
+ * are utility functions that draw the most common primitives - rectangles
+ * and trapezoids - in a maximaly optimized fashion.
+ *
+ * When constructing arbitrary paths, the current pen location is
+ * initialized using the move_to command. The subsequent path segments
+ * implicitly use the last pen location as their first vertex and move
+ * the pen location to the last vertex they produce at the end. Also
+ * there are special versions of functions that allow specifying the
+ * vertices of the path segments relative to the last pen location
+ * rather then in the absolute coordinates.
+ */
+
+/**
+ * cogl_fast_fill_rectangle:
+ * @x: X coordinate of the top-left corner
+ * @y: Y coordinate of the top-left corner
+ * @width: Width of the rectangle
+ * @height: Height of the rectangle
+ *
+ * Fills a rectangle at the given coordinates with the current
+ * drawing color in a highly optimizied fashion.
+ **/
+void
+cogl_fast_fill_rectangle (gint x,
+ gint y,
+ guint width,
+ guint height)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_enable (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0);
+
+ GE( glRecti (x, y, x + width, y + height) );
+}
+
+/**
+ * cogl_fast_fill_rectanglex:
+ * @x: X coordinate of the top-left corner
+ * @y: Y coordinate of the top-left corner
+ * @width: Width of the rectangle
+ * @height: Height of the rectangle
+ *
+ * A fixed-point version of cogl_fast_fill_rectangle.
+ **/
+void
+cogl_fast_fill_rectanglex (ClutterFixed x,
+ ClutterFixed y,
+ ClutterFixed width,
+ ClutterFixed height)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_enable (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0);
+
+ GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x),
+ CLUTTER_FIXED_TO_FLOAT (y),
+ CLUTTER_FIXED_TO_FLOAT (x + width),
+ CLUTTER_FIXED_TO_FLOAT (y + height)) );
+}
+
+/**
+ * cogl_fast_fill_trapezoid:
+ * @y1: Y coordinate of the top two vertices.
+ * @x11: X coordinate of the top-left vertex.
+ * @x21: X coordinate of the top-right vertex.
+ * @y2: Y coordinate of the bottom two vertices.
+ * @x12: X coordinate of the bottom-left vertex.
+ * @x22: X coordinate of the bottom-right vertex.
+ *
+ * Fills a trapezoid at the given coordinates with the current
+ * drawing color in a highly optimized fashion.
+ **/
+void
+cogl_fast_fill_trapezoid (gint y1,
+ gint x11,
+ gint x21,
+ gint y2,
+ gint x12,
+ gint x22)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_enable (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0);
+
+ GE( glBegin (GL_QUADS) );
+ GE( glVertex2i (x11, y1) );
+ GE( glVertex2i (x21, y1) );
+ GE( glVertex2i (x22, y2) );
+ GE( glVertex2i (x12, y2) );
+ GE( glEnd () );
+}
+
+/**
+ * cogl_fast_fill_trapezoidx:
+ * @y1: Y coordinate of the top two vertices.
+ * @x11: X coordinate of the top-left vertex.
+ * @x21: X coordinate of the top-right vertex.
+ * @y2: Y coordinate of the bottom two vertices.
+ * @x12: X coordinate of the bottom-left vertex.
+ * @x22: X coordinate of the bottom-right vertex.
+ *
+ * A fixed-point version of cogl_fast_fill_trapezoid.
+ **/
+void
+cogl_fast_fill_trapezoidx (ClutterFixed y1,
+ ClutterFixed x11,
+ ClutterFixed x21,
+ ClutterFixed y2,
+ ClutterFixed x12,
+ ClutterFixed x22)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_enable (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0);
+
+ GE( glBegin (GL_QUADS) );
+
+ GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x11),
+ CLUTTER_FIXED_TO_FLOAT (y1)) );
+ GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x21),
+ CLUTTER_FIXED_TO_FLOAT (y1)) );
+ GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x22),
+ CLUTTER_FIXED_TO_FLOAT (y2)) );
+ GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x12),
+ CLUTTER_FIXED_TO_FLOAT (y2)) );
+ GE( glEnd () );
+}
+
+static void
+_cogl_path_clear_nodes ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (ctx->path_nodes)
+ g_free(ctx->path_nodes);
+
+ ctx->path_nodes = (CoglFloatVec2*) g_malloc (2 * sizeof(CoglFloatVec2));
+ ctx->path_nodes_size = 0;
+ ctx->path_nodes_cap = 2;
+}
+
+static void
+_cogl_path_add_node (ClutterFixed x,
+ ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ CoglFloatVec2 *new_nodes = NULL;
+
+ if (ctx->path_nodes_size == ctx->path_nodes_cap)
+ {
+ new_nodes = g_realloc (ctx->path_nodes,
+ 2 * ctx->path_nodes_cap
+ * sizeof (CoglFloatVec2));
+
+ if (new_nodes == NULL) return;
+
+ ctx->path_nodes = new_nodes;
+ ctx->path_nodes_cap *= 2;
+ }
+
+ ctx->path_nodes [ctx->path_nodes_size] .x = CLUTTER_FIXED_TO_FLOAT (x);
+ ctx->path_nodes [ctx->path_nodes_size] .y = CLUTTER_FIXED_TO_FLOAT (y);
+ ctx->path_nodes_size++;
+
+ if (ctx->path_nodes_size == 1)
+ {
+ ctx->path_nodes_min.x = ctx->path_nodes_max.x = x;
+ ctx->path_nodes_min.y = ctx->path_nodes_max.y = y;
+ }
+ else
+ {
+ if (x < ctx->path_nodes_min.x) ctx->path_nodes_min.x = x;
+ if (x > ctx->path_nodes_max.x) ctx->path_nodes_max.x = x;
+ if (y < ctx->path_nodes_min.y) ctx->path_nodes_min.y = y;
+ if (y > ctx->path_nodes_max.y) ctx->path_nodes_max.y = y;
+ }
+}
+
+static void
+_cogl_path_stroke_nodes ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_enable (COGL_ENABLE_VERTEX_ARRAY
+ | (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0));
+
+ GE( glVertexPointer (2, GL_FLOAT, 0, ctx->path_nodes) );
+ GE( glDrawArrays (GL_LINE_STRIP, 0, ctx->path_nodes_size) );
+}
+
+static void
+_cogl_path_fill_nodes ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ guint bounds_x;
+ guint bounds_y;
+ guint bounds_w;
+ guint bounds_h;
+
+ GE( glClear (GL_STENCIL_BUFFER_BIT) );
+
+ GE( glEnable (GL_STENCIL_TEST) );
+ GE( glStencilFunc (GL_ALWAYS, 0x0, 0x0) );
+ GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) );
+ GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) );
+
+ cogl_enable (COGL_ENABLE_VERTEX_ARRAY
+ | (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0));
+
+ GE( glVertexPointer (2, GL_FLOAT, 0, ctx->path_nodes) );
+ GE( glDrawArrays (GL_TRIANGLE_FAN, 0, ctx->path_nodes_size) );
+
+ GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
+ GE( glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) );
+ GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) );
+
+ bounds_x = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.x);
+ bounds_y = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.y);
+ bounds_w = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.x - ctx->path_nodes_min.x);
+ bounds_h = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.y - ctx->path_nodes_min.y);
+
+ cogl_fast_fill_rectangle (bounds_x, bounds_y, bounds_w, bounds_h);
+
+ GE( glDisable (GL_STENCIL_TEST) );
+}
+
+/**
+ * cogl_fill:
+ *
+ * Fills the constructed shape using the current drawing color.
+ **/
+void
+cogl_fill ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (ctx->path_nodes_size == 0)
+ return;
+
+ _cogl_path_fill_nodes();
+
+}
+
+/**
+ * cogl_stroke:
+ *
+ * Strokes the constructed shape using the current drawing color
+ * and a width of 1 pixel (regardless of the current transformation
+ * matrix).
+ **/
+void
+cogl_stroke ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (ctx->path_nodes_size == 0)
+ return;
+
+ _cogl_path_stroke_nodes();
+}
+
+/**
+ * cogl_path_move_to:
+ * @x: X coordinate of the pen location to move to.
+ * @y: Y coordinate of the pen location to move to.
+ *
+ * Clears the previously constructed shape and begins a new path
+ * contour by moving the pen to the given coordinates.
+ **/
+void
+cogl_path_move_to (ClutterFixed x,
+ ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ /* FIXME: handle multiple contours maybe? */
+
+ _cogl_path_clear_nodes ();
+ _cogl_path_add_node (x, y);
+
+ ctx->path_start.x = x;
+ ctx->path_start.y = y;
+
+ ctx->path_pen = ctx->path_start;
+}
+
+/**
+ * cogl_path_move_to_rel:
+ * @x: X offset from the current pen location to move the pen to.
+ * @y: Y offset from the current pen location to move the pen to.
+ *
+ * Clears the previously constructed shape and begins a new path
+ * contour by moving the pen to the given coordinates relative
+ * to the current pen location.
+ **/
+void
+cogl_path_move_to_rel (ClutterFixed x,
+ ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_move_to (ctx->path_pen.x + x,
+ ctx->path_pen.y + y);
+}
+
+/**
+ * cogl_path_line_to:
+ * @x: X coordinate of the end line vertex
+ * @y: Y coordinate of the end line vertex
+ *
+ * Adds a straight line segment to the current path that ends at the
+ * given coordinates.
+ **/
+void
+cogl_path_line_to (ClutterFixed x,
+ ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ _cogl_path_add_node (x, y);
+
+ ctx->path_pen.x = x;
+ ctx->path_pen.y = y;
+}
+
+/**
+ * cogl_path_line_to:
+ * @x: X offset from the current pen location of the end line vertex
+ * @y: Y offset from the current pen location of the end line vertex
+ *
+ * Adds a straight line segment to the current path that ends at the
+ * given coordinates relative to the current pen location.
+ **/
+void
+cogl_path_line_to_rel (ClutterFixed x,
+ ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_line_to (ctx->path_pen.x + x,
+ ctx->path_pen.y + y);
+}
+
+/**
+ * cogl_path_h_line_to:
+ * @x: X coordinate of the end line vertex
+ *
+ * Adds a straight horizontal line segment to the current path that
+ * ends at the given X coordinate and current pen Y coordinate.
+ **/
+void
+cogl_path_h_line_to (ClutterFixed x)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_line_to (x,
+ ctx->path_pen.y);
+}
+
+/**
+ * cogl_path_v_line_to:
+ * @y: Y coordinate of the end line vertex
+ *
+ * Adds a stright vertical line segment to the current path that ends
+ * at the current pen X coordinate and the given Y coordinate.
+ **/
+void
+cogl_path_v_line_to (ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_line_to (ctx->path_pen.x,
+ y);
+}
+
+/**
+ * cogl_path_h_line_to_rel:
+ * @x: X offset from the current pen location of the end line vertex
+ *
+ * Adds a straight horizontal line segment to the current path that
+ * ends at the given X coordinate relative to the current pen location
+ * and current pen Y coordinate.
+ **/
+void
+cogl_path_h_line_to_rel (ClutterFixed x)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_line_to (ctx->path_pen.x + x,
+ ctx->path_pen.y);
+}
+
+/**
+ * cogl_path_v_line_to_rel:
+ * @y: Y offset from the current pen location of the end line vertex
+ *
+ * Adds a stright vertical line segment to the current path that ends
+ * at the current pen X coordinate and the given Y coordinate relative
+ * to the current pen location.
+ **/
+void
+cogl_path_v_line_to_rel (ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_line_to (ctx->path_pen.x,
+ ctx->path_pen.y + y);
+}
+
+/**
+ * cogl_path_close:
+ *
+ * Closes the path being constructed by adding a straight line segment
+ * to it that ends at the first vertex of the path.
+ **/
+void
+cogl_path_close ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ _cogl_path_add_node (ctx->path_start.x, ctx->path_start.y);
+ ctx->path_pen = ctx->path_start;
+}
+
+/**
+ * cogl_line:
+ * @x1: X coordinate of the start line vertex
+ * @y1: Y coordinate of the start line vertex
+ * @x2: X coordinate of the end line vertex
+ * @y2: Y coordinate of the end line vertex
+ *
+ * Clears the previously constructed shape and constructs a straight
+ * line shape start and ending at the given coordinates.
+ **/
+void
+cogl_line (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2)
+{
+ cogl_path_move_to (x1, y1);
+ cogl_path_line_to (x2, y2);
+}
+
+/**
+ * cogl_polyline:
+ * @coords: A pointer to the first element of an array of fixed-point
+ * values that specify the vertex coordinates.
+ * @num_points: The total number of vertices.
+ *
+ * Clears the previously constructed shape and constructs a series of straight
+ * line segments, starting from the first given vertex coordinate. Each
+ * subsequent segment stars where the previous one ended and ends at the next
+ * given vertex coordinate.
+ *
+ * The coords array must contain 2 * num_points values. The first value
+ * represents the X coordinate of the first vertex, the second value
+ * represents the Y coordinate of the first vertex, continuing in the same
+ * fashion for the rest of the vertices. (num_points - 1) segments will
+ * be constructed.
+ **/
+void
+cogl_polyline (ClutterFixed *coords,
+ gint num_points)
+{
+ gint c = 0;
+
+ cogl_path_move_to (coords[0], coords[1]);
+
+ for (c = 1; c < num_points; ++c)
+ cogl_path_line_to (coords[2*c], coords[2*c+1]);
+}
+
+
+/**
+ * cogl_polygon:
+ * @coords: A pointer to the first element of an array of fixed-point
+ * values that specify the vertex coordinates.
+ * @num_points: The total number of vertices.
+ *
+ * Clears the previously constructed shape and constructs a polygonal
+ * shape of the given number of vertices.
+ *
+ * The coords array must contain 2 * num_points values. The first value
+ * represents the X coordinate of the first vertex, the second value
+ * represents the Y coordinate of the first vertex, continuing in the same
+ * fashion for the rest of the vertices.
+ **/
+void
+cogl_polygon (ClutterFixed *coords,
+ gint num_points)
+{
+ cogl_polyline (coords, num_points);
+ cogl_path_close ();
+}
+
+/**
+ * cogl_rectangle:
+ * @x: X coordinate of the top-left corner.
+ * @y: Y coordinate of the top-left corner.
+ * @width: Rectangle width.
+ * @height: Rectangle height.
+ *
+ * Clears the previously constructed shape and constructs a rectangular
+ * shape at the given coordinates.
+ **/
+void
+cogl_rectangle (ClutterFixed x,
+ ClutterFixed y,
+ ClutterFixed width,
+ ClutterFixed height)
+{
+ cogl_path_move_to (x, y);
+ cogl_path_line_to (x + width, y);
+ cogl_path_line_to (x + width, y + height);
+ cogl_path_line_to (x, y + height);
+ cogl_path_close ();
+}
+
+static void
+_cogl_arc (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_1,
+ ClutterAngle angle_2,
+ ClutterAngle angle_step,
+ guint move_first)
+{
+ ClutterAngle a = 0x0;
+ ClutterAngle temp = 0x0;
+ ClutterFixed cosa = 0x0;
+ ClutterFixed sina = 0x0;
+ ClutterFixed px = 0x0;
+ ClutterFixed py = 0x0;
+
+ /* Fix invalid angles */
+
+ if (angle_1 == angle_2 || angle_step == 0x0)
+ return;
+
+ if (angle_step < 0x0)
+ angle_step = -angle_step;
+
+ if (angle_2 < angle_1)
+ {
+ temp = angle_1;
+ angle_1 = angle_2;
+ angle_2 = temp;
+ }
+
+ /* Walk the arc by given step */
+
+ for (a = angle_1; a < angle_2; a += angle_step)
+ {
+ cosa = clutter_cosi (a);
+ sina = clutter_sini (a);
+
+ px = center_x + CFX_MUL (cosa, radius_x);
+ py = center_y + CFX_MUL (sina, radius_y);
+
+ if (a == angle_1 && move_first)
+ cogl_path_move_to (px, py);
+ else
+ cogl_path_line_to (px, py);
+ }
+}
+
+/**
+ * cogl_path_arc:
+ * @center_x: X coordinate of the elliptical arc center
+ * @center_y: Y coordinate of the elliptical arc center
+ * @radius_x: X radius of the elliptical arc
+ * @radius_y: Y radious of the elliptical arc
+ * @angle_1: Angle in the unit-circle at which the arc begin
+ * @angle_2: Angle in the unit-circle at which the arc ends
+ * @angle_step: Angle increment resolution for subdivision
+ *
+ * Adds an elliptical arc segment to the current path. A straight line
+ * segment will link the current pen location with the first vertex
+ * of the arc.
+ **/
+void
+cogl_path_arc (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_1,
+ ClutterAngle angle_2,
+ ClutterAngle angle_step)
+{
+ _cogl_arc (center_x, center_y,
+ radius_x, radius_y,
+ angle_1, angle_2,
+ angle_step, 0 /* no move */);
+}
+
+/**
+ * cogl_path_arc_rel:
+ * @center_x: X offset from the current pen location of the elliptical
+ * arc center
+ * @center_y: Y offset from the current pen location of the elliptical
+ * arc center
+ * @radius_x: X radius of the elliptical arc
+ * @radius_y: Y radious of the elliptical arc
+ * @angle_1: Angle in the unit-circle at which the arc begin
+ * @angle_2: Angle in the unit-circle at which the arc ends
+ * @angle_step: Angle increment resolution for subdivision
+ *
+ * Adds an elliptical arc segment to the current path. A straight line
+ * segment will link the current pen location with the first vertex
+ * of the arc.
+ **/
+void
+cogl_path_arc_rel (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_1,
+ ClutterAngle angle_2,
+ ClutterAngle angle_step)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ _cogl_arc (ctx->path_pen.x + center_x,
+ ctx->path_pen.y + center_y,
+ radius_x, radius_y,
+ angle_1, angle_2,
+ angle_step, 0 /* no move */);
+}
+
+/**
+ * cogl_arc:
+ * @center_x: X coordinate of the elliptical arc center
+ * @center_y: Y coordinate of the elliptical arc center
+ * @radius_x: X radius of the elliptical arc
+ * @radius_y: Y radious of the elliptical arc
+ * @angle_1: Angle in the unit-circle at which the arc begin
+ * @angle_2: Angle in the unit-circle at which the arc ends
+ * @angle_step: Angle increment resolution for subdivision
+ *
+ * Clears the previously constructed shape and constructs and elliptical arc
+ * shape.
+ **/
+void
+cogl_arc (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_1,
+ ClutterAngle angle_2,
+ ClutterAngle angle_step)
+{
+ _cogl_arc (center_x, center_y,
+ radius_x, radius_y,
+ angle_1, angle_2,
+ angle_step, 1 /* move first */);
+}
+
+/**
+ * cogl_ellipse:
+ * @center_x: X coordinate of the ellipse center
+ * @center_y: Y coordinate of the ellipse center
+ * @radius_x: X radius of the ellipse
+ * @radius_y: Y radius of the ellipse
+ * @angle_step: Angle increment resolution for subdivision
+ *
+ * Clears the previously constructed shape and constructs an ellipse
+ * shape.
+ **/
+void
+cogl_ellipse (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_step)
+{
+
+ /* FIXME: if shows to be slow might be optimized
+ * by mirroring just a quarter of it */
+
+ _cogl_arc (center_x, center_y,
+ radius_x, radius_y,
+ 0, CLUTTER_ANGLE_FROM_DEG(360),
+ angle_step, 1 /* move first */);
+
+ cogl_path_close();
+}
+
+/**
+ * cogl_round_rectangle:
+ * @x: X coordinate of the top-left corner
+ * @y: Y coordinate of the top-left corner
+ * @width: Width of the rectangle
+ * @height: Height of the rectangle
+ * @radius: Radius of the corner arcs.
+ * @arc_step: Angle increment resolution for subdivision of
+ * the corner arcs.
+ *
+ * Clears the previously constructed shape and constructs a rectangular
+ * shape with rounded corners.
+ **/
+void
+cogl_round_rectangle (ClutterFixed x,
+ ClutterFixed y,
+ ClutterFixed width,
+ ClutterFixed height,
+ ClutterFixed radius,
+ ClutterAngle arc_step)
+{
+ ClutterFixed inner_width = width - (radius << 1);
+ ClutterFixed inner_height = height - (radius << 1);
+
+ cogl_path_move_to (x, y + radius);
+ cogl_path_arc_rel (radius, 0,
+ radius, radius,
+ CLUTTER_ANGLE_FROM_DEG (180),
+ CLUTTER_ANGLE_FROM_DEG (270),
+ arc_step);
+
+ cogl_path_h_line_to_rel (inner_width);
+ cogl_path_arc_rel (0, radius,
+ radius, radius,
+ CLUTTER_ANGLE_FROM_DEG (-90),
+ CLUTTER_ANGLE_FROM_DEG (0),
+ arc_step);
+
+ cogl_path_v_line_to_rel (inner_height);
+ cogl_path_arc_rel (-radius, 0,
+ radius, radius,
+ CLUTTER_ANGLE_FROM_DEG (0),
+ CLUTTER_ANGLE_FROM_DEG (90),
+ arc_step);
+
+ cogl_path_h_line_to_rel (-inner_width);
+ cogl_path_arc_rel (0, -radius,
+ radius, radius,
+ CLUTTER_ANGLE_FROM_DEG (90),
+ CLUTTER_ANGLE_FROM_DEG (180),
+ arc_step);
+
+ cogl_path_close ();
+}
+
+static void
+_cogl_path_bezier2_sub (CoglBezQuad *quad)
+{
+ CoglBezQuad quads[_COGL_MAX_BEZ_RECURSE_DEPTH];
+ CoglBezQuad *qleft;
+ CoglBezQuad *qright;
+ CoglBezQuad *q;
+ CoglFixedVec2 mid;
+ CoglFixedVec2 dif;
+ CoglFixedVec2 c1;
+ CoglFixedVec2 c2;
+ CoglFixedVec2 c3;
+ gint qindex;
+
+ /* Put first curve on stack */
+ quads[0] = *quad;
+ qindex = 0;
+
+ /* While stack is not empty */
+ while (qindex >= 0)
+ {
+
+ q = &quads[qindex];
+
+ /* Calculate distance of control point from its
+ * counterpart on the line between end points */
+ mid.x = ((q->p1.x + q->p3.x) >> 1);
+ mid.y = ((q->p1.y + q->p3.y) >> 1);
+ dif.x = (q->p2.x - mid.x);
+ dif.y = (q->p2.y - mid.y);
+ if (dif.x < 0) dif.x = -dif.x;
+ if (dif.y < 0) dif.y = -dif.y;
+
+ /* Cancel if the curve is flat enough */
+ if (dif.x + dif.y <= CFX_ONE
+ || qindex == _COGL_MAX_BEZ_RECURSE_DEPTH - 1)
+ {
+ /* Add subdivision point (skip last) */
+ if (qindex == 0) return;
+ _cogl_path_add_node (q->p3.x, q->p3.y);
+ --qindex; continue;
+ }
+
+ /* Left recursion goes on top of stack! */
+ qright = q; qleft = &quads[++qindex];
+
+ /* Subdivide into 2 sub-curves */
+ c1.x = ((q->p1.x + q->p2.x) >> 1);
+ c1.y = ((q->p1.y + q->p2.y) >> 1);
+ c3.x = ((q->p2.x + q->p3.x) >> 1);
+ c3.y = ((q->p2.y + q->p3.y) >> 1);
+ c2.x = ((c1.x + c3.x) >> 1);
+ c2.y = ((c1.y + c3.y) >> 1);
+
+ /* Add left recursion onto stack */
+ qleft->p1 = q->p1;
+ qleft->p2 = c1;
+ qleft->p3 = c2;
+
+ /* Add right recursion onto stack */
+ qright->p1 = c2;
+ qright->p2 = c3;
+ qright->p3 = q->p3;
+ }
+}
+
+static void
+_cogl_path_bezier3_sub (CoglBezCubic *cubic)
+{
+ CoglBezCubic cubics[_COGL_MAX_BEZ_RECURSE_DEPTH];
+ CoglBezCubic *cleft;
+ CoglBezCubic *cright;
+ CoglBezCubic *c;
+ CoglFixedVec2 dif1;
+ CoglFixedVec2 dif2;
+ CoglFixedVec2 mm;
+ CoglFixedVec2 c1;
+ CoglFixedVec2 c2;
+ CoglFixedVec2 c3;
+ CoglFixedVec2 c4;
+ CoglFixedVec2 c5;
+ gint cindex;
+
+ /* Put first curve on stack */
+ cubics[0] = *cubic;
+ cindex = 0;
+
+ while (cindex >= 0)
+ {
+ c = &cubics[cindex];
+
+#define CFX_MUL2(x) ((x) << 1)
+#define CFX_MUL3(x) (((x) << 1) + (x))
+#define CFX_SQ(x) CFX_MUL (x, x)
+
+ /* Calculate distance of control points from their
+ * counterparts on the line between end points */
+ dif1.x = CFX_MUL3 (c->p2.x) - CFX_MUL2 (c->p1.x) - c->p4.x;
+ dif1.y = CFX_MUL3 (c->p2.y) - CFX_MUL2 (c->p1.y) - c->p4.y;
+ dif2.x = CFX_MUL3 (c->p3.x) - CFX_MUL2 (c->p4.x) - c->p1.x;
+ dif2.y = CFX_MUL3 (c->p3.y) - CFX_MUL2 (c->p4.y) - c->p1.y;
+ if (dif1.x < 0) dif1.x = -dif1.x;
+ if (dif1.y < 0) dif1.y = -dif1.y;
+ if (dif2.x < 0) dif2.x = -dif2.x;
+ if (dif2.y < 0) dif2.y = -dif2.y;
+
+#undef CFX_MUL2
+#undef CFX_MUL3
+#undef CFX_SQ
+
+ /* Pick the greatest of two distances */
+ if (dif1.x < dif2.x) dif1.x = dif2.x;
+ if (dif1.y < dif2.y) dif1.y = dif2.y;
+
+ /* Cancel if the curve is flat enough */
+ if (dif1.x + dif1.y <= CFX_ONE
+ || cindex == _COGL_MAX_BEZ_RECURSE_DEPTH-1)
+ {
+ /* Add subdivision point (skip last) */
+ if (cindex == 0) return;
+ _cogl_path_add_node (c->p4.x, c->p4.y);
+ --cindex; continue;
+ }
+
+ /* Left recursion goes on top of stack! */
+ cright = c; cleft = &cubics[++cindex];
+
+ /* Subdivide into 2 sub-curves */
+ c1.x = ((c->p1.x + c->p2.x) >> 1);
+ c1.y = ((c->p1.y + c->p2.y) >> 1);
+ mm.x = ((c->p2.x + c->p3.x) >> 1);
+ mm.y = ((c->p2.y + c->p3.y) >> 1);
+ c5.x = ((c->p3.x + c->p4.x) >> 1);
+ c5.y = ((c->p3.y + c->p4.y) >> 1);
+
+ c2.x = ((c1.x + mm.x) >> 1);
+ c2.y = ((c1.y + mm.y) >> 1);
+ c4.x = ((mm.x + c5.x) >> 1);
+ c4.y = ((mm.y + c5.y) >> 1);
+
+ c3.x = ((c2.x + c4.x) >> 1);
+ c3.y = ((c2.y + c4.y) >> 1);
+
+ /* Add left recursion to stack */
+ cleft->p1 = c->p1;
+ cleft->p2 = c1;
+ cleft->p3 = c2;
+ cleft->p4 = c3;
+
+ /* Add right recursion to stack */
+ cright->p1 = c3;
+ cright->p2 = c4;
+ cright->p3 = c5;
+ cright->p4 = c->p4;
+ }
+}
+
+/**
+ * cogl_path_bezier2_to:
+ * @x1: X coordinate of the second bezier control point
+ * @y1: Y coordinate of the second bezier control point
+ * @x2: X coordinate of the third bezier control point
+ * @y2: Y coordinate of the third bezier control point
+ *
+ * Adds a quadratic bezier curve segment to the current path with the given
+ * second and third control points and using current pen location as the
+ * first control point.
+ **/
+void
+cogl_path_bezier2_to (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ CoglBezQuad quad;
+
+ /* Prepare quadratic curve */
+ quad.p1 = ctx->path_pen;
+ quad.p2.x = x1;
+ quad.p2.y = y1;
+ quad.p3.x = x2;
+ quad.p3.y = y2;
+
+ /* Run subdivision */
+ _cogl_path_bezier2_sub (&quad);
+
+ /* Add last point */
+ _cogl_path_add_node (quad.p3.x, quad.p3.y);
+ ctx->path_pen = quad.p3;
+}
+
+/**
+ * cogl_path_bezier3_to:
+ * @x1: X coordinate of the second bezier control point
+ * @y1: Y coordinate of the second bezier control point
+ * @x2: X coordinate of the third bezier control point
+ * @y2: Y coordinate of the third bezier control point
+ * @x3: X coordinate of the fourth bezier control point
+ * @y3: Y coordinate of the fourth bezier control point
+ *
+ * Adds a cubic bezier curve segment to the current path with the given
+ * second, third and fourth control points and using current pen location
+ * as the first control point.
+ **/
+void
+cogl_path_bezier3_to (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed x3,
+ ClutterFixed y3)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ CoglBezCubic cubic;
+
+ /* Prepare cubic curve */
+ cubic.p1 = ctx->path_pen;
+ cubic.p2.x = x1;
+ cubic.p2.y = y1;
+ cubic.p3.x = x2;
+ cubic.p3.y = y2;
+ cubic.p4.x = x3;
+ cubic.p4.y = y3;
+
+ /* Run subdivision */
+ _cogl_path_bezier3_sub (&cubic);
+
+ /* Add last point */
+ _cogl_path_add_node (cubic.p4.x, cubic.p4.y);
+ ctx->path_pen = cubic.p4;
+}
+
+/**
+ * cogl_path_bezier2_to_rel:
+ * @x1: X coordinate of the second bezier control point
+ * @y1: Y coordinate of the second bezier control point
+ * @x2: X coordinate of the third bezier control point
+ * @y2: Y coordinate of the third bezier control point
+ *
+ * Adds a quadratic bezier curve segment to the current path with the given
+ * second and third control points and using current pen location as the
+ * first control point. The given coordinates are relative to the current
+ * pen location.
+ **/
+void
+cogl_path_bezier2_to_rel (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_bezier2_to (ctx->path_pen.x + x1,
+ ctx->path_pen.y + y2,
+ ctx->path_pen.x + x2,
+ ctx->path_pen.y + y2);
+}
+
+/**
+ * cogl_path_bezier3_to_rel:
+ * @x1: X coordinate of the second bezier control point
+ * @y1: Y coordinate of the second bezier control point
+ * @x2: X coordinate of the third bezier control point
+ * @y2: Y coordinate of the third bezier control point
+ * @x3: X coordinate of the fourth bezier control point
+ * @y3: Y coordinate of the fourth bezier control point
+ *
+ * Adds a cubic bezier curve segment to the current path with the given
+ * second, third and fourth control points and using current pen location
+ * as the first control point. The given coordinates are relative to the
+ * current pen location.
+ **/
+void
+cogl_path_bezier3_to_rel (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed x3,
+ ClutterFixed y3)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_bezier3_to (ctx->path_pen.x + x1,
+ ctx->path_pen.y + y2,
+ ctx->path_pen.x + x2,
+ ctx->path_pen.y + y2,
+ ctx->path_pen.x + x3,
+ ctx->path_pen.y + y3);
+}
diff --git a/gl/cogl-primitives.h b/gl/cogl-primitives.h
new file mode 100644
index 00000000..f43ab6a4
--- /dev/null
+++ b/gl/cogl-primitives.h
@@ -0,0 +1,61 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_PRIMITIVES_H
+#define __COGL_PRIMITIVES_H
+
+typedef struct _CoglFixedVec2 CoglFixedVec2;
+typedef struct _CoglFloatVec2 CoglFloatVec2;
+typedef struct _CoglBezQuad CoglBezQuad;
+typedef struct _CoglBezCubic CoglBezCubic;
+
+struct _CoglFixedVec2
+{
+ ClutterFixed x;
+ ClutterFixed y;
+};
+
+struct _CoglFloatVec2
+{
+ GLfloat x;
+ GLfloat y;
+};
+
+struct _CoglBezQuad
+{
+ CoglFixedVec2 p1;
+ CoglFixedVec2 p2;
+ CoglFixedVec2 p3;
+};
+
+struct _CoglBezCubic
+{
+ CoglFixedVec2 p1;
+ CoglFixedVec2 p2;
+ CoglFixedVec2 p3;
+ CoglFixedVec2 p4;
+};
+
+#endif /* __COGL_PRIMITIVES_H */
diff --git a/gl/cogl-texture.c b/gl/cogl-texture.c
new file mode 100644
index 00000000..5e4d6ee5
--- /dev/null
+++ b/gl/cogl-texture.c
@@ -0,0 +1,2303 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-util.h"
+#include "cogl-bitmap.h"
+#include "cogl-texture.h"
+#include "cogl-context.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+/*
+#define COGL_DEBUG 1
+
+#define GE(x) \
+{ \
+ glGetError(); x; \
+ GLuint err = glGetError(); \
+ if (err != 0) \
+ printf("err: 0x%x\n", err); \
+} */
+
+struct _CoglSpanIter
+{
+ gint index;
+ GArray *array;
+ CoglTexSliceSpan *span;
+ ClutterFixed pos;
+ ClutterFixed next_pos;
+ ClutterFixed origin;
+ ClutterFixed cover_start;
+ ClutterFixed cover_end;
+ ClutterFixed intersect_start;
+ ClutterFixed intersect_end;
+ ClutterFixed intersect_start_local;
+ ClutterFixed intersect_end_local;
+ gboolean intersects;
+};
+
+/*
+ * _cogl_texture_handle_find:
+ * @handle: A texture handle
+ *
+ * Returns the index of the given CoglHandle if found in the
+ * handle array.
+ */
+static gint
+_cogl_texture_handle_find (CoglHandle handle)
+{
+ _COGL_GET_CONTEXT (ctx, -1);
+
+ gint i;
+
+ if (ctx->texture_handles == NULL)
+ return -1;
+
+ for (i=0; i < ctx->texture_handles->len; ++i)
+ if (g_array_index (ctx->texture_handles, CoglHandle, i) == handle)
+ return i;
+
+ return -1;
+}
+
+/*
+ * _cogl_texture_handle_new:
+ * @tex: A pointer to an allocated CoglTexture structure
+ *
+ * Returns a new CoglHandle for the given CoglTexture
+ * object.
+ */
+static CoglHandle
+_cogl_texture_handle_new (CoglTexture *tex)
+{
+ _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
+
+ CoglHandle handle = (CoglHandle)tex;
+
+ if (ctx->texture_handles == NULL)
+ ctx->texture_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle));
+
+ g_array_append_val (ctx->texture_handles, handle);
+
+ return handle;
+}
+
+/*
+ * _cogl_texture_handle_release:
+ * @handle: A valid CoglHandle
+ *
+ * Frees the given CoglHandle for use with another object.
+ */
+static void
+_cogl_texture_handle_release (CoglHandle handle)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ gint i;
+
+ if ( (i = _cogl_texture_handle_find (handle)) == -1)
+ return;
+
+ g_array_remove_index_fast (ctx->texture_handles, i);
+}
+
+/*
+ * _cogl_texture_pointer_from_handle:
+ * @handle: A valid CoglHandle
+ *
+ * Returns a pointer to the texture object referenced by
+ * given handle.
+ */
+CoglTexture *
+_cogl_texture_pointer_from_handle (CoglHandle handle)
+{
+ return (CoglTexture*) handle;
+}
+
+gboolean
+cogl_is_texture (CoglHandle handle)
+{
+ if (handle == COGL_INVALID_HANDLE)
+ return FALSE;
+
+ return _cogl_texture_handle_find (handle) >= 0;
+}
+
+static void
+_cogl_texture_bitmap_free (CoglTexture *tex)
+{
+ if (tex->bitmap.data != NULL && tex->bitmap_owner)
+ g_free (tex->bitmap.data);
+
+ tex->bitmap.data = NULL;
+ tex->bitmap_owner = FALSE;
+}
+
+static void
+_cogl_texture_bitmap_swap (CoglTexture *tex,
+ CoglBitmap *new_bitmap)
+{
+ if (tex->bitmap.data != NULL && tex->bitmap_owner)
+ g_free (tex->bitmap.data);
+
+ tex->bitmap = *new_bitmap;
+ tex->bitmap_owner = TRUE;
+}
+
+static void
+_cogl_span_iter_update (CoglSpanIter *iter)
+{
+ /* Pick current span */
+ iter->span = &g_array_index (iter->array,
+ CoglTexSliceSpan,
+ iter->index);
+
+ /* Offset next position by span size */
+ iter->next_pos = iter->pos +
+ CLUTTER_INT_TO_FIXED (iter->span->size - iter->span->waste);
+
+ /* Check if span intersects the area to cover */
+ if (iter->next_pos <= iter->cover_start ||
+ iter->pos >= iter->cover_end)
+ {
+ /* Intersection undefined */
+ iter->intersects = FALSE;
+ return;
+ }
+
+ iter->intersects = TRUE;
+
+ /* Clip start position to coverage area */
+ if (iter->pos < iter->cover_start)
+ iter->intersect_start = iter->cover_start;
+ else
+ iter->intersect_start = iter->pos;
+
+ /* Clip end position to coverage area */
+ if (iter->next_pos > iter->cover_end)
+ iter->intersect_end = iter->cover_end;
+ else
+ iter->intersect_end = iter->next_pos;
+}
+
+static void
+_cogl_span_iter_begin (CoglSpanIter *iter,
+ GArray *array,
+ ClutterFixed origin,
+ ClutterFixed cover_start,
+ ClutterFixed cover_end)
+{
+ /* Copy info */
+ iter->index = 0;
+ iter->array = array;
+ iter->span = NULL;
+ iter->origin = origin;
+ iter->cover_start = cover_start;
+ iter->cover_end = cover_end;
+ iter->pos = iter->origin;
+
+ /* Update intersection */
+ _cogl_span_iter_update (iter);
+}
+
+void
+_cogl_span_iter_next (CoglSpanIter *iter)
+{
+ /* Move current position */
+ iter->pos = iter->next_pos;
+
+ /* Pick next slice (wrap when last reached) */
+ iter->index = (iter->index + 1) % iter->array->len;
+
+ /* Update intersection */
+ _cogl_span_iter_update (iter);
+}
+
+static gboolean
+_cogl_span_iter_end (CoglSpanIter *iter)
+{
+ /* End reached when whole area covered */
+ return iter->pos >= iter->cover_end;
+}
+
+static void
+_cogl_subregion_gl_store_rules (gint bmp_rowstride,
+ gint bmp_width,
+ gint bmp_bpp,
+ gint src_x,
+ gint src_y,
+ gboolean pack)
+{
+ const GLenum ALIGNMENT = pack ?
+ GL_PACK_ALIGNMENT : GL_UNPACK_ALIGNMENT;
+
+ const GLenum ROW_LENGTH = pack ?
+ GL_PACK_ROW_LENGTH : GL_UNPACK_ROW_LENGTH;
+
+ const GLenum SKIP_ROWS = pack ?
+ GL_PACK_SKIP_ROWS : GL_UNPACK_SKIP_ROWS;
+
+ const GLenum SKIP_PIXELS = pack ?
+ GL_PACK_SKIP_PIXELS : GL_UNPACK_SKIP_PIXELS;
+
+ /* Encode the part of the rowstride that is a multiple of bmp_bpp in
+ ROW_LENGTH and the remainder in ALIGNMENT */
+ GE( glPixelStorei (ROW_LENGTH, bmp_rowstride / bmp_bpp) );
+
+ if (bmp_rowstride == bmp_width * bmp_bpp)
+ {
+ GE( glPixelStorei (ALIGNMENT, 1) );
+ }
+ else
+ {
+ if ((bmp_rowstride % 4) == 0)
+ {
+ GE( glPixelStorei (ALIGNMENT, 4) );
+ }
+ else
+ {
+ if ((bmp_rowstride % 2) == 0)
+ GE( glPixelStorei (ALIGNMENT, 2) );
+ }
+ }
+
+ GE( glPixelStorei (SKIP_ROWS, src_y) );
+ GE( glPixelStorei (SKIP_PIXELS, src_x) );
+}
+
+static gboolean
+_cogl_texture_upload_to_gl (CoglTexture *tex)
+{
+ CoglTexSliceSpan *x_span;
+ CoglTexSliceSpan *y_span;
+ GLuint gl_handle;
+ gint bpp;
+ gint x,y;
+
+ bpp = _cogl_get_format_bpp (tex->bitmap.format);
+
+ /* Iterate vertical slices */
+ for (y = 0; y < tex->slice_y_spans->len; ++y)
+ {
+ y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y);
+
+ /* Iterate horizontal slices */
+ for (x = 0; x < tex->slice_x_spans->len; ++x)
+ {
+ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x);
+
+ /* Pick the gl texture object handle */
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint,
+ y * tex->slice_x_spans->len + x);
+
+ /* Setup gl alignment to match rowstride and top-left corner */
+ _cogl_subregion_gl_store_rules (tex->bitmap.rowstride,
+ tex->bitmap.width,
+ bpp,
+ x_span->start,
+ y_span->start,
+ FALSE);
+
+ /* Upload new image data */
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+ GE( glTexSubImage2D (tex->gl_target, 0, 0, 0,
+ x_span->size - x_span->waste,
+ y_span->size - y_span->waste,
+ tex->gl_format, tex->gl_type,
+ tex->bitmap.data) );
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_cogl_texture_download_from_gl (CoglTexture *tex,
+ CoglBitmap *target_bmp,
+ GLuint target_gl_format,
+ GLuint target_gl_type)
+{
+ CoglTexSliceSpan *x_span;
+ CoglTexSliceSpan *y_span;
+ GLuint gl_handle;
+ gint bpp;
+ gint x,y;
+ CoglBitmap slice_bmp;
+
+ bpp = _cogl_get_format_bpp (target_bmp->format);
+
+ /* Iterate vertical slices */
+ for (y = 0; y < tex->slice_y_spans->len; ++y)
+ {
+ y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y);
+
+ /* Iterate horizontal slices */
+ for (x = 0; x < tex->slice_x_spans->len; ++x)
+ {
+ /*if (x != 0 || y != 1) continue;*/
+ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x);
+
+ /* Pick the gl texture object handle */
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint,
+ y * tex->slice_x_spans->len + x);
+
+ /* If there's any waste we need to copy manually
+ (no glGetTexSubImage) */
+
+ if (y_span->waste != 0 || x_span->waste != 0)
+ {
+ /* Setup temp bitmap for slice subregion */
+ slice_bmp.format = tex->bitmap.format;
+ slice_bmp.width = x_span->size;
+ slice_bmp.height = y_span->size;
+ slice_bmp.rowstride = bpp * slice_bmp.width;
+ slice_bmp.data = (guchar*) g_malloc (slice_bmp.rowstride *
+ slice_bmp.height);
+
+ /* Setup gl alignment to 0,0 top-left corner */
+ _cogl_subregion_gl_store_rules (slice_bmp.rowstride,
+ slice_bmp.width,
+ bpp, 0, 0, TRUE);
+
+ /* Download slice image data into temp bmp */
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+ GE (glGetTexImage (tex->gl_target, 0,
+ target_gl_format,
+ target_gl_type,
+ slice_bmp.data) );
+
+ /* Copy portion of slice from temp to target bmp */
+ _cogl_bitmap_copy_subregion (&slice_bmp,
+ target_bmp,
+ 0, 0,
+ x_span->start,
+ y_span->start,
+ x_span->size - x_span->waste,
+ y_span->size - y_span->waste);
+ /* Free temp bitmap */
+ g_free (slice_bmp.data);
+ }
+ else
+ {
+ /* Setup gl alignment to match rowstride and top-left corner */
+
+ /* FIXME: for some strange reason any value other than 0
+ * for GL_PACK_SKIP_PIXELS or GL_PACK_SKIP_ROWS corrupts the
+ * memory. As a workaround we offset data pointer manually
+
+ _cogl_subregion_gl_store_rules (target_bmp->rowstride,
+ target_bmp->width,
+ bpp,
+ x_span->start,
+ y_span->start,
+ TRUE);*/
+ _cogl_subregion_gl_store_rules (target_bmp->rowstride,
+ target_bmp->width,
+ bpp,
+ 0, 0,
+ TRUE);
+
+ /* Download slice image data */
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+ GE( glGetTexImage (tex->gl_target, 0,
+ target_gl_format,
+ target_gl_type,
+ target_bmp->data +
+ x_span->start * bpp +
+ y_span->start * target_bmp->rowstride) );
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_cogl_texture_upload_subregion_to_gl (CoglTexture *tex,
+ gint src_x,
+ gint src_y,
+ gint dst_x,
+ gint dst_y,
+ gint width,
+ gint height,
+ CoglBitmap *source_bmp,
+ GLuint source_gl_format,
+ GLuint source_gl_type)
+{
+ gint bpp;
+ CoglSpanIter x_iter;
+ CoglSpanIter y_iter;
+ GLuint gl_handle;
+ gint source_x = 0, source_y = 0;
+ gint inter_w = 0, inter_h = 0;
+ gint local_x = 0, local_y = 0;
+
+ bpp = _cogl_get_format_bpp (source_bmp->format);
+
+ /* Iterate vertical spans */
+ for (source_y = src_y,
+ _cogl_span_iter_begin (&y_iter, tex->slice_y_spans,
+ 0, CLUTTER_INT_TO_FIXED (dst_y),
+ CLUTTER_INT_TO_FIXED (dst_y + height));
+
+ !_cogl_span_iter_end (&y_iter);
+
+ _cogl_span_iter_next (&y_iter),
+ source_y += inter_h )
+ {
+ /* Iterate horizontal spans */
+ for (source_x = src_x,
+ _cogl_span_iter_begin (&x_iter, tex->slice_x_spans,
+ 0, CLUTTER_INT_TO_FIXED (dst_x),
+ CLUTTER_INT_TO_FIXED (dst_x + width));
+
+ !_cogl_span_iter_end (&x_iter);
+
+ _cogl_span_iter_next (&x_iter),
+ source_x += inter_w )
+ {
+ /* Pick intersection width and height */
+ inter_w = CLUTTER_FIXED_TO_INT (x_iter.intersect_end -
+ x_iter.intersect_start);
+ inter_h = CLUTTER_FIXED_TO_INT (y_iter.intersect_end -
+ y_iter.intersect_start);
+
+ /* Localize intersection top-left corner to slice*/
+ local_x = CLUTTER_FIXED_TO_INT (x_iter.intersect_start -
+ x_iter.pos);
+ local_y = CLUTTER_FIXED_TO_INT (y_iter.intersect_start -
+ y_iter.pos);
+
+ /* Pick slice GL handle */
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint,
+ y_iter.index * tex->slice_x_spans->len +
+ x_iter.index);
+
+ /* Setup gl alignment to match rowstride and top-left corner */
+
+ _cogl_subregion_gl_store_rules (source_bmp->rowstride,
+ source_bmp->width,
+ bpp,
+ source_x,
+ source_y,
+ FALSE);
+
+ /* Upload new image data */
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+ GE( glTexSubImage2D (tex->gl_target, 0,
+ local_x, local_y,
+ inter_w, inter_h,
+ source_gl_format,
+ source_gl_type,
+ source_bmp->data) );
+ }
+ }
+
+ return TRUE;
+}
+
+static gint
+_cogl_rect_slices_for_size (gint size_to_fill,
+ gint max_span_size,
+ gint max_waste,
+ GArray *out_spans)
+{
+ gint n_spans = 0;
+ CoglTexSliceSpan span;
+
+ /* Init first slice span */
+ span.start = 0;
+ span.size = max_span_size;
+ span.waste = 0;
+
+ /* Repeat until whole area covered */
+ while (size_to_fill >= span.size)
+ {
+ /* Add another slice span of same size */
+ if (out_spans) g_array_append_val (out_spans, span);
+ span.start += span.size;
+ size_to_fill -= span.size;
+ n_spans++;
+ }
+
+ /* Add one last smaller slice span */
+ if (size_to_fill > 0)
+ {
+ span.size = size_to_fill;
+ if (out_spans) g_array_append_val (out_spans, span);
+ n_spans++;
+ }
+
+ return n_spans;
+}
+
+static gint
+_cogl_pot_slices_for_size (gint size_to_fill,
+ gint max_span_size,
+ gint max_waste,
+ GArray *out_spans)
+{
+ gint n_spans = 0;
+ CoglTexSliceSpan span;
+
+ /* Init first slice span */
+ span.start = 0;
+ span.size = max_span_size;
+ span.waste = 0;
+
+ /* Fix invalid max_waste */
+ if (max_waste < 0) max_waste = 0;
+
+ while (TRUE)
+ {
+ /* Is the whole area covered? */
+ if (size_to_fill > span.size)
+ {
+ /* Not yet - add a span of this size */
+ if (out_spans) g_array_append_val (out_spans, span);
+ span.start += span.size;
+ size_to_fill -= span.size;
+ n_spans++;
+ }
+ else if (span.size - size_to_fill <= max_waste)
+ {
+ /* Yes and waste is small enough */
+ span.waste = span.size - size_to_fill;
+ if (out_spans) g_array_append_val (out_spans, span);
+ return ++n_spans;
+ }
+ else
+ {
+ /* Yes but waste is too large */
+ while (span.size - size_to_fill > max_waste)
+ {
+ span.size /= 2;
+ g_assert (span.size > 0);
+ }
+ }
+ }
+
+ /* Can't get here */
+ return 0;
+}
+
+static gboolean
+_cogl_texture_size_supported (GLenum gl_target,
+ GLenum gl_format,
+ GLenum gl_type,
+ int width,
+ int height)
+{
+ if (gl_target == GL_TEXTURE_RECTANGLE_ARB)
+ {
+ /* There is no proxy rectangle texture target so best we can
+ * do is to check against the safest value (although depending
+ * on our specific format and type the size could be supported
+ * when it seems it is not) */
+
+ GLint max_size = 0;
+
+ GE( glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &max_size) );
+
+ return (max_size && width <= max_size && height <= max_size);
+ }
+ else if (gl_target == GL_TEXTURE_2D)
+ {
+ /* Proxy texture allows for a quick check for supported size */
+
+ GLint new_width = 0;
+
+ GE( glTexImage2D (GL_PROXY_TEXTURE_2D, 0, GL_RGBA,
+ width, height, 0 /* border */,
+ gl_format, gl_type, NULL) );
+
+ GE( glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0,
+ GL_TEXTURE_WIDTH, &new_width) );
+
+ return new_width != 0;
+ }
+ else
+ {
+ /* not used */
+ return 0;
+ }
+}
+
+static gboolean
+_cogl_texture_slices_create (CoglTexture *tex)
+{
+ gint bpp;
+ gint max_width;
+ gint max_height;
+ GLuint *gl_handles;
+ gint n_x_slices;
+ gint n_y_slices;
+ gint n_slices;
+ gint x, y;
+ CoglTexSliceSpan *x_span;
+ CoglTexSliceSpan *y_span;
+ const GLfloat transparent_color[4] = { 0x00, 0x00, 0x00, 0x00 };
+
+ gint (*slices_for_size) (gint, gint, gint, GArray*);
+
+ bpp = _cogl_get_format_bpp (tex->bitmap.format);
+
+ /* Initialize size of largest slice according to supported features*/
+ if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT))
+ {
+ max_width = tex->bitmap.width;
+ max_height = tex->bitmap.height;
+ tex->gl_target = GL_TEXTURE_2D;
+ slices_for_size = _cogl_rect_slices_for_size;
+ }
+ else if (cogl_features_available (COGL_FEATURE_TEXTURE_RECTANGLE))
+ {
+ max_width = tex->bitmap.width;
+ max_height = tex->bitmap.height;
+ tex->gl_target = GL_TEXTURE_RECTANGLE_ARB;
+ slices_for_size = _cogl_rect_slices_for_size;
+ }
+ else
+ {
+ max_width = cogl_util_next_p2 (tex->bitmap.width);
+ max_height = cogl_util_next_p2 (tex->bitmap.height);
+ tex->gl_target = GL_TEXTURE_2D;
+ slices_for_size = _cogl_pot_slices_for_size;
+ }
+
+ /* Negative number means no slicing forced by the user */
+ if (tex->max_waste <= -1)
+ {
+ CoglTexSliceSpan span;
+
+ /* Check if size supported else bail out */
+ if (!_cogl_texture_size_supported (tex->gl_target,
+ tex->gl_format,
+ tex->gl_type,
+ max_width,
+ max_height))
+ {
+ return FALSE;
+ }
+
+ n_x_slices = 1;
+ n_y_slices = 1;
+
+ /* Init span arrays */
+ tex->slice_x_spans = g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan),
+ 1);
+
+ tex->slice_y_spans = g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan),
+ 1);
+
+ /* Add a single span for width and height */
+ span.start = 0;
+ span.size = max_width;
+ span.waste = max_width - tex->bitmap.width;
+ g_array_append_val (tex->slice_x_spans, span);
+
+ span.size = max_height;
+ span.waste = max_height - tex->bitmap.height;
+ g_array_append_val (tex->slice_y_spans, span);
+ }
+ else
+ {
+ /* Decrease the size of largest slice until supported by GL */
+ while (!_cogl_texture_size_supported (tex->gl_target,
+ tex->gl_format,
+ tex->gl_type,
+ max_width,
+ max_height))
+ {
+ /* Alternate between width and height */
+ if (max_width > max_height)
+ max_width /= 2;
+ else
+ max_height /= 2;
+
+ if (max_width == 0 || max_height == 0)
+ return FALSE;
+ }
+
+ /* Determine the slices required to cover the bitmap area */
+ n_x_slices = slices_for_size (tex->bitmap.width,
+ max_width, tex->max_waste,
+ NULL);
+
+ n_y_slices = slices_for_size (tex->bitmap.height,
+ max_height, tex->max_waste,
+ NULL);
+
+ /* Init span arrays with reserved size */
+ tex->slice_x_spans = g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan),
+ n_x_slices);
+
+ tex->slice_y_spans = g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan),
+ n_y_slices);
+
+ /* Fill span arrays with info */
+ slices_for_size (tex->bitmap.width,
+ max_width, tex->max_waste,
+ tex->slice_x_spans);
+
+ slices_for_size (tex->bitmap.height,
+ max_height, tex->max_waste,
+ tex->slice_y_spans);
+ }
+
+ /* Init and resize GL handle array */
+ n_slices = n_x_slices * n_y_slices;
+
+ tex->slice_gl_handles = g_array_sized_new (FALSE, FALSE,
+ sizeof (GLuint),
+ n_slices);
+
+ g_array_set_size (tex->slice_gl_handles, n_slices);
+
+
+ /* Hardware repeated tiling if supported, else tile in software*/
+ if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT)
+ && n_slices == 1)
+ tex->wrap_mode = GL_REPEAT;
+ else
+ tex->wrap_mode = GL_CLAMP_TO_EDGE;
+
+ /* Generate a "working set" of GL texture objects
+ * (some implementations might supported faster
+ * re-binding between textures inside a set) */
+ gl_handles = (GLuint*) tex->slice_gl_handles->data;
+
+ GE( glGenTextures (n_slices, gl_handles) );
+
+
+ /* Init each GL texture object */
+ for (y = 0; y < n_y_slices; ++y)
+ {
+ y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y);
+
+ for (x = 0; x < n_x_slices; ++x)
+ {
+ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x);
+
+#if COGL_DEBUG
+ printf ("CREATE SLICE (%d,%d)\n", x,y);
+ printf ("size: (%d x %d)\n",
+ x_span->size - x_span->waste,
+ y_span->size - y_span->waste);
+#endif
+ /* Setup texture parameters */
+ GE( glBindTexture (tex->gl_target, gl_handles[y * n_x_slices + x]) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) );
+
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S,
+ tex->wrap_mode) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T,
+ tex->wrap_mode) );
+
+ /* Use a transparent border color so that we can leave the
+ color buffer alone when using texture co-ordinates
+ outside of the texture */
+ GE( glTexParameterfv (tex->gl_target, GL_TEXTURE_BORDER_COLOR,
+ transparent_color) );
+
+ /* Pass NULL data to init size and internal format */
+ GE( glTexImage2D (tex->gl_target, 0, tex->gl_intformat,
+ x_span->size, y_span->size, 0,
+ tex->gl_format, tex->gl_type, 0) );
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+_cogl_texture_slices_free (CoglTexture *tex)
+{
+ if (tex->slice_x_spans != NULL)
+ g_array_free (tex->slice_x_spans, TRUE);
+
+ if (tex->slice_y_spans != NULL)
+ g_array_free (tex->slice_y_spans, TRUE);
+
+ if (tex->slice_gl_handles != NULL)
+ {
+ if (tex->is_foreign == FALSE)
+ {
+ GE( glDeleteTextures (tex->slice_gl_handles->len,
+ (GLuint*) tex->slice_gl_handles->data) );
+ }
+
+ g_array_free (tex->slice_gl_handles, TRUE);
+ }
+}
+
+static gboolean
+_cogl_pixel_format_from_gl_internal (GLenum gl_int_format,
+ CoglPixelFormat *out_format)
+{
+ /* It doesn't really matter we convert to exact same
+ format (some have no cogl match anyway) since format
+ is re-matched against cogl when getting or setting
+ texture image data.
+ */
+
+ switch (gl_int_format)
+ {
+ case GL_ALPHA: case GL_ALPHA4: case GL_ALPHA8:
+ case GL_ALPHA12: case GL_ALPHA16:
+
+ *out_format = COGL_PIXEL_FORMAT_A_8;
+ return TRUE;
+
+ case GL_LUMINANCE: case GL_LUMINANCE4: case GL_LUMINANCE8:
+ case GL_LUMINANCE12: case GL_LUMINANCE16:
+
+ *out_format = COGL_PIXEL_FORMAT_G_8;
+ return TRUE;
+
+ case GL_RGB: case GL_RGB4: case GL_RGB5: case GL_RGB8:
+ case GL_RGB10: case GL_RGB12: case GL_RGB16: case GL_R3_G3_B2:
+
+ *out_format = COGL_PIXEL_FORMAT_RGB_888;
+ return TRUE;
+
+ case GL_RGBA: case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1:
+ case GL_RGBA8: case GL_RGB10_A2: case GL_RGBA12: case GL_RGBA16:
+
+ *out_format = COGL_PIXEL_FORMAT_RGBA_8888;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static CoglPixelFormat
+_cogl_pixel_format_to_gl (CoglPixelFormat format,
+ GLenum *out_glintformat,
+ GLenum *out_glformat,
+ GLenum *out_gltype)
+{
+ CoglPixelFormat required_format;
+ GLenum glintformat = 0;
+ GLenum glformat = 0;
+ GLenum gltype = 0;
+
+ /* No premultiplied formats accepted by GL
+ * (FIXME: latest hardware?) */
+
+ if (format & COGL_PREMULT_BIT)
+ format = (format & COGL_UNPREMULT_MASK);
+
+ /* Everything else accepted
+ * (FIXME: check YUV support) */
+ required_format = format;
+
+ /* Find GL equivalents */
+ switch (format)
+ {
+ case COGL_PIXEL_FORMAT_A_8:
+ glintformat = GL_ALPHA;
+ glformat = GL_ALPHA;
+ gltype = GL_UNSIGNED_BYTE;
+ break;
+ case COGL_PIXEL_FORMAT_G_8:
+ glintformat = GL_LUMINANCE;
+ glformat = GL_LUMINANCE;
+ gltype = GL_UNSIGNED_BYTE;
+ break;
+
+ case COGL_PIXEL_FORMAT_RGB_888:
+ glintformat = GL_RGB;
+ glformat = GL_RGB;
+ gltype = GL_UNSIGNED_BYTE;
+ break;
+ case COGL_PIXEL_FORMAT_BGR_888:
+ glintformat = GL_RGB;
+ glformat = GL_BGR;
+ gltype = GL_UNSIGNED_BYTE;
+ break;
+ case COGL_PIXEL_FORMAT_RGBA_8888:
+ glintformat = GL_RGBA;
+ glformat = GL_RGBA;
+ gltype = GL_UNSIGNED_BYTE;
+ break;
+ case COGL_PIXEL_FORMAT_BGRA_8888:
+ glintformat = GL_RGBA;
+ glformat = GL_BGRA;
+ gltype = GL_UNSIGNED_BYTE;
+ break;
+
+ /* The following two types of channel ordering
+ * have no GL equivalent unless defined using
+ * system word byte ordering */
+ case COGL_PIXEL_FORMAT_ARGB_8888:
+ glintformat = GL_RGBA;
+ glformat = GL_BGRA;
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ gltype = GL_UNSIGNED_INT_8_8_8_8;
+#else
+ gltype = GL_UNSIGNED_INT_8_8_8_8_REV;
+#endif
+ break;
+
+ case COGL_PIXEL_FORMAT_ABGR_8888:
+ glintformat = GL_RGBA;
+ glformat = GL_RGBA;
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ gltype = GL_UNSIGNED_INT_8_8_8_8;
+#else
+ gltype = GL_UNSIGNED_INT_8_8_8_8_REV;
+#endif
+ break;
+
+ /* The following three types of channel ordering
+ * are always defined using system word byte
+ * ordering (even according to GLES spec) */
+ case COGL_PIXEL_FORMAT_RGB_565:
+ glintformat = GL_RGB;
+ glformat = GL_RGB;
+ gltype = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case COGL_PIXEL_FORMAT_RGBA_4444:
+ glintformat = GL_RGBA;
+ glformat = GL_RGBA;
+ gltype = GL_UNSIGNED_SHORT_4_4_4_4;
+ break;
+ case COGL_PIXEL_FORMAT_RGBA_5551:
+ glintformat = GL_RGBA;
+ glformat = GL_RGBA;
+ gltype = GL_UNSIGNED_SHORT_5_5_5_1;
+ break;
+
+ /* FIXME: check extensions for YUV support */
+ default:
+ break;
+ }
+
+ if (out_glintformat != NULL)
+ *out_glintformat = glintformat;
+ if (out_glformat != NULL)
+ *out_glformat = glformat;
+ if (out_gltype != NULL)
+ *out_gltype = gltype;
+
+ return required_format;
+}
+
+static gboolean
+_cogl_texture_bitmap_prepare (CoglTexture *tex,
+ CoglPixelFormat internal_format)
+{
+ CoglBitmap new_bitmap;
+ CoglPixelFormat new_data_format;
+ gboolean success;
+
+ /* Was there any internal conversion requested? */
+ if (internal_format == COGL_PIXEL_FORMAT_ANY)
+ internal_format = tex->bitmap.format;
+
+ /* Find closest format accepted by GL */
+ new_data_format = _cogl_pixel_format_to_gl (internal_format,
+ &tex->gl_intformat,
+ &tex->gl_format,
+ &tex->gl_type);
+
+ /* Convert to internal format */
+ if (new_data_format != tex->bitmap.format)
+ {
+ success = _cogl_bitmap_convert_and_premult (&tex->bitmap,
+ &new_bitmap,
+ new_data_format);
+
+ if (!success)
+ return FALSE;
+
+ /* Update texture with new data */
+ _cogl_texture_bitmap_swap (tex, &new_bitmap);
+ }
+
+ return TRUE;
+}
+
+static void
+_cogl_texture_free (CoglTexture *tex)
+{
+ /* Frees texture resources but its handle is not
+ released! Do that separately before this! */
+ _cogl_texture_bitmap_free (tex);
+ _cogl_texture_slices_free (tex);
+ g_free (tex);
+}
+
+CoglHandle
+cogl_texture_new_with_size (guint width,
+ guint height,
+ gint max_waste,
+ CoglPixelFormat internal_format)
+{
+ CoglTexture *tex;
+ gint bpp;
+ gint rowstride;
+
+ /* Since no data, we need some internal format */
+ if (internal_format == COGL_PIXEL_FORMAT_ANY)
+ return COGL_INVALID_HANDLE;
+
+ /* Rowstride from width */
+ bpp = _cogl_get_format_bpp (internal_format);
+ rowstride = width * bpp;
+
+ /* Init texture with empty bitmap */
+ tex = (CoglTexture*) g_malloc (sizeof (CoglTexture));
+
+ tex->ref_count = 1;
+#if COGL_DEBUG
+ printf ("COGL TEX new %p %i\n", tex, tex->ref_count);
+#endif
+
+ tex->is_foreign = FALSE;
+
+ tex->bitmap.width = width;
+ tex->bitmap.height = height;
+ tex->bitmap.format = internal_format;
+ tex->bitmap.rowstride = rowstride;
+ tex->bitmap.data = NULL;
+ tex->bitmap_owner = FALSE;
+
+ tex->slice_x_spans = NULL;
+ tex->slice_y_spans = NULL;
+ tex->slice_gl_handles = NULL;
+
+ tex->max_waste = max_waste;
+ tex->min_filter = CGL_NEAREST;
+ tex->mag_filter = CGL_NEAREST;
+
+ /* Find closest GL format match */
+ tex->bitmap.format =
+ _cogl_pixel_format_to_gl (internal_format,
+ &tex->gl_intformat,
+ &tex->gl_format,
+ &tex->gl_type);
+
+ /* Create slices for the given format and size */
+ if (!_cogl_texture_slices_create (tex))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ return _cogl_texture_handle_new (tex);
+}
+
+CoglHandle
+cogl_texture_new_from_data (guint width,
+ guint height,
+ gint max_waste,
+ CoglPixelFormat format,
+ CoglPixelFormat internal_format,
+ guint rowstride,
+ const guchar *data)
+{
+ CoglTexture *tex;
+ gint bpp;
+
+ if (format == COGL_PIXEL_FORMAT_ANY)
+ return COGL_INVALID_HANDLE;
+
+ if (data == NULL)
+ return COGL_INVALID_HANDLE;
+
+ /* Rowstride from width if not given */
+ bpp = _cogl_get_format_bpp (format);
+ if (rowstride == 0) rowstride = width * bpp;
+
+ /* Create new texture and fill with given data */
+ tex = (CoglTexture*) g_malloc (sizeof (CoglTexture));
+
+ tex->ref_count = 1;
+#if COGL_DEBUG
+ printf ("COGL TEX new %p %i\n", tex, tex->ref_count);
+#endif
+
+ tex->is_foreign = FALSE;
+
+ tex->bitmap.width = width;
+ tex->bitmap.height = height;
+ tex->bitmap.data = (guchar*)data;
+ tex->bitmap.format = format;
+ tex->bitmap.rowstride = rowstride;
+ tex->bitmap_owner = FALSE;
+
+ tex->slice_x_spans = NULL;
+ tex->slice_y_spans = NULL;
+ tex->slice_gl_handles = NULL;
+
+ tex->max_waste = max_waste;
+ tex->min_filter = CGL_NEAREST;
+ tex->mag_filter = CGL_NEAREST;
+
+ /* FIXME: If upload fails we should set some kind of
+ * error flag but still return texture handle (this
+ * is to keep the behavior equal to _new_from_file;
+ * see below) */
+
+ if (!_cogl_texture_bitmap_prepare (tex, internal_format))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ if (!_cogl_texture_slices_create (tex))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ if (!_cogl_texture_upload_to_gl (tex))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ _cogl_texture_bitmap_free (tex);
+
+ return _cogl_texture_handle_new (tex);
+}
+
+CoglHandle
+cogl_texture_new_from_file (const gchar *filename,
+ gint max_waste,
+ CoglPixelFormat internal_format,
+ GError **error)
+{
+ CoglBitmap bmp;
+ CoglTexture *tex;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE);
+
+ /* Try loading with imaging backend */
+ if (!_cogl_bitmap_from_file (&bmp, filename, error))
+ {
+ /* Try fallback */
+ if (!_cogl_bitmap_fallback_from_file (&bmp, filename))
+ return COGL_INVALID_HANDLE;
+ else if (error && *error)
+ {
+ g_error_free (*error);
+ *error = NULL;
+ }
+ }
+
+ /* Create new texture and fill with loaded data */
+ tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture));
+
+ tex->ref_count = 1;
+#if COGL_DEBUG
+ printf ("COGL TEX new %p %i\n", tex, tex->ref_count);
+#endif
+
+ tex->is_foreign = FALSE;
+
+ tex->bitmap = bmp;
+ tex->bitmap_owner = TRUE;
+
+ tex->slice_x_spans = NULL;
+ tex->slice_y_spans = NULL;
+ tex->slice_gl_handles = NULL;
+
+ tex->max_waste = max_waste;
+ tex->min_filter = CGL_NEAREST;
+ tex->mag_filter = CGL_NEAREST;
+
+ /* FIXME: If upload fails we should set some kind of
+ * error flag but still return texture handle if the
+ * user decides to destroy another texture and upload
+ * this one instead (reloading from file is not needed
+ * in that case). As a rule then, everytime a valid
+ * CoglHandle is returned, it should also be destroyed
+ * with cogl_texture_unref at some point! */
+
+ if (!_cogl_texture_bitmap_prepare (tex, internal_format))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ if (!_cogl_texture_slices_create (tex))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ if (!_cogl_texture_upload_to_gl (tex))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ _cogl_texture_bitmap_free (tex);
+
+ return _cogl_texture_handle_new (tex);
+}
+
+CoglHandle
+cogl_texture_new_from_foreign (GLuint gl_handle,
+ GLenum gl_target,
+ GLuint width,
+ GLuint height,
+ GLuint x_pot_waste,
+ GLuint y_pot_waste,
+ CoglPixelFormat format)
+{
+ /* NOTE: width, height and internal format are not queriable
+ in GLES, hence such a function prototype. However, here
+ they are still queried from the texture for improved
+ robustness and for completeness in case GLES 1.0 gets
+ unsupported in favor of a new version and cleaner api
+ */
+
+ GLenum gl_error = 0;
+ GLboolean gl_istexture;
+ GLint gl_compressed = GL_FALSE;
+ GLint gl_int_format = 0;
+ GLint gl_width = 0;
+ GLint gl_height = 0;
+ GLint gl_min_filter;
+ GLint gl_mag_filter;
+ guint bpp;
+ CoglTexture *tex;
+ CoglTexSliceSpan x_span;
+ CoglTexSliceSpan y_span;
+
+ /* Allow 2-dimensional textures only */
+ if (gl_target != GL_TEXTURE_2D &&
+ gl_target != GL_TEXTURE_RECTANGLE_ARB)
+ return COGL_INVALID_HANDLE;
+
+ /* Make sure it is a valid GL texture object */
+ gl_istexture = GE( glIsTexture (gl_handle) );
+ if (gl_istexture == GL_FALSE)
+ return COGL_INVALID_HANDLE;
+
+ /* Make sure binding succeeds */
+ gl_error = glGetError ();
+ glBindTexture (gl_target, gl_handle);
+ if (glGetError () != GL_NO_ERROR)
+ return COGL_INVALID_HANDLE;
+
+ /* Obtain texture parameters
+ (only level 0 we are interested in) */
+
+ GE( glGetTexLevelParameteriv (gl_target, 0,
+ GL_TEXTURE_COMPRESSED,
+ &gl_compressed) );
+
+ GE( glGetTexLevelParameteriv (gl_target, 0,
+ GL_TEXTURE_INTERNAL_FORMAT,
+ &gl_int_format) );
+
+
+ GE( glGetTexLevelParameteriv (gl_target, 0,
+ GL_TEXTURE_WIDTH,
+ &gl_width) );
+
+ GE( glGetTexLevelParameteriv (gl_target, 0,
+ GL_TEXTURE_HEIGHT,
+ &gl_height) );
+
+ GE( glGetTexParameteriv (gl_target,
+ GL_TEXTURE_MIN_FILTER,
+ &gl_min_filter));
+
+ GE( glGetTexParameteriv (gl_target,
+ GL_TEXTURE_MAG_FILTER,
+ &gl_mag_filter));
+
+ /* Validate width and height */
+ if (gl_width <= 0 || gl_height <= 0)
+ return COGL_INVALID_HANDLE;
+
+ /* Validate pot waste */
+ if (x_pot_waste < 0 || x_pot_waste >= gl_width ||
+ y_pot_waste < 0 || y_pot_waste >= gl_height)
+ return COGL_INVALID_HANDLE;
+
+ /* Compressed texture images not supported */
+ if (gl_compressed == GL_TRUE)
+ return COGL_INVALID_HANDLE;
+
+ /* Try and match to a cogl format */
+ if (!_cogl_pixel_format_from_gl_internal (gl_int_format,
+ &format))
+ {
+ return COGL_INVALID_HANDLE;
+ }
+
+ bpp = _cogl_get_format_bpp (format);
+
+ /* Create new texture */
+ tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture));
+
+ tex->ref_count = 1;
+#if COGL_DEBUG
+ printf ("COGL TEX new %p %i\n", tex, tex->ref_count);
+#endif
+
+ /* Setup bitmap info */
+ tex->is_foreign = TRUE;
+
+ tex->bitmap.format = format;
+ tex->bitmap.width = gl_width - x_pot_waste;
+ tex->bitmap.height = gl_height - y_pot_waste;
+ tex->bitmap.rowstride = tex->bitmap.width * bpp;
+ tex->bitmap_owner = FALSE;
+
+ tex->gl_target = gl_target;
+ tex->gl_intformat = gl_int_format;
+ tex->gl_format = gl_int_format;
+ tex->gl_type = GL_UNSIGNED_BYTE;
+
+ tex->min_filter = gl_min_filter;
+ tex->mag_filter = gl_mag_filter;
+ tex->max_waste = 0;
+
+ /* Create slice arrays */
+ tex->slice_x_spans =
+ g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan), 1);
+
+ tex->slice_y_spans =
+ g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan), 1);
+
+ tex->slice_gl_handles =
+ g_array_sized_new (FALSE, FALSE,
+ sizeof (GLuint), 1);
+
+ /* Store info for a single slice */
+ x_span.start = 0;
+ x_span.size = gl_width;
+ x_span.waste = x_pot_waste;
+ g_array_append_val (tex->slice_x_spans, x_span);
+
+ y_span.start = 0;
+ y_span.size = gl_height;
+ y_span.waste = y_pot_waste;
+ g_array_append_val (tex->slice_y_spans, y_span);
+
+ g_array_append_val (tex->slice_gl_handles, gl_handle);
+
+ /* Replace mipmap min filter modes with single level ones */
+ if (gl_min_filter != GL_NEAREST && gl_min_filter != GL_LINEAR)
+ {
+ if (gl_min_filter == GL_NEAREST_MIPMAP_NEAREST)
+ {
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST) );
+ tex->min_filter = CGL_NEAREST;
+ }
+ else
+ {
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR) );
+ tex->min_filter = CGL_LINEAR;
+ }
+ }
+
+ /* Force appropriate wrap parameter */
+ if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) &&
+ gl_target == GL_TEXTURE_2D)
+ {
+ /* Hardware repeated tiling */
+ tex->wrap_mode = GL_REPEAT;
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_REPEAT) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_REPEAT) );
+ }
+ else
+ {
+ /* Any tiling will be done in software */
+ tex->wrap_mode = GL_CLAMP_TO_EDGE;
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) );
+ }
+
+ return _cogl_texture_handle_new (tex);
+}
+
+CoglHandle
+cogl_texture_ref (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return COGL_INVALID_HANDLE;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ tex->ref_count++;
+#if COGL_DEBUG
+ printf ("COGL TEX ref %p %i\n", tex, tex->ref_count);
+#endif
+
+ return handle;
+}
+
+void
+cogl_texture_unref (CoglHandle handle)
+{
+ /* Check if valid texture handle */
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+#if COGL_DEBUG
+ printf ("COGL TEX unref %p %i\n", tex, tex->ref_count - 1);
+#endif
+
+ if (--tex->ref_count < 1)
+ {
+#if COGL_DEBUG
+ printf ("COGL TEX free %p %i\n", tex, tex->ref_count - 1);
+#endif
+
+ /* Free texture handle and resources */
+ _cogl_texture_handle_release (tex);
+ _cogl_texture_free (tex);
+ }
+}
+
+guint
+cogl_texture_get_width (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->bitmap.width;
+}
+
+guint
+cogl_texture_get_height (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->bitmap.height;
+}
+
+CoglPixelFormat
+cogl_texture_get_format (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return COGL_PIXEL_FORMAT_ANY;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->bitmap.format;
+}
+
+guint
+cogl_texture_get_rowstride (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->bitmap.rowstride;
+}
+
+gint
+cogl_texture_get_max_waste (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->max_waste;
+}
+
+gboolean
+cogl_texture_is_sliced (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return FALSE;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ if (tex->slice_gl_handles == NULL)
+ return FALSE;
+
+ if (tex->slice_gl_handles->len <= 1)
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+cogl_texture_get_gl_texture (CoglHandle handle,
+ GLuint *out_gl_handle,
+ GLenum *out_gl_target)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return FALSE;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ if (tex->slice_gl_handles == NULL)
+ return FALSE;
+
+ if (tex->slice_gl_handles->len < 1)
+ return FALSE;
+
+ if (out_gl_handle != NULL)
+ *out_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0);
+
+ if (out_gl_target != NULL)
+ *out_gl_target = tex->gl_target;
+
+ return TRUE;
+}
+
+COGLenum
+cogl_texture_get_min_filter (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->min_filter;
+}
+
+COGLenum
+cogl_texture_get_mag_filter (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->mag_filter;
+}
+
+void
+cogl_texture_set_filters (CoglHandle handle,
+ COGLenum min_filter,
+ COGLenum mag_filter)
+{
+ CoglTexture *tex;
+ GLuint gl_handle;
+ int i;
+
+ if (!cogl_is_texture (handle))
+ return;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* Store new values */
+ tex->min_filter = min_filter;
+ tex->mag_filter = mag_filter;
+
+ /* Make sure slices were created */
+ if (tex->slice_gl_handles == NULL)
+ return;
+
+ /* Apply new filters to every slice */
+ for (i=0; i<tex->slice_gl_handles->len; ++i)
+ {
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, i);
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) );
+ }
+}
+
+gboolean
+cogl_texture_set_region (CoglHandle handle,
+ gint src_x,
+ gint src_y,
+ gint dst_x,
+ gint dst_y,
+ guint dst_width,
+ guint dst_height,
+ gint width,
+ gint height,
+ CoglPixelFormat format,
+ guint rowstride,
+ const guchar *data)
+{
+ CoglTexture *tex;
+ gint bpp;
+ CoglBitmap source_bmp;
+ CoglBitmap temp_bmp;
+ gboolean source_bmp_owner = FALSE;
+ CoglPixelFormat closest_format;
+ GLenum closest_gl_format;
+ GLenum closest_gl_type;
+ gboolean success;
+
+ /* Check if valid texture handle */
+ if (!cogl_is_texture (handle))
+ return FALSE;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* Check for valid format */
+ if (format == COGL_PIXEL_FORMAT_ANY)
+ return FALSE;
+
+ /* Init source bitmap */
+ source_bmp.width = width;
+ source_bmp.height = height;
+ source_bmp.format = format;
+ source_bmp.data = (guchar*)data;
+
+ /* Rowstride from width if none specified */
+ bpp = _cogl_get_format_bpp (format);
+ source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride;
+
+ /* Find closest format to internal that's supported by GL */
+ closest_format = _cogl_pixel_format_to_gl (tex->bitmap.format,
+ NULL, /* don't need */
+ &closest_gl_format,
+ &closest_gl_type);
+
+ /* If no direct match, convert */
+ if (closest_format != format)
+ {
+ /* Convert to required format */
+ success = _cogl_bitmap_convert_and_premult (&source_bmp,
+ &temp_bmp,
+ closest_format);
+
+ /* Swap bitmaps if succeeded */
+ if (!success) return FALSE;
+ source_bmp = temp_bmp;
+ source_bmp_owner = TRUE;
+ }
+
+ /* Send data to GL */
+ _cogl_texture_upload_subregion_to_gl (tex,
+ src_x, src_y,
+ dst_x, dst_y,
+ dst_width, dst_height,
+ &source_bmp,
+ closest_gl_format,
+ closest_gl_type);
+
+ /* Free data if owner */
+ if (source_bmp_owner)
+ g_free (source_bmp.data);
+
+ return TRUE;
+}
+
+gint
+cogl_texture_get_data (CoglHandle handle,
+ CoglPixelFormat format,
+ guint rowstride,
+ guchar *data)
+{
+ CoglTexture *tex;
+ gint bpp;
+ gint byte_size;
+ CoglPixelFormat closest_format;
+ gint closest_bpp;
+ GLenum closest_gl_format;
+ GLenum closest_gl_type;
+ CoglBitmap target_bmp;
+ CoglBitmap new_bmp;
+ gboolean success;
+ guchar *src;
+ guchar *dst;
+ gint y;
+
+ /* Check if valid texture handle */
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* Default to internal format if none specified */
+ if (format == COGL_PIXEL_FORMAT_ANY)
+ format = tex->bitmap.format;
+
+ /* Rowstride from texture width if none specified */
+ bpp = _cogl_get_format_bpp (format);
+ if (rowstride == 0) rowstride = tex->bitmap.width * bpp;
+
+ /* Return byte size if only that requested */
+ byte_size = tex->bitmap.height * rowstride;
+ if (data == NULL) return byte_size;
+
+ /* Find closest format that's supported by GL */
+ closest_format = _cogl_pixel_format_to_gl (format,
+ NULL, /* don't need */
+ &closest_gl_format,
+ &closest_gl_type);
+
+ closest_bpp = _cogl_get_format_bpp (closest_format);
+
+ /* Is the requested format supported? */
+ if (closest_format == format)
+ {
+ /* Target user data directly */
+ target_bmp = tex->bitmap;
+ target_bmp.format = format;
+ target_bmp.rowstride = rowstride;
+ target_bmp.data = data;
+ }
+ else
+ {
+ /* Target intermediate buffer */
+ target_bmp = tex->bitmap;
+ target_bmp.format = closest_format;
+ target_bmp.rowstride = target_bmp.width * closest_bpp;
+ target_bmp.data = (guchar*) g_malloc (target_bmp.height
+ * target_bmp.rowstride);
+ }
+
+ /* Retrieve data from slices */
+ _cogl_texture_download_from_gl (tex, &target_bmp,
+ closest_gl_format,
+ closest_gl_type);
+
+ /* Was intermediate used? */
+ if (closest_format != format)
+ {
+ /* Convert to requested format */
+ success = _cogl_bitmap_convert_and_premult (&target_bmp,
+ &new_bmp,
+ format);
+
+ /* Free intermediate data and return if failed */
+ g_free (target_bmp.data);
+ if (!success) return 0;
+
+ /* Copy to user buffer */
+ for (y = 0; y < new_bmp.height; ++y)
+ {
+ src = new_bmp.data + y * new_bmp.rowstride;
+ dst = data + y * rowstride;
+ memcpy (dst, src, new_bmp.width);
+ }
+
+ /* Free converted data */
+ g_free (new_bmp.data);
+ }
+
+ return byte_size;
+}
+
+static void
+_cogl_texture_quad_sw (CoglTexture *tex,
+ ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed tx1,
+ ClutterFixed ty1,
+ ClutterFixed tx2,
+ ClutterFixed ty2)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ CoglSpanIter iter_x , iter_y;
+ ClutterFixed tw , th;
+ ClutterFixed tqx , tqy;
+ ClutterFixed first_tx , first_ty;
+ ClutterFixed first_qx , first_qy;
+ ClutterFixed slice_tx1 , slice_ty1;
+ ClutterFixed slice_tx2 , slice_ty2;
+ ClutterFixed slice_qx1 , slice_qy1;
+ ClutterFixed slice_qx2 , slice_qy2;
+ GLuint gl_handle;
+
+#if COGL_DEBUG
+ printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n");
+#endif
+
+ /* Prepare GL state */
+ gulong enable_flags = 0;
+
+ if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB)
+ enable_flags |= COGL_ENABLE_TEXTURE_RECT;
+ else
+ enable_flags |= COGL_ENABLE_TEXTURE_2D;
+
+ if (ctx->color_alpha < 255
+ || tex->bitmap.format & COGL_A_BIT)
+ {
+ enable_flags |= COGL_ENABLE_BLEND;
+ }
+
+ cogl_enable (enable_flags);
+
+ /* Scale ratio from texture to quad widths */
+ tw = CLUTTER_INT_TO_FIXED (tex->bitmap.width);
+ th = CLUTTER_INT_TO_FIXED (tex->bitmap.height);
+
+ tqx = CFX_QDIV (x2-x1, CFX_QMUL (tw, (tx2 - tx1)));
+ tqy = CFX_QDIV (y2-y1, CFX_QMUL (th, (ty2 - ty1)));
+
+ /* Integral texture coordinate for first tile */
+ first_tx = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (tx1));
+ first_ty = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (ty1));
+
+ /* Denormalize texture coordinates */
+ first_tx = CFX_QMUL (first_tx, tw);
+ first_ty = CFX_QMUL (first_ty, th);
+ tx1 = CFX_QMUL (tx1, tw);
+ ty1 = CFX_QMUL (ty1, th);
+ tx2 = CFX_QMUL (tx2, tw);
+ ty2 = CFX_QMUL (ty2, th);
+
+ /* Quad coordinate of the first tile */
+ first_qx = x1 - CFX_QMUL (tx1 - first_tx, tqx);
+ first_qy = y1 - CFX_QMUL (ty1 - first_ty, tqy);
+
+
+ /* Iterate until whole quad height covered */
+ for (_cogl_span_iter_begin (&iter_y, tex->slice_y_spans,
+ first_ty, ty1, ty2) ;
+ !_cogl_span_iter_end (&iter_y) ;
+ _cogl_span_iter_next (&iter_y) )
+ {
+ /* Discard slices out of quad early */
+ if (!iter_y.intersects) continue;
+
+ /* Span-quad intersection in quad coordinates */
+ slice_qy1 = first_qy +
+ CFX_QMUL (iter_y.intersect_start - first_ty, tqy);
+
+ slice_qy2 = first_qy +
+ CFX_QMUL (iter_y.intersect_end - first_ty, tqy);
+
+ /* Localize slice texture coordinates */
+ slice_ty1 = iter_y.intersect_start - iter_y.pos;
+ slice_ty2 = iter_y.intersect_end - iter_y.pos;
+
+ /* Normalize texture coordinates to current slice
+ (rectangle texture targets take denormalized) */
+ if (tex->gl_target != GL_TEXTURE_RECTANGLE_ARB)
+ {
+ slice_ty1 /= iter_y.span->size;
+ slice_ty2 /= iter_y.span->size;
+ }
+
+
+ /* Iterate until whole quad width covered */
+ for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans,
+ first_tx, tx1, tx2) ;
+ !_cogl_span_iter_end (&iter_x) ;
+ _cogl_span_iter_next (&iter_x) )
+ {
+ /* Discard slices out of quad early */
+ if (!iter_x.intersects) continue;
+
+ /* Span-quad intersection in quad coordinates */
+ slice_qx1 = first_qx +
+ CFX_QMUL (iter_x.intersect_start - first_tx, tqx);
+
+ slice_qx2 = first_qx +
+ CFX_QMUL (iter_x.intersect_end - first_tx, tqx);
+
+ /* Localize slice texture coordinates */
+ slice_tx1 = iter_x.intersect_start - iter_x.pos;
+ slice_tx2 = iter_x.intersect_end - iter_x.pos;
+
+ /* Normalize texture coordinates to current slice
+ (rectangle texture targets take denormalized) */
+ if (tex->gl_target != GL_TEXTURE_RECTANGLE_ARB)
+ {
+ slice_tx1 /= iter_x.span->size;
+ slice_tx2 /= iter_x.span->size;
+ }
+
+#if COGL_DEBUG
+ printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index);
+ printf("qx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx1));
+ printf("qy1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy1));
+ printf("qx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx2));
+ printf("qy2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy2));
+ printf("tx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx1));
+ printf("ty1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty1));
+ printf("tx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx2));
+ printf("ty2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty2));
+#endif
+
+ /* Pick and bind opengl texture object */
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint,
+ iter_y.index * iter_x.array->len +
+ iter_x.index);
+
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+#define CFX_F CLUTTER_FIXED_TO_FLOAT
+
+ /* Draw textured quad */
+ GE( glBegin (GL_QUADS) );
+
+ GE( glTexCoord2f (CFX_F(slice_tx1), CFX_F(slice_ty1)) );
+ GE( glVertex2f (CFX_F(slice_qx1), CFX_F(slice_qy1)) );
+
+ GE( glTexCoord2f (CFX_F(slice_tx2), CFX_F(slice_ty1)) );
+ GE( glVertex2f (CFX_F(slice_qx2), CFX_F(slice_qy1)) );
+
+ GE( glTexCoord2f (CFX_F(slice_tx2), CFX_F(slice_ty2)) );
+ GE( glVertex2f (CFX_F(slice_qx2), CFX_F(slice_qy2)) );
+
+ GE( glTexCoord2f (CFX_F(slice_tx1), CFX_F(slice_ty2)) );
+ GE( glVertex2f (CFX_F(slice_qx1), CFX_F(slice_qy2)) );
+
+ glEnd ();
+
+#undef CFX_F
+ }
+ }
+}
+
+static void
+_cogl_texture_quad_hw (CoglTexture *tex,
+ ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed tx1,
+ ClutterFixed ty1,
+ ClutterFixed tx2,
+ ClutterFixed ty2)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ CoglTexSliceSpan *x_span;
+ CoglTexSliceSpan *y_span;
+ GLuint gl_handle;
+
+#if COGL_DEBUG
+ printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n");
+#endif
+
+ /* Prepare GL state */
+ gulong enable_flags = 0;
+
+ if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB)
+ enable_flags |= COGL_ENABLE_TEXTURE_RECT;
+ else
+ enable_flags |= COGL_ENABLE_TEXTURE_2D;
+
+ if (ctx->color_alpha < 255
+ || tex->bitmap.format & COGL_A_BIT)
+ {
+ enable_flags |= COGL_ENABLE_BLEND;
+ }
+
+ cogl_enable (enable_flags);
+
+ /* Pick and bind opengl texture object */
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0);
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0);
+ y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0);
+
+ /* Don't include the waste in the texture coordinates */
+ tx1 = tx1 * (x_span->size - x_span->waste) / x_span->size;
+ tx2 = tx2 * (x_span->size - x_span->waste) / x_span->size;
+ ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size;
+ ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size;
+
+ /* Denormalize texture coordinates for rectangle textures */
+ if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB)
+ {
+ tx1 *= x_span->size;
+ tx2 *= x_span->size;
+ ty1 *= y_span->size;
+ ty2 *= y_span->size;
+ }
+
+#define CFX_F(x) CLUTTER_FIXED_TO_FLOAT(x)
+
+ /* Draw textured quad */
+ GE( glBegin (GL_QUADS) );
+
+ GE( glTexCoord2f (CFX_F(tx1), CFX_F(ty1)) );
+ GE( glVertex2f (CFX_F(x1), CFX_F(y1)) );
+
+ GE( glTexCoord2f (CFX_F(tx2), CFX_F(ty1)) );
+ GE( glVertex2f (CFX_F(x2), CFX_F(y1)) );
+
+ GE( glTexCoord2f (CFX_F(tx2), CFX_F(ty2)) );
+ GE( glVertex2f (CFX_F(x2), CFX_F(y2)) );
+
+ GE( glTexCoord2f (CFX_F(tx1), CFX_F(ty2)) );
+ GE( glVertex2f (CFX_F(x1), CFX_F(y2)) );
+
+ glEnd ();
+
+#undef CFX_F
+}
+
+void
+cogl_texture_rectangle (CoglHandle handle,
+ ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed tx1,
+ ClutterFixed ty1,
+ ClutterFixed tx2,
+ ClutterFixed ty2)
+{
+ CoglTexture *tex;
+ ClutterFixed tempx;
+
+ /* Check if valid texture */
+ if (!cogl_is_texture (handle))
+ return;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* Make sure we got stuff to draw */
+ if (tex->slice_gl_handles == NULL)
+ return;
+
+ if (tex->slice_gl_handles->len == 0)
+ return;
+
+ if (tx1 == tx2 || ty1 == ty2)
+ return;
+
+ /* Fix quad coord ordering
+ (atm this is required for sw tiling to iterate
+ over slices properly) */
+ if (x1 > x2)
+ {
+ tempx = x1;
+ x1 = x2;
+ x2 = tempx;
+ }
+
+ if (y1 > y2)
+ {
+ tempx = y1;
+ y1 = y2;
+ y2 = tempx;
+ }
+
+ /* Fix texture coord ordering */
+ if (tx1 > tx2)
+ {
+ tempx = tx1;
+ tx1 = tx2;
+ tx2 = tempx;
+ }
+
+ if (ty1 > ty2)
+ {
+ tempx = ty1;
+ ty1 = ty2;
+ ty2 = tempx;
+ }
+
+ /* Pick tiling mode according to hw support */
+ if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT)
+ && tex->slice_gl_handles->len == 1)
+ {
+ _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2);
+ }
+ else
+ {
+ if (tex->slice_gl_handles->len == 1
+ && tx1 >= -CFX_ONE && tx2 <= CFX_ONE
+ && ty1 >= -CFX_ONE && ty2 <= CFX_ONE)
+ {
+ _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2);
+ }
+ else
+ {
+ _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2);
+ }
+ }
+}
+
+void
+cogl_texture_polygon (CoglHandle handle,
+ guint n_vertices,
+ CoglTextureVertex *vertices,
+ gboolean use_color)
+{
+ CoglTexture *tex;
+ int i, x, y, vnum;
+ GLuint gl_handle;
+ CoglTexSliceSpan *y_span, *x_span;
+
+ /* Check if valid texture */
+ if (!cogl_is_texture (handle))
+ return;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* The polygon will have artifacts where the slices join if the wrap
+ mode is GL_LINEAR because the filtering will pull in pixels from
+ the transparent border. To make it clear that the function
+ shouldn't be used in these circumstances we just bail out and
+ draw nothing */
+ if (tex->slice_gl_handles->len != 1
+ && (tex->min_filter != GL_NEAREST || tex->mag_filter != GL_NEAREST))
+ {
+ static gboolean shown_warning = FALSE;
+
+ if (!shown_warning)
+ {
+ g_warning ("cogl_texture_polygon does not work for sliced textures "
+ "when the minification and magnification filters are not "
+ "CGL_NEAREST");
+ shown_warning = TRUE;
+ }
+ return;
+ }
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* Prepare GL state */
+ if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB)
+ cogl_enable (COGL_ENABLE_TEXTURE_RECT | COGL_ENABLE_BLEND);
+ else
+ cogl_enable (COGL_ENABLE_TEXTURE_2D | COGL_ENABLE_BLEND);
+
+ /* Temporarily change the wrapping mode on all of the slices to use
+ a transparent border */
+ for (i = 0; i < tex->slice_gl_handles->len; i++)
+ {
+ GE( glBindTexture (tex->gl_target,
+ g_array_index (tex->slice_gl_handles, GLuint, i)) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S,
+ GL_CLAMP_TO_BORDER) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T,
+ GL_CLAMP_TO_BORDER) );
+ }
+
+ i = 0;
+
+ /* Render all of the slices with the full geometry but use a
+ transparent border color so that any part of the texture not
+ covered by the slice will be ignored */
+ for (y = 0; y < tex->slice_y_spans->len; y++)
+ {
+ y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y);
+
+ for (x = 0; x < tex->slice_x_spans->len; x++)
+ {
+ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x);
+
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, i++);
+
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+ GE( glBegin (GL_TRIANGLE_FAN) );
+
+ for (vnum = 0; vnum < n_vertices; vnum++)
+ {
+ GLfloat tx, ty;
+
+ if (use_color)
+ cogl_color (&vertices[vnum].color);
+
+ /* Transform the texture co-ordinates so they are
+ relative to the slice */
+ tx = (CLUTTER_FIXED_TO_FLOAT (vertices[vnum].tx)
+ - x_span->start / (GLfloat) tex->bitmap.width)
+ * tex->bitmap.width / x_span->size;
+ ty = (CLUTTER_FIXED_TO_FLOAT (vertices[vnum].ty)
+ - y_span->start / (GLfloat) tex->bitmap.height)
+ * tex->bitmap.height / y_span->size;
+
+ if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB)
+ {
+ tx *= x_span->size;
+ ty *= y_span->size;
+ }
+
+ GE( glTexCoord2f (tx, ty) );
+
+ GE( glVertex3f (CLUTTER_FIXED_TO_FLOAT (vertices[vnum].x),
+ CLUTTER_FIXED_TO_FLOAT (vertices[vnum].y),
+ CLUTTER_FIXED_TO_FLOAT (vertices[vnum].z)) );
+ }
+
+ GE( glEnd () );
+ }
+ }
+
+ /* Restore the wrapping mode */
+ for (i = 0; i < tex->slice_gl_handles->len; i++)
+ {
+ GE( glBindTexture (tex->gl_target,
+ g_array_index (tex->slice_gl_handles, GLuint, i)) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, tex->wrap_mode) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, tex->wrap_mode) );
+ }
+}
diff --git a/gl/cogl-texture.h b/gl/cogl-texture.h
new file mode 100644
index 00000000..d4104804
--- /dev/null
+++ b/gl/cogl-texture.h
@@ -0,0 +1,64 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_TEXTURE_H
+#define __COGL_TEXTURE_H
+
+#include "cogl-bitmap.h"
+
+typedef struct _CoglTexture CoglTexture;
+typedef struct _CoglTexSliceSpan CoglTexSliceSpan;
+typedef struct _CoglSpanIter CoglSpanIter;
+
+struct _CoglTexSliceSpan
+{
+ gint start;
+ gint size;
+ gint waste;
+};
+
+struct _CoglTexture
+{
+ guint ref_count;
+ CoglBitmap bitmap;
+ gboolean bitmap_owner;
+ GLenum gl_target;
+ GLenum gl_intformat;
+ GLenum gl_format;
+ GLenum gl_type;
+ GArray *slice_x_spans;
+ GArray *slice_y_spans;
+ GArray *slice_gl_handles;
+ gint max_waste;
+ COGLenum min_filter;
+ COGLenum mag_filter;
+ gboolean is_foreign;
+ GLint wrap_mode;
+};
+
+CoglTexture*
+_cogl_texture_pointer_from_handle (CoglHandle handle);
+
+#endif /* __COGL_TEXTURE_H */
diff --git a/gl/cogl.c b/gl/cogl.c
index 5e752cd8..3e8fc350 100644
--- a/gl/cogl.c
+++ b/gl/cogl.c
@@ -39,23 +39,12 @@
typedef CoglFuncPtr (*GLXGetProcAddressProc) (const guint8 *procName);
#endif
-static gulong __enable_flags = 0;
-
-/* FBO Procs */
-typedef void (*GenFramebuffers) (GLsizei n, GLuint *ids);
-typedef void (*BindFramebuffer) (GLenum target, GLuint framebuffer);
-typedef void (*FramebufferTexture2D) (GLenum target, GLenum attachment,
- GLenum textarget, GLuint texture,
- GLint level);
-typedef GLenum (*CheckFramebufferStatus)(GLenum target);
-typedef void (*DeleteFramebuffers) (GLsizei n, const GLuint *framebuffers);
-
-static GenFramebuffers _gen_framebuffers = NULL;
-static BindFramebuffer _bind_framebuffer = NULL;
-static FramebufferTexture2D _framebuffer_texture_2d = NULL;
-static CheckFramebufferStatus _check_framebuffer_status = NULL;
-static DeleteFramebuffers _delete_framebuffers = NULL;
+#include "cogl-internal.h"
+#include "cogl-util.h"
+#include "cogl-context.h"
+
+/* GL error to string conversion */
#if COGL_DEBUG
struct token_string
{
@@ -89,19 +78,21 @@ error_string(GLenum errorCode)
}
#endif
-#if COGL_DEBUG
-#define GE(x...) G_STMT_START { \
- GLenum err; \
- (x); \
- while ((err = glGetError()) != GL_NO_ERROR) { \
- fprintf(stderr, "glError: %s caught at %s:%u\n", \
- (char *)error_string(err), \
- __FILE__, __LINE__); \
- } \
-} G_STMT_END
-#else
-#define GE(x) (x);
-#endif
+
+/* Expecting ARB functions not to be defined */
+#define glCreateProgramObjectARB ctx->pf_glCreateProgramObjectARB
+#define glCreateShaderObjectARB ctx->pf_glCreateShaderObjectARB
+#define glShaderSourceARB ctx->pf_glShaderSourceARB
+#define glCompileShaderARB ctx->pf_glCompileShaderARB
+#define glAttachObjectARB ctx->pf_glAttachObjectARB
+#define glLinkProgramARB ctx->pf_glLinkProgramARB
+#define glUseProgramObjectARB ctx->pf_glUseProgramObjectARB
+#define glGetUniformLocationARB ctx->pf_glGetUniformLocationARB
+#define glDeleteObjectARB ctx->pf_glDeleteObjectARB
+#define glGetInfoLogARB ctx->pf_glGetInfoLogARB
+#define glGetObjectParameterivARB ctx->pf_glGetObjectParameterivARB
+#define glUniform1fARB ctx->pf_glUniform1fARB
+
CoglFuncPtr
cogl_get_proc_address (const gchar* name)
@@ -217,9 +208,6 @@ cogl_paint_init (const ClutterColor *color)
* glDepthFunc (GL_LEQUAL);
* glAlphaFunc (GL_GREATER, 0.1);
*/
-
- cogl_enable (CGL_ENABLE_BLEND);
- glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
/* FIXME: inline most of these */
@@ -272,59 +260,155 @@ cogl_rotate (gint angle, gint x, gint y, gint z)
glRotatef ((float)angle, (float)x, (float)y, (float)z);
}
+static inline gboolean
+cogl_toggle_flag (CoglContext *ctx,
+ gulong new_flags,
+ gulong flag,
+ GLenum gl_flag)
+{
+ /* Toggles and caches a single enable flag on or off
+ * by comparing to current state
+ */
+ if (new_flags & flag)
+ {
+ if (!(ctx->enable_flags & flag))
+ {
+ GE( glEnable (gl_flag) );
+ ctx->enable_flags |= flag;
+ return TRUE;
+ }
+ }
+ else if (ctx->enable_flags & flag)
+ {
+ GE( glDisable (gl_flag) );
+ ctx->enable_flags &= ~flag;
+ }
+
+ return FALSE;
+}
+
+static inline gboolean
+cogl_toggle_client_flag (CoglContext *ctx,
+ gulong new_flags,
+ gulong flag,
+ GLenum gl_flag)
+{
+ /* Toggles and caches a single client-side enable flag
+ * on or off by comparing to current state
+ */
+ if (new_flags & flag)
+ {
+ if (!(ctx->enable_flags & flag))
+ {
+ GE( glEnableClientState (gl_flag) );
+ ctx->enable_flags |= flag;
+ return TRUE;
+ }
+ }
+ else if (ctx->enable_flags & flag)
+ {
+ GE( glDisableClientState (gl_flag) );
+ ctx->enable_flags &= ~flag;
+ }
+
+ return FALSE;
+}
+
void
cogl_enable (gulong flags)
{
/* This function essentially caches glEnable state() in the
* hope of lessening number GL traffic.
*/
- if (flags & CGL_ENABLE_BLEND)
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (cogl_toggle_flag (ctx, flags,
+ COGL_ENABLE_BLEND,
+ GL_BLEND))
{
- if (!(__enable_flags & CGL_ENABLE_BLEND))
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ cogl_toggle_flag (ctx, flags,
+ COGL_ENABLE_TEXTURE_2D,
+ GL_TEXTURE_2D);
+
+ cogl_toggle_client_flag (ctx, flags,
+ COGL_ENABLE_VERTEX_ARRAY,
+ GL_VERTEX_ARRAY);
+
+ cogl_toggle_client_flag (ctx, flags,
+ COGL_ENABLE_TEXCOORD_ARRAY,
+ GL_TEXTURE_COORD_ARRAY);
+
+#ifdef GL_TEXTURE_RECTANGLE_ARB
+ cogl_toggle_flag (ctx, flags,
+ COGL_ENABLE_TEXTURE_RECT,
+ GL_TEXTURE_RECTANGLE_ARB);
+#endif
+}
+
+gulong
+cogl_get_enable ()
+{
+ _COGL_GET_CONTEXT (ctx, 0);
+
+ return ctx->enable_flags;
+}
+
+/*
+void
+cogl_enable (gulong flags)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (flags & COGL_ENABLE_BLEND)
+ {
+ if (!(ctx->enable_flags & COGL_ENABLE_BLEND))
{
glEnable (GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- __enable_flags |= CGL_ENABLE_BLEND;
+ ctx->enable_flags |= COGL_ENABLE_BLEND;
}
}
- else if (__enable_flags & CGL_ENABLE_BLEND)
+ else if (ctx->enable_flags & COGL_ENABLE_BLEND)
{
glDisable (GL_BLEND);
- __enable_flags &= ~CGL_ENABLE_BLEND;
+ ctx->enable_flags &= ~COGL_ENABLE_BLEND;
}
- if (flags & CGL_ENABLE_TEXTURE_2D)
+ if (flags & COGL_ENABLE_TEXTURE_2D)
{
- if (!(__enable_flags & CGL_ENABLE_TEXTURE_2D))
+ if (!(ctx->enable_flags & COGL_ENABLE_TEXTURE_2D))
{
glEnable (GL_TEXTURE_2D);
- __enable_flags |= CGL_ENABLE_TEXTURE_2D;
+ ctx->enable_flags |= COGL_ENABLE_TEXTURE_2D;
}
}
- else if (__enable_flags & CGL_ENABLE_TEXTURE_2D)
+ else if (ctx->enable_flags & COGL_ENABLE_TEXTURE_2D)
{
glDisable (GL_TEXTURE_2D);
- __enable_flags &= ~CGL_ENABLE_TEXTURE_2D;
+ ctx->enable_flags &= ~COGL_ENABLE_TEXTURE_2D;
}
-
+
#ifdef GL_TEXTURE_RECTANGLE_ARB
- if (flags & CGL_ENABLE_TEXTURE_RECT)
+ if (flags & COGL_ENABLE_TEXTURE_RECT)
{
- if (!(__enable_flags & CGL_ENABLE_TEXTURE_RECT))
+ if (!(ctx->enable_flags & COGL_ENABLE_TEXTURE_RECT))
{
glEnable (GL_TEXTURE_RECTANGLE_ARB);
- __enable_flags |= CGL_ENABLE_TEXTURE_RECT;
+ ctx->enable_flags |= COGL_ENABLE_TEXTURE_RECT;
}
}
- else if (__enable_flags & CGL_ENABLE_TEXTURE_RECT)
+ else if (ctx->enable_flags & COGL_ENABLE_TEXTURE_RECT)
{
glDisable (GL_TEXTURE_RECTANGLE_ARB);
- __enable_flags &= ~CGL_ENABLE_TEXTURE_RECT;
+ ctx->enable_flags &= ~COGL_ENABLE_TEXTURE_RECT;
}
#endif
}
-
+*/
void
cogl_enable_depth_test (gboolean setting)
{
@@ -345,7 +429,15 @@ cogl_enable_depth_test (gboolean setting)
void
cogl_color (const ClutterColor *color)
{
- glColor4ub (color->red, color->green, color->blue, color->alpha);
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ glColor4ub (color->red,
+ color->green,
+ color->blue,
+ color->alpha);
+
+ /* Store alpha for proper blending enables */
+ ctx->color_alpha = color->alpha;
}
void
@@ -354,198 +446,64 @@ cogl_clip_set (ClutterFixed x_offset,
ClutterFixed width,
ClutterFixed height)
{
- GE( glEnable (GL_STENCIL_TEST) );
+ if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES))
+ {
+ GLdouble eqn_left[4] = { 1.0, 0, 0,
+ -CLUTTER_FIXED_TO_FLOAT (x_offset) };
+ GLdouble eqn_right[4] = { -1.0, 0, 0,
+ CLUTTER_FIXED_TO_FLOAT (x_offset + width) };
+ GLdouble eqn_top[4] = { 0, 1.0, 0, -CLUTTER_FIXED_TO_FLOAT (y_offset) };
+ GLdouble eqn_bottom[4] = { 0, -1.0, 0, CLUTTER_FIXED_TO_FLOAT
+ (y_offset + height) };
+
+ GE( glClipPlane (GL_CLIP_PLANE0, eqn_left) );
+ GE( glClipPlane (GL_CLIP_PLANE1, eqn_right) );
+ GE( glClipPlane (GL_CLIP_PLANE2, eqn_top) );
+ GE( glClipPlane (GL_CLIP_PLANE3, eqn_bottom) );
+ GE( glEnable (GL_CLIP_PLANE0) );
+ GE( glEnable (GL_CLIP_PLANE1) );
+ GE( glEnable (GL_CLIP_PLANE2) );
+ GE( glEnable (GL_CLIP_PLANE3) );
+ }
+ else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER))
+ {
+ GE( glEnable (GL_STENCIL_TEST) );
- GE( glClearStencil (0.0f) );
- GE( glClear (GL_STENCIL_BUFFER_BIT) );
+ GE( glClearStencil (0.0f) );
+ GE( glClear (GL_STENCIL_BUFFER_BIT) );
- GE( glStencilFunc (GL_NEVER, 0x1, 0x1) );
- GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
+ GE( glStencilFunc (GL_NEVER, 0x1, 0x1) );
+ GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
- GE( glColor3f (1.0f, 1.0f, 1.0f) );
+ GE( glColor3f (1.0f, 1.0f, 1.0f) );
- GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x_offset),
- CLUTTER_FIXED_TO_FLOAT (y_offset),
- CLUTTER_FIXED_TO_FLOAT (x_offset + width),
- CLUTTER_FIXED_TO_FLOAT (y_offset + height)) );
+ GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x_offset),
+ CLUTTER_FIXED_TO_FLOAT (y_offset),
+ CLUTTER_FIXED_TO_FLOAT (x_offset + width),
+ CLUTTER_FIXED_TO_FLOAT (y_offset + height)) );
- GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
- GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
+ GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
+ GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
+ }
}
void
cogl_clip_unset (void)
{
- GE( glDisable (GL_STENCIL_TEST) );
-}
-
-gboolean
-cogl_texture_can_size (COGLenum target,
- COGLenum pixel_format,
- COGLenum pixel_type,
- int width,
- int height)
-{
-#ifdef GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB
- if (target == CGL_TEXTURE_RECTANGLE_ARB)
+ if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES))
{
- GLint max_size = 0;
-
- GE( glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &max_size) );
-
- return (max_size && width <= max_size && height <= max_size);
+ GE( glDisable (GL_CLIP_PLANE3) );
+ GE( glDisable (GL_CLIP_PLANE2) );
+ GE( glDisable (GL_CLIP_PLANE1) );
+ GE( glDisable (GL_CLIP_PLANE0) );
}
- else /* Assumes CGL_TEXTURE_2D */
-#endif
+ else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER))
{
- GLint new_width = 0;
-
- GE( glTexImage2D (GL_PROXY_TEXTURE_2D, 0, GL_RGBA,
- width, height, 0 /* border */,
- pixel_format, pixel_type, NULL) );
-
- GE( glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0,
- GL_TEXTURE_WIDTH, &new_width) );
-
- return new_width != 0;
+ GE( glDisable (GL_STENCIL_TEST) );
}
}
void
-cogl_texture_quad (gint x1,
- gint x2,
- gint y1,
- gint y2,
- ClutterFixed tx1,
- ClutterFixed ty1,
- ClutterFixed tx2,
- ClutterFixed ty2)
-{
- gdouble txf1, tyf1, txf2, tyf2;
-
- txf1 = CLUTTER_FIXED_TO_DOUBLE (tx1);
- tyf1 = CLUTTER_FIXED_TO_DOUBLE (ty1);
- txf2 = CLUTTER_FIXED_TO_DOUBLE (tx2);
- tyf2 = CLUTTER_FIXED_TO_DOUBLE (ty2);
-
- glBegin (GL_QUADS);
- glTexCoord2f (txf2, tyf2); glVertex2i (x2, y2);
- glTexCoord2f (txf1, tyf2); glVertex2i (x1, y2);
- glTexCoord2f (txf1, tyf1); glVertex2i (x1, y1);
- glTexCoord2f (txf2, tyf1); glVertex2i (x2, y1);
- glEnd ();
-}
-
-void
-cogl_textures_create (guint num, COGLuint *textures)
-{
- GE( glGenTextures (num, textures) );
-}
-
-void
-cogl_textures_destroy (guint num, const COGLuint *textures)
-{
- GE( glDeleteTextures (num, textures) );
-}
-
-void
-cogl_texture_bind (COGLenum target, COGLuint texture)
-{
- GE( glBindTexture (target, texture) );
-}
-
-void
-cogl_texture_set_alignment (COGLenum target,
- guint alignment,
- guint row_length)
-{
- GE( glPixelStorei (GL_UNPACK_ROW_LENGTH, row_length) );
- GE( glPixelStorei (GL_UNPACK_ALIGNMENT, alignment) );
-}
-
-void
-cogl_texture_set_filters (COGLenum target,
- COGLenum min_filter,
- COGLenum max_filter)
-{
- GE( glTexParameteri(target, GL_TEXTURE_MAG_FILTER, max_filter) );
- GE( glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filter) );
-}
-
-void
-cogl_texture_set_wrap (COGLenum target,
- COGLenum wrap_s,
- COGLenum wrap_t)
-{
- GE( glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap_s) );
- GE( glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap_s) );
-}
-
-void
-cogl_texture_image_2d (COGLenum target,
- COGLint internal_format,
- gint width,
- gint height,
- COGLenum format,
- COGLenum type,
- const guchar* pixels)
-{
- GE( glTexImage2D (target,
- 0, /* No mipmap support as yet */
- internal_format,
- width,
- height,
- 0, /* 0 pixel border */
- format,
- type,
- pixels) );
-}
-
-void
-cogl_texture_sub_image_2d (COGLenum target,
- gint xoff,
- gint yoff,
- gint width,
- gint height,
- COGLenum format,
- COGLenum type,
- const guchar* pixels)
-{
- GE( glTexSubImage2D (target,
- 0,
- xoff,
- yoff,
- width,
- height,
- format,
- type,
- pixels));
-}
-
-void
-cogl_rectangle (gint x, gint y, guint width, guint height)
-{
- GE( glRecti (x, y, x + width, y + height) );
-}
-
-/* FIXME: Should use ClutterReal or Fixed */
-void
-cogl_trapezoid (gint y1,
- gint x11,
- gint x21,
- gint y2,
- gint x12,
- gint x22)
-{
- GE( glBegin (GL_QUADS) );
- GE( glVertex2i (x11, y1) );
- GE( glVertex2i (x21, y1) );
- GE( glVertex2i (x22, y2) );
- GE( glVertex2i (x12, y2) );
- GE( glEnd () );
-}
-
-void
cogl_alpha_func (COGLenum func,
ClutterFixed ref)
{
@@ -649,13 +607,17 @@ cogl_setup_viewport (guint width,
GE( glTranslatef (0.0f, -1.0 * height, 0.0f) );
}
-ClutterFeatureFlags
-cogl_get_features ()
+static void
+_cogl_features_init ()
{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
ClutterFeatureFlags flags = 0;
const gchar *gl_extensions;
+ int max_clip_planes = 0;
+ int stencil_bits = 0;
- flags = CLUTTER_FEATURE_TEXTURE_READ_PIXELS;
+ flags = COGL_FEATURE_TEXTURE_READ_PIXELS;
gl_extensions = (const gchar*) glGetString (GL_EXTENSIONS);
@@ -663,53 +625,194 @@ cogl_get_features ()
if (cogl_check_extension ("GL_ARB_texture_rectangle", gl_extensions) ||
cogl_check_extension ("GL_EXT_texture_rectangle", gl_extensions))
{
- flags |= CLUTTER_FEATURE_TEXTURE_RECTANGLE;
+ flags |= COGL_FEATURE_TEXTURE_RECTANGLE;
}
#endif
-
+
+ if (cogl_check_extension ("GL_ARB_texture_non_power_of_two", gl_extensions))
+ {
+ flags |= COGL_FEATURE_TEXTURE_NPOT;
+ }
+
#ifdef GL_YCBCR_MESA
if (cogl_check_extension ("GL_MESA_ycbcr_texture", gl_extensions))
{
- flags |= CLUTTER_FEATURE_TEXTURE_YUV;
+ flags |= COGL_FEATURE_TEXTURE_YUV;
}
#endif
- if (cogl_check_extension ("GL_ARB_vertex_shader", gl_extensions) &&
+ if (cogl_check_extension ("GL_ARB_shader_objects", gl_extensions) &&
+ cogl_check_extension ("GL_ARB_vertex_shader", gl_extensions) &&
cogl_check_extension ("GL_ARB_fragment_shader", gl_extensions))
{
- flags |= CLUTTER_FEATURE_SHADERS_GLSL;
+ ctx->pf_glCreateProgramObjectARB =
+ (PFNGLCREATEPROGRAMOBJECTARBPROC)
+ cogl_get_proc_address ("glCreateProgramObjectARB");
+
+ ctx->pf_glCreateShaderObjectARB =
+ (PFNGLCREATESHADEROBJECTARBPROC)
+ cogl_get_proc_address ("glCreateShaderObjectARB");
+
+ ctx->pf_glShaderSourceARB =
+ (PFNGLSHADERSOURCEARBPROC)
+ cogl_get_proc_address ("glShaderSourceARB");
+
+ ctx->pf_glCompileShaderARB =
+ (PFNGLCOMPILESHADERARBPROC)
+ cogl_get_proc_address ("glCompileShaderARB");
+
+ ctx->pf_glAttachObjectARB =
+ (PFNGLATTACHOBJECTARBPROC)
+ cogl_get_proc_address ("glAttachObjectARB");
+
+ ctx->pf_glLinkProgramARB =
+ (PFNGLLINKPROGRAMARBPROC)
+ cogl_get_proc_address ("glLinkProgramARB");
+
+ ctx->pf_glUseProgramObjectARB =
+ (PFNGLUSEPROGRAMOBJECTARBPROC)
+ cogl_get_proc_address ("glUseProgramObjectARB");
+
+ ctx->pf_glGetUniformLocationARB =
+ (PFNGLGETUNIFORMLOCATIONARBPROC)
+ cogl_get_proc_address ("glGetUniformLocationARB");
+
+ ctx->pf_glDeleteObjectARB =
+ (PFNGLDELETEOBJECTARBPROC)
+ cogl_get_proc_address ("glDeleteObjectARB");
+
+ ctx->pf_glGetInfoLogARB =
+ (PFNGLGETINFOLOGARBPROC)
+ cogl_get_proc_address ("glGetInfoLogARB");
+
+ ctx->pf_glGetObjectParameterivARB =
+ (PFNGLGETOBJECTPARAMETERIVARBPROC)
+ cogl_get_proc_address ("glGetObjectParameterivARB");
+
+ ctx->pf_glUniform1fARB =
+ (PFNGLUNIFORM1FARBPROC)
+ cogl_get_proc_address ("glUniform1fARB");
+
+ if (ctx->pf_glCreateProgramObjectARB &&
+ ctx->pf_glCreateShaderObjectARB &&
+ ctx->pf_glShaderSourceARB &&
+ ctx->pf_glCompileShaderARB &&
+ ctx->pf_glAttachObjectARB &&
+ ctx->pf_glLinkProgramARB &&
+ ctx->pf_glUseProgramObjectARB &&
+ ctx->pf_glGetUniformLocationARB &&
+ ctx->pf_glDeleteObjectARB &&
+ ctx->pf_glGetInfoLogARB &&
+ ctx->pf_glGetObjectParameterivARB &&
+ ctx->pf_glUniform1fARB)
+ flags |= COGL_FEATURE_SHADERS_GLSL;
}
+
if (cogl_check_extension ("GL_EXT_framebuffer_object", gl_extensions) ||
cogl_check_extension ("GL_ARB_framebuffer_object", gl_extensions))
+ {
+ ctx->pf_glGenRenderbuffersEXT =
+ (PFNGLGENRENDERBUFFERSEXTPROC)
+ cogl_get_proc_address ("glGenRenderbuffersEXT");
+
+ ctx->pf_glBindRenderbufferEXT =
+ (PFNGLBINDRENDERBUFFEREXTPROC)
+ cogl_get_proc_address ("glBindRenderbufferEXT");
+
+ ctx->pf_glRenderbufferStorageEXT =
+ (PFNGLRENDERBUFFERSTORAGEEXTPROC)
+ cogl_get_proc_address ("glRenderbufferStorageEXT");
+
+ ctx->pf_glGenFramebuffersEXT =
+ (PFNGLGENFRAMEBUFFERSEXTPROC)
+ cogl_get_proc_address ("glGenFramebuffersEXT");
+
+ ctx->pf_glBindFramebufferEXT =
+ (PFNGLBINDFRAMEBUFFEREXTPROC)
+ cogl_get_proc_address ("glBindFramebufferEXT");
+
+ ctx->pf_glFramebufferTexture2DEXT =
+ (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
+ cogl_get_proc_address ("glFramebufferTexture2DEXT");
+
+ ctx->pf_glFramebufferRenderbufferEXT =
+ (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)
+ cogl_get_proc_address ("glFramebufferRenderbufferEXT");
+
+ ctx->pf_glCheckFramebufferStatusEXT =
+ (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
+ cogl_get_proc_address ("glCheckFramebufferStatusEXT");
+
+ ctx->pf_glDeleteFramebuffersEXT =
+ (PFNGLDELETEFRAMEBUFFERSEXTPROC)
+ cogl_get_proc_address ("glDeleteFramebuffersEXT");
+
+ if (ctx->pf_glGenRenderbuffersEXT &&
+ ctx->pf_glBindRenderbufferEXT &&
+ ctx->pf_glRenderbufferStorageEXT &&
+ ctx->pf_glGenFramebuffersEXT &&
+ ctx->pf_glBindFramebufferEXT &&
+ ctx->pf_glFramebufferTexture2DEXT &&
+ ctx->pf_glFramebufferRenderbufferEXT &&
+ ctx->pf_glCheckFramebufferStatusEXT &&
+ ctx->pf_glDeleteFramebuffersEXT)
+ flags |= COGL_FEATURE_OFFSCREEN;
+ }
+
+ if (cogl_check_extension ("GL_EXT_framebuffer_blit", gl_extensions))
+ {
+ ctx->pf_glBlitFramebufferEXT =
+ (PFNGLBLITFRAMEBUFFEREXTPROC)
+ cogl_get_proc_address ("glBlitFramebufferEXT");
+
+ if (ctx->pf_glBlitFramebufferEXT)
+ flags |= COGL_FEATURE_OFFSCREEN_BLIT;
+ }
+
+ if (cogl_check_extension ("GL_EXT_framebuffer_multisample", gl_extensions))
{
- _gen_framebuffers =
- (GenFramebuffers) cogl_get_proc_address ("glGenFramebuffersEXT");
-
- _bind_framebuffer =
- (BindFramebuffer) cogl_get_proc_address ("glBindFramebufferEXT");
-
- _framebuffer_texture_2d =
- (FramebufferTexture2D)
- cogl_get_proc_address ("glFramebufferTexture2DEXT");
-
- _check_framebuffer_status =
- (CheckFramebufferStatus)
- cogl_get_proc_address ("glCheckFramebufferStatusEXT");
-
- _delete_framebuffers =
- (DeleteFramebuffers)
- cogl_get_proc_address ("glDeleteFramebuffersEXT");
-
- if (_gen_framebuffers
- && _bind_framebuffer
- && _framebuffer_texture_2d
- && _check_framebuffer_status
- && _delete_framebuffers)
- flags |= CLUTTER_FEATURE_OFFSCREEN;
+ ctx->pf_glRenderbufferStorageMultisampleEXT =
+ (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)
+ cogl_get_proc_address ("glRenderbufferStorageMultisampleEXT");
+
+ if (ctx->pf_glRenderbufferStorageMultisampleEXT)
+ flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE;
}
- return flags;
+ GE( glGetIntegerv (GL_STENCIL_BITS, &stencil_bits) );
+ if (stencil_bits > 0)
+ flags |= COGL_FEATURE_STENCIL_BUFFER;
+
+ GE( glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) );
+ if (max_clip_planes >= 4)
+ flags |= COGL_FEATURE_FOUR_CLIP_PLANES;
+
+ /* Cache features */
+ ctx->feature_flags = flags;
+ ctx->features_cached = TRUE;
+}
+
+ClutterFeatureFlags
+cogl_get_features ()
+{
+ _COGL_GET_CONTEXT (ctx, 0);
+
+ if (!ctx->features_cached)
+ _cogl_features_init ();
+
+ return ctx->feature_flags;
+}
+
+gboolean
+cogl_features_available (CoglFeatureFlags features)
+{
+ _COGL_GET_CONTEXT (ctx, 0);
+
+ if (!ctx->features_cached)
+ _cogl_features_init ();
+
+ return (ctx->feature_flags & features) == features;
}
void
@@ -835,217 +938,79 @@ cogl_fog_set (const ClutterColor *fog_color,
glFogf (GL_FOG_END, CLUTTER_FIXED_TO_FLOAT (stop));
}
-/* FBOs - offscreen */
-
-COGLuint
-cogl_offscreen_create (COGLuint target_texture)
-{
-#ifdef GL_FRAMEBUFFER_EXT
- COGLuint handle;
- GLenum status;
-
- if (_gen_framebuffers == NULL
- || _bind_framebuffer == NULL
- || _framebuffer_texture_2d == NULL
- || _check_framebuffer_status == NULL)
- {
- /* tmp warning - need error reporting */
- g_warning("Missing GL_FRAMEBUFFER_EXT API\n");
- return 0;
- }
-
- _gen_framebuffers (1, &handle);
- _bind_framebuffer (GL_FRAMEBUFFER_EXT, handle);
-
- _framebuffer_texture_2d (GL_FRAMEBUFFER_EXT,
- GL_COLOR_ATTACHMENT0_EXT,
- GL_TEXTURE_RECTANGLE_ARB,
- target_texture,
- 0);
-
- status = _check_framebuffer_status (GL_FRAMEBUFFER_EXT);
-
- if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
- {
- _delete_framebuffers (1, &handle);
- return 0;
- }
-
- _bind_framebuffer (GL_FRAMEBUFFER_EXT, 0);
-
- return handle;
-#else
- /* tmp warning - need error reporting */
- g_warning("No GL_FRAMEBUFFER_EXT\n");
- return 0;
-#endif
-}
-
-void
-cogl_offscreen_destroy (COGLuint offscreen_handle)
-{
- if (_delete_framebuffers)
- _delete_framebuffers (1, &offscreen_handle);
-}
-
-void
-cogl_offscreen_redirect_start (COGLuint offscreen_handle,
- gint width,
- gint height)
-{
- /* FIXME: silly we need to pass width / height to init viewport */
-#ifdef GL_FRAMEBUFFER_EXT
-
- if (_bind_framebuffer == NULL)
- return;
-
- _bind_framebuffer (GL_FRAMEBUFFER_EXT, offscreen_handle);
-
- glViewport (0, 0, width, height);
-
- glMatrixMode (GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity ();
-
- glMatrixMode (GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity ();
-
- glTranslatef (-1.0f, -1.0f, 0.0f);
- glScalef (2.0f / (float)width, 2.0f / (float)height, 1.0f);
-
- /* Clear the scene, appears needed on some backends - OSX */
- glClearColor (0.0, 0.0, 0.0, 0.0);
- glClear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-
-#endif
-}
-
-void
-cogl_offscreen_redirect_end (COGLuint offscreen_handle,
- gint width,
- gint height)
-{
- /* FIXME: silly we need to pass width / height to reset to */
- if (_bind_framebuffer == NULL)
- return;
-
-#ifdef GL_FRAMEBUFFER_EXT
- glViewport (0, 0, width, height);
-
- glMatrixMode (GL_PROJECTION);
- glPopMatrix();
-
- glMatrixMode (GL_MODELVIEW);
- glPopMatrix();
-
- _bind_framebuffer (GL_FRAMEBUFFER_EXT, 0);
-#endif
-}
-
-
/* Shader Magic follows */
-#ifdef __GNUC__
-
-#define PROC(rettype, retval, procname, args...) \
- static rettype (*proc) (args) = NULL; \
- if (proc == NULL) \
- { \
- proc = (void*)cogl_get_proc_address (#procname);\
- if (!proc)\
- {\
- g_warning ("failed to lookup proc: %s", #procname);\
- return retval;\
- }\
- }
-#else
-
-#define PROC(rettype, retval, procname, ...) \
- static rettype (*proc) (__VA_ARGS__) = NULL; \
- if (proc == NULL) \
- { \
- proc = (void*)cogl_get_proc_address (#procname);\
- if (!proc)\
- {\
- g_warning ("failed to lookup proc: %s", #procname);\
- return retval;\
- }\
- }
-
-#endif
-
COGLhandle
cogl_create_program (void)
{
- PROC (GLhandleARB, 0, glCreateProgramObjectARB, void);
- return proc ();
+ _COGL_GET_CONTEXT (ctx, 0);
+ return glCreateProgramObjectARB ();
}
COGLhandle
cogl_create_shader (COGLenum shaderType)
{
- PROC (GLhandleARB, 0, glCreateShaderObjectARB, GLenum);
- return proc (shaderType);
+ _COGL_GET_CONTEXT (ctx, 0);
+ return glCreateShaderObjectARB (shaderType);
}
void
cogl_shader_source (COGLhandle shader,
const gchar *source)
{
- PROC (GLvoid,, glShaderSourceARB, GLhandleARB, GLsizei, const GLcharARB **, const GLint *)
- proc (shader, 1, &source, NULL);
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ glShaderSourceARB (shader, 1, &source, NULL);
}
void
cogl_shader_compile (COGLhandle shader_handle)
{
- PROC (GLvoid,, glCompileShaderARB, GLhandleARB);
- proc (shader_handle);
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ glCompileShaderARB (shader_handle);
}
void
cogl_program_attach_shader (COGLhandle program_handle,
COGLhandle shader_handle)
{
- PROC (GLvoid,, glAttachObjectARB, GLhandleARB, GLhandleARB);
- proc (program_handle, shader_handle);
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ glAttachObjectARB (program_handle, shader_handle);
}
void
cogl_program_link (COGLhandle program_handle)
{
- PROC (GLvoid,, glLinkProgramARB, GLhandleARB);
- proc (program_handle);
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ glLinkProgramARB (program_handle);
}
void
cogl_program_use (COGLhandle program_handle)
{
- PROC (GLvoid,, glUseProgramObjectARB, GLhandleARB);
- proc (program_handle);
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ glUseProgramObjectARB (program_handle);
}
COGLint
cogl_program_get_uniform_location (COGLhandle program_handle,
const gchar *uniform_name)
{
- PROC (GLint,0, glGetUniformLocationARB, GLhandleARB, const GLcharARB *)
- return proc (program_handle, uniform_name);
+ _COGL_GET_CONTEXT (ctx, 0);
+ return glGetUniformLocationARB (program_handle, uniform_name);
}
void
cogl_program_destroy (COGLhandle handle)
{
- PROC (GLvoid,, glDeleteObjectARB, GLhandleARB);
- proc (handle);
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ glDeleteObjectARB (handle);
}
void
cogl_shader_destroy (COGLhandle handle)
{
- PROC (GLvoid,, glDeleteObjectARB, GLhandleARB);
- proc (handle);
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ glDeleteObjectARB (handle);
}
void
@@ -1054,8 +1019,8 @@ cogl_shader_get_info_log (COGLhandle handle,
gchar *buffer)
{
COGLint len;
- PROC (GLvoid,, glGetInfoLogARB, GLhandleARB, GLsizei, GLsizei *, GLcharARB *);
- proc (handle, size-1, &len, buffer);
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ glGetInfoLogARB (handle, size-1, &len, buffer);
buffer[len]='\0';
}
@@ -1064,15 +1029,14 @@ cogl_shader_get_parameteriv (COGLhandle handle,
COGLenum pname,
COGLint *dest)
{
- PROC (GLvoid,, glGetObjectParameterivARB, GLhandleARB, GLenum, GLint*)
- proc (handle, pname, dest);
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ glGetObjectParameterivARB (handle, pname, dest);
}
-
void
cogl_program_uniform_1f (COGLint uniform_no,
gfloat value)
{
- PROC (GLvoid,, glUniform1fARB, GLint, GLfloat);
- proc (uniform_no, value);
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ glUniform1fARB (uniform_no, value);
}
diff --git a/gles/Makefile.am b/gles/Makefile.am
index 32dda4a6..c629e1ee 100644
--- a/gles/Makefile.am
+++ b/gles/Makefile.am
@@ -1,19 +1,36 @@
-libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/clutter
-libclutterinclude_HEADERS = $(top_srcdir)/clutter/cogl/cogl.h \
- $(top_srcdir)/clutter/cogl/gles/cogl-defines.h
+libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/cogl
+libclutterinclude_HEADERS = $(top_builddir)/clutter/cogl/cogl.h \
+ $(top_builddir)/clutter/cogl/cogl-defines-gles.h
INCLUDES = \
- -I$(top_srcdir) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/clutter \
-I$(top_srcdir)/clutter/cogl \
- $(CLUTTER_CFLAGS) \
- $(CLUTTER_DEBUG_CFLAGS) \
+ -I$(top_srcdir)/clutter/cogl/common \
+ -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \
+ -I$(top_builddir)/clutter \
+ -I$(top_builddir)/clutter/cogl \
+ $(CLUTTER_CFLAGS) \
+ $(CLUTTER_DEBUG_CFLAGS) \
$(GCC_FLAGS)
LDADD = $(CLUTTER_LIBS)
noinst_LTLIBRARIES = libclutter-cogl.la
-libclutter_cogl_la_SOURCES = \
- $(top_srcdir)/clutter/cogl/cogl.h \
- $(top_srcdir)/clutter/cogl/gl/cogl-defines.h \
- cogl.c
+libclutter_cogl_la_SOURCES = \
+ $(top_builddir)/clutter/cogl/cogl.h \
+ $(top_builddir)/clutter/cogl/gles/cogl-defines.h \
+ cogl-defines.h \
+ cogl-internal.h \
+ cogl-primitives.h \
+ cogl-texture.h \
+ cogl-fbo.h \
+ cogl-context.h \
+ cogl.c \
+ cogl-primitives.c \
+ cogl-texture.c \
+ cogl-fbo.c \
+ cogl-context.c
+
+libclutter_cogl_la_LIBADD = $(top_builddir)/clutter/cogl/common/libclutter-cogl-common.la
diff --git a/gles/cogl-context.c b/gles/cogl-context.c
new file mode 100644
index 00000000..0027e96d
--- /dev/null
+++ b/gles/cogl-context.c
@@ -0,0 +1,96 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+
+#include <GLES/gl.h>
+#include <string.h>
+
+#include "cogl-internal.h"
+#include "cogl-util.h"
+#include "cogl-context.h"
+
+static CoglContext *_context = NULL;
+
+gboolean
+cogl_create_context ()
+{
+ if (_context != NULL)
+ return FALSE;
+
+ /* Allocate context memory */
+ _context = (CoglContext*) g_malloc (sizeof (CoglContext));
+
+ /* Init default values */
+ _context->feature_flags = 0;
+ _context->features_cached = FALSE;
+
+ _context->enable_flags = 0;
+ _context->color_alpha = 255;
+
+ _context->path_nodes = NULL;
+ _context->path_nodes_cap = 0;
+ _context->path_nodes_size = 0;
+
+ _context->texture_handles = NULL;
+ _context->texture_vertices_size = 0;
+ _context->texture_vertices = NULL;
+
+ _context->fbo_handles = NULL;
+ _context->draw_buffer = COGL_WINDOW_BUFFER;
+
+ /* Init OpenGL state */
+ glTexEnvx (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glColorMask (TRUE, TRUE, TRUE, FALSE);
+ cogl_enable (0);
+
+ return TRUE;
+}
+
+void
+cogl_destroy_context ()
+{
+ if (_context == NULL)
+ return;
+
+ if (_context->texture_vertices)
+ g_free (_context->texture_vertices);
+
+ g_free (_context);
+}
+
+CoglContext *
+_cogl_context_get_default ()
+{
+ /* Create if doesn't exists yet */
+ if (_context == NULL)
+ cogl_create_context ();
+
+ return _context;
+}
diff --git a/gles/cogl-context.h b/gles/cogl-context.h
new file mode 100644
index 00000000..c5117a9f
--- /dev/null
+++ b/gles/cogl-context.h
@@ -0,0 +1,78 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_CONTEXT_H
+#define __COGL_CONTEXT_H
+
+#include "cogl-primitives.h"
+
+typedef struct
+{
+ GLfixed v[3];
+ GLfixed t[2];
+ GLfixed c[4];
+} CoglTextureGLVertex;
+
+typedef struct
+{
+ /* Features cache */
+ CoglFeatureFlags feature_flags;
+ gboolean features_cached;
+
+ /* Enable cache */
+ gulong enable_flags;
+ guint8 color_alpha;
+
+ /* Primitives */
+ CoglFixedVec2 path_start;
+ CoglFixedVec2 path_pen;
+ CoglFixedVec2 *path_nodes;
+ guint path_nodes_cap;
+ guint path_nodes_size;
+ CoglFixedVec2 path_nodes_min;
+ CoglFixedVec2 path_nodes_max;
+
+ /* Textures */
+ GArray *texture_handles;
+ CoglTextureGLVertex *texture_vertices;
+ gulong texture_vertices_size;
+
+ /* Framebuffer objects */
+ GArray *fbo_handles;
+ CoglBufferTarget draw_buffer;
+
+} CoglContext;
+
+CoglContext *
+_cogl_context_get_default ();
+
+/* Obtains the context and returns retval if NULL */
+#define _COGL_GET_CONTEXT(ctxvar, retval) \
+CoglContext *ctxvar = _cogl_context_get_default (); \
+if (ctxvar == NULL) return retval;
+
+#define NO_RETVAL
+
+#endif /* __COGL_CONTEXT_H */
diff --git a/gles/cogl-defines.h b/gles/cogl-defines.h.in
index 83f2ae15..b304680f 100644
--- a/gles/cogl-defines.h
+++ b/gles/cogl-defines.h.in
@@ -444,18 +444,6 @@ typedef GLuint COGLhandle;
/* extras */
-#define CGL_TEXTURE_2D GL_TEXTURE_2D
-#define CGL_ARGB GL_ARGB
-
-/* FIXME: There is no BGR support in GLES - so with below BGR textures are
- * borked. Will likely need a feature flag and some coversion..
- */
-#define CGL_BGR GL_RGB
-#define CGL_BGRA GL_RGBA
-
-/* Its unlikely we support this */
-#define CGL_TEXTURE_RECTANGLE_ARB 0
-
/* YUV textures also unsupported */
#define CGL_YCBCR_MESA 0
#define CGL_UNSIGNED_SHORT_8_8_REV_MESA 0
diff --git a/gles/cogl-fbo.c b/gles/cogl-fbo.c
new file mode 100644
index 00000000..e8f186b3
--- /dev/null
+++ b/gles/cogl-fbo.c
@@ -0,0 +1,188 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-util.h"
+#include "cogl-texture.h"
+#include "cogl-fbo.h"
+#include "cogl-context.h"
+
+/* Expecting EXT functions not to be defined - redirect to pointers in context */
+#define glGenRenderbuffersEXT ctx->pf_glGenRenderbuffersEXT
+#define glBindRenderbufferEXT ctx->pf_glBindRenderbufferEXT
+#define glRenderbufferStorageEXT ctx->pf_glRenderbufferStorageEXT
+#define glGenFramebuffersEXT ctx->pf_glGenFramebuffersEXT
+#define glBindFramebufferEXT ctx->pf_glBindFramebufferEXT
+#define glFramebufferTexture2DEXT ctx->pf_glFramebufferTexture2DEXT
+#define glFramebufferRenderbufferEXT ctx->pf_glFramebufferRenderbufferEXT
+#define glCheckFramebufferStatusEXT ctx->pf_glCheckFramebufferStatusEXT
+#define glDeleteFramebuffersEXT ctx->pf_glDeleteFramebuffersEXT
+#define glBlitFramebufferEXT ctx->pf_glBlitFramebufferEXT
+#define glRenderbufferStorageMultisampleEXT ctx->pf_glRenderbufferStorageMultisampleEXT
+
+static gint
+_cogl_fbo_handle_find (CoglHandle handle)
+{
+ _COGL_GET_CONTEXT (ctx, -1);
+
+ gint i;
+
+ if (ctx->fbo_handles == NULL)
+ return -1;
+
+ for (i=0; i < ctx->fbo_handles->len; ++i)
+ if (g_array_index (ctx->fbo_handles, CoglHandle, i) == handle)
+ return i;
+
+ return -1;
+}
+/*
+static CoglHandle
+_cogl_fbo_handle_new (CoglFbo *fbo)
+{
+ _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
+
+ CoglHandle handle = (CoglHandle)fbo;
+
+ if (ctx->fbo_handles == NULL)
+ ctx->fbo_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle));
+
+ g_array_append_val (ctx->fbo_handles, handle);
+
+ return handle;
+}
+
+static void
+_cogl_fbo_handle_release (CoglHandle handle)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ gint i;
+
+ if ( (i = _cogl_fbo_handle_find (handle)) == -1)
+ return;
+
+ g_array_remove_index_fast (ctx->fbo_handles, i);
+}
+
+static CoglFbo*
+_cogl_fbo_pointer_from_handle (CoglHandle handle)
+{
+ return (CoglFbo*) handle;
+}*/
+
+gboolean
+cogl_is_offscreen_buffer (CoglHandle handle)
+{
+ if (handle == COGL_INVALID_HANDLE)
+ return FALSE;
+
+ return _cogl_fbo_handle_find (handle) >= 0;
+}
+
+CoglHandle
+cogl_offscreen_new_to_texture (CoglHandle texhandle)
+{
+ return COGL_INVALID_HANDLE;
+}
+
+CoglHandle
+cogl_offscreen_new_multisample ()
+{
+ return COGL_INVALID_HANDLE;
+}
+
+CoglHandle
+cogl_offscreen_ref (CoglHandle handle)
+{
+ return COGL_INVALID_HANDLE;
+}
+
+void
+cogl_offscreen_unref (CoglHandle handle)
+{
+}
+
+void
+cogl_offscreen_blit_region (CoglHandle src_buffer,
+ CoglHandle dst_buffer,
+ int src_x,
+ int src_y,
+ int src_w,
+ int src_h,
+ int dst_x,
+ int dst_y,
+ int dst_w,
+ int dst_h)
+{
+}
+
+void
+cogl_offscreen_blit (CoglHandle src_buffer,
+ CoglHandle dst_buffer)
+{
+}
+
+void
+cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ switch (target)
+ {
+ case COGL_OFFSCREEN_BUFFER:
+
+ /* Not supported */
+ return;
+
+ case COGL_WINDOW_BUFFER:
+
+ /* Draw to RGB channels */
+ GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE) );
+
+ break;
+
+ case COGL_MASK_BUFFER:
+
+ /* Draw only to ALPHA channel */
+ GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE) );
+
+ break;
+ case COGL_WINDOW_BUFFER & COGL_MASK_BUFFER:
+
+ /* Draw to all channels */
+ GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) );
+
+ break;
+ }
+
+ /* Store new target */
+ ctx->draw_buffer = target;
+}
diff --git a/gles/cogl-fbo.h b/gles/cogl-fbo.h
new file mode 100644
index 00000000..f2dd5298
--- /dev/null
+++ b/gles/cogl-fbo.h
@@ -0,0 +1,38 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_FBO_H
+#define __COGL_FBO_H
+
+typedef struct
+{
+ guint ref_count;
+ int width;
+ int height;
+ GLuint gl_handle;
+
+} CoglFbo;
+
+#endif /* __COGL_FBO_H */
diff --git a/gles/cogl-internal.h b/gles/cogl-internal.h
new file mode 100644
index 00000000..69baa4a9
--- /dev/null
+++ b/gles/cogl-internal.h
@@ -0,0 +1,63 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_INTERNAL_H
+#define __COGL_INTERNAL_H
+
+#define COGL_DEBUG 0
+
+#if COGL_DEBUG
+#define GE(x...) { \
+ GLenum err; \
+ (x); \
+ fprintf(stderr, "%s\n", #x); \
+ while ((err = glGetError()) != GL_NO_ERROR) { \
+ fprintf(stderr, "glError: %s caught at %s:%u\n", \
+ (char *)error_string(err), \
+ __FILE__, __LINE__); \
+ } \
+}
+#else
+#define GE(x) (x);
+#endif
+
+#define COGL_ENABLE_BLEND (1<<1)
+#define COGL_ENABLE_TEXTURE_2D (1<<2)
+#define COGL_ENABLE_ALPHA_TEST (1<<3)
+#define COGL_ENABLE_TEXTURE_RECT (1<<4)
+#define COGL_ENABLE_VERTEX_ARRAY (1<<5)
+#define COGL_ENABLE_TEXCOORD_ARRAY (1<<6)
+#define COGL_ENABLE_COLOR_ARRAY (1<<7)
+
+gint
+_cogl_get_format_bpp (CoglPixelFormat format);
+
+void
+cogl_enable (gulong flags);
+
+gulong
+cogl_get_enable ();
+
+#endif /* __COGL_INTERNAL_H */
diff --git a/gles/cogl-primitives.c b/gles/cogl-primitives.c
new file mode 100644
index 00000000..1bba58f5
--- /dev/null
+++ b/gles/cogl-primitives.c
@@ -0,0 +1,1112 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-context.h"
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _COGL_MAX_BEZ_RECURSE_DEPTH 16
+
+/**
+ * SECTION:cogl-primitives
+ * @short_description: Functions that draw various primitive shapes and
+ * allow for construction of more complex paths.
+ *
+ * There are three levels on which drawing with cogl can be used. The
+ * highest level functions construct various simple primitive shapes
+ * to be either filled or stroked. Using a lower-level set of functions
+ * more complex and arbitrary paths can be constructed by concatenating
+ * straight line, bezier curve and arc segments. Additionally there
+ * are utility functions that draw the most common primitives - rectangles
+ * and trapezoids - in a maximaly optimized fashion.
+ *
+ * When constructing arbitrary paths, the current pen location is
+ * initialized using the move_to command. The subsequent path segments
+ * implicitly use the last pen location as their first vertex and move
+ * the pen location to the last vertex they produce at the end. Also
+ * there are special versions of functions that allow specifying the
+ * vertices of the path segments relative to the last pen location
+ * rather then in the absolute coordinates.
+ */
+
+/**
+ * cogl_fast_fill_rectangle:
+ * @x: X coordinate of the top-left corner
+ * @y: Y coordinate of the top-left corner
+ * @width: Width of the rectangle
+ * @height: Height of the rectangle
+ *
+ * Fills a rectangle at the given coordinates with the current
+ * drawing color in a highly optimizied fashion.
+ **/
+void
+cogl_fast_fill_rectangle (gint x,
+ gint y,
+ guint width,
+ guint height)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ /* 32-bit integers are not supported as coord types
+ in GLES . Fixed type has got 16 bits left of the
+ point which is equal to short anyway. */
+
+ GLshort rect_verts[8] = {
+ (GLshort) x, (GLshort) y,
+ (GLshort) (x + width), (GLshort) y,
+ (GLshort) x, (GLshort) (y + height),
+ (GLshort) (x + width), (GLshort) (y + height)
+ };
+
+ cogl_enable (COGL_ENABLE_VERTEX_ARRAY
+ | (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0));
+
+ GE( glVertexPointer (2, GL_SHORT, 0, rect_verts) );
+ GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) );
+}
+
+/**
+ * cogl_fast_fill_rectanglex:
+ * @x: X coordinate of the top-left corner
+ * @y: Y coordinate of the top-left corner
+ * @width: Width of the rectangle
+ * @height: Height of the rectangle
+ *
+ * A fixed-point version of cogl_fast_fill_rectangle.
+ **/
+void
+cogl_fast_fill_rectanglex (ClutterFixed x,
+ ClutterFixed y,
+ ClutterFixed width,
+ ClutterFixed height)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ GLfixed rect_verts[8] = {
+ x, y,
+ x + width, y,
+ x, y + height,
+ x + width, y + height
+ };
+
+ cogl_enable (COGL_ENABLE_VERTEX_ARRAY
+ | (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0));
+
+ GE( glVertexPointer (2, GL_FIXED, 0, rect_verts) );
+ GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) );
+}
+
+/**
+ * cogl_fast_fill_trapezoid:
+ * @y1: Y coordinate of the top two vertices.
+ * @x11: X coordinate of the top-left vertex.
+ * @x21: X coordinate of the top-right vertex.
+ * @y2: Y coordinate of the bottom two vertices.
+ * @x12: X coordinate of the bottom-left vertex.
+ * @x22: X coordinate of the bottom-right vertex.
+ *
+ * Fills a trapezoid at the given coordinates with the current
+ * drawing color in a highly optimized fashion.
+ **/
+void
+cogl_fast_fill_trapezoid (gint y1,
+ gint x11,
+ gint x21,
+ gint y2,
+ gint x12,
+ gint x22)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ GLshort trap_verts[8] = {
+ (GLshort) x11, (GLshort) y1,
+ (GLshort) x21, (GLshort) y1,
+ (GLshort) x12, (GLshort) y2,
+ (GLshort) x22, (GLshort) y2
+ };
+
+ cogl_enable (COGL_ENABLE_VERTEX_ARRAY
+ | (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0));
+
+ GE( glVertexPointer (2, GL_SHORT, 0, trap_verts) );
+ GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) );
+}
+
+/**
+ * cogl_fast_fill_trapezoidx:
+ * @y1: Y coordinate of the top two vertices.
+ * @x11: X coordinate of the top-left vertex.
+ * @x21: X coordinate of the top-right vertex.
+ * @y2: Y coordinate of the bottom two vertices.
+ * @x12: X coordinate of the bottom-left vertex.
+ * @x22: X coordinate of the bottom-right vertex.
+ *
+ * A fixed-point version of cogl_fast_fill_trapezoid.
+ **/
+void
+cogl_fast_fill_trapezoidx (ClutterFixed y1,
+ ClutterFixed x11,
+ ClutterFixed x21,
+ ClutterFixed y2,
+ ClutterFixed x12,
+ ClutterFixed x22)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ GLfixed trap_verts[8] = {
+ x11, y1,
+ x21, y1,
+ x12, y2,
+ x22, y2
+ };
+
+ cogl_enable (COGL_ENABLE_VERTEX_ARRAY
+ | (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0));
+
+ GE( glVertexPointer (2, GL_FIXED, 0, trap_verts) );
+ GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) );
+}
+
+static void
+_cogl_path_clear_nodes ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (ctx->path_nodes)
+ g_free (ctx->path_nodes);
+
+ ctx->path_nodes = (CoglFixedVec2*) g_malloc (2 * sizeof(CoglFixedVec2));
+ ctx->path_nodes_size = 0;
+ ctx->path_nodes_cap = 2;
+}
+
+static void
+_cogl_path_add_node (ClutterFixed x,
+ ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ CoglFixedVec2 *new_nodes = NULL;
+
+ if (ctx->path_nodes_size == ctx->path_nodes_cap)
+ {
+ new_nodes = g_realloc (ctx->path_nodes,
+ 2 * ctx->path_nodes_cap
+ * sizeof (CoglFixedVec2));
+
+ if (new_nodes == NULL) return;
+
+ ctx->path_nodes = new_nodes;
+ ctx->path_nodes_cap *= 2;
+ }
+
+ ctx->path_nodes [ctx->path_nodes_size] .x = x;
+ ctx->path_nodes [ctx->path_nodes_size] .y = y;
+ ctx->path_nodes_size++;
+
+ if (ctx->path_nodes_size == 1)
+ {
+ ctx->path_nodes_min.x = ctx->path_nodes_max.x = x;
+ ctx->path_nodes_min.y = ctx->path_nodes_max.y = y;
+ }
+ else
+ {
+ if (x < ctx->path_nodes_min.x) ctx->path_nodes_min.x = x;
+ if (x > ctx->path_nodes_max.x) ctx->path_nodes_max.x = x;
+ if (y < ctx->path_nodes_min.y) ctx->path_nodes_min.y = y;
+ if (y > ctx->path_nodes_max.y) ctx->path_nodes_max.y = y;
+ }
+}
+
+static void
+_cogl_path_stroke_nodes ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_enable (COGL_ENABLE_VERTEX_ARRAY
+ | (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0));
+
+ GE( glVertexPointer (2, GL_FIXED, 0, ctx->path_nodes) );
+ GE( glDrawArrays (GL_LINE_STRIP, 0, ctx->path_nodes_size) );
+}
+
+static void
+_cogl_path_fill_nodes ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ guint bounds_x;
+ guint bounds_y;
+ guint bounds_w;
+ guint bounds_h;
+
+ GE( glClear (GL_STENCIL_BUFFER_BIT) );
+
+ GE( glEnable (GL_STENCIL_TEST) );
+ GE( glStencilFunc (GL_ALWAYS, 0x0, 0x0) );
+ GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) );
+ GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) );
+
+ cogl_enable (COGL_ENABLE_VERTEX_ARRAY
+ | (ctx->color_alpha < 255
+ ? COGL_ENABLE_BLEND : 0));
+
+ GE( glVertexPointer (2, GL_FIXED, 0, ctx->path_nodes) );
+ GE( glDrawArrays (GL_TRIANGLE_FAN, 0, ctx->path_nodes_size) );
+
+ GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
+ GE( glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) );
+ GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) );
+
+ bounds_x = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.x);
+ bounds_y = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.y);
+ bounds_w = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.x - ctx->path_nodes_min.x);
+ bounds_h = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.y - ctx->path_nodes_min.y);
+
+ cogl_fast_fill_rectangle (bounds_x, bounds_y, bounds_w, bounds_h);
+
+ GE( glDisable (GL_STENCIL_TEST) );
+}
+
+/**
+ * cogl_fill:
+ *
+ * Fills the constructed shape using the current drawing color.
+ **/
+void
+cogl_fill ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (ctx->path_nodes_size == 0)
+ return;
+
+ _cogl_path_fill_nodes();
+}
+
+/**
+ * cogl_stroke:
+ *
+ * Strokes the constructed shape using the current drawing color
+ * and a width of 1 pixel (regardless of the current transformation
+ * matrix).
+ **/
+void
+cogl_stroke ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (ctx->path_nodes_size == 0)
+ return;
+
+ _cogl_path_stroke_nodes();
+}
+
+/**
+ * cogl_path_move_to:
+ * @x: X coordinate of the pen location to move to.
+ * @y: Y coordinate of the pen location to move to.
+ *
+ * Clears the previously constructed shape and begins a new path
+ * contour by moving the pen to the given coordinates.
+ **/
+void
+cogl_path_move_to (ClutterFixed x,
+ ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ /* FIXME: handle multiple contours maybe? */
+
+ _cogl_path_clear_nodes ();
+ _cogl_path_add_node (x, y);
+
+ ctx->path_start.x = x;
+ ctx->path_start.y = y;
+
+ ctx->path_pen = ctx->path_start;
+}
+
+/**
+ * cogl_path_move_to_rel:
+ * @x: X offset from the current pen location to move the pen to.
+ * @y: Y offset from the current pen location to move the pen to.
+ *
+ * Clears the previously constructed shape and begins a new path
+ * contour by moving the pen to the given coordinates relative
+ * to the current pen location.
+ **/
+void
+cogl_path_move_to_rel (ClutterFixed x,
+ ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_move_to (ctx->path_pen.x + x,
+ ctx->path_pen.y + y);
+}
+
+/**
+ * cogl_path_line_to:
+ * @x: X coordinate of the end line vertex
+ * @y: Y coordinate of the end line vertex
+ *
+ * Adds a straight line segment to the current path that ends at the
+ * given coordinates.
+ **/
+void
+cogl_path_line_to (ClutterFixed x,
+ ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ _cogl_path_add_node (x, y);
+
+ ctx->path_pen.x = x;
+ ctx->path_pen.y = y;
+}
+
+/**
+ * cogl_path_line_to:
+ * @x: X offset from the current pen location of the end line vertex
+ * @y: Y offset from the current pen location of the end line vertex
+ *
+ * Adds a straight line segment to the current path that ends at the
+ * given coordinates relative to the current pen location.
+ **/
+void
+cogl_path_line_to_rel (ClutterFixed x,
+ ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_line_to (ctx->path_pen.x + x,
+ ctx->path_pen.y + y);
+}
+
+/**
+ * cogl_path_h_line_to:
+ * @x: X coordinate of the end line vertex
+ *
+ * Adds a straight horizontal line segment to the current path that
+ * ends at the given X coordinate and current pen Y coordinate.
+ **/
+void
+cogl_path_h_line_to (ClutterFixed x)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_line_to (x,
+ ctx->path_pen.y);
+}
+
+/**
+ * cogl_path_v_line_to:
+ * @y: Y coordinate of the end line vertex
+ *
+ * Adds a stright vertical line segment to the current path that ends
+ * at the current pen X coordinate and the given Y coordinate.
+ **/
+void
+cogl_path_v_line_to (ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_line_to (ctx->path_pen.x,
+ y);
+}
+
+/**
+ * cogl_path_h_line_to_rel:
+ * @x: X offset from the current pen location of the end line vertex
+ *
+ * Adds a straight horizontal line segment to the current path that
+ * ends at the given X coordinate relative to the current pen location
+ * and current pen Y coordinate.
+ **/
+void
+cogl_path_h_line_to_rel (ClutterFixed x)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_line_to (ctx->path_pen.x + x,
+ ctx->path_pen.y);
+}
+
+/**
+ * cogl_path_v_line_to_rel:
+ * @y: Y offset from the current pen location of the end line vertex
+ *
+ * Adds a stright vertical line segment to the current path that ends
+ * at the current pen X coordinate and the given Y coordinate relative
+ * to the current pen location.
+ **/
+void
+cogl_path_v_line_to_rel (ClutterFixed y)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_line_to (ctx->path_pen.x,
+ ctx->path_pen.y + y);
+}
+
+/**
+ * cogl_path_close:
+ *
+ * Closes the path being constructed by adding a straight line segment
+ * to it that ends at the first vertex of the path.
+ **/
+void
+cogl_path_close ()
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ _cogl_path_add_node (ctx->path_start.x, ctx->path_start.y);
+ ctx->path_pen = ctx->path_start;
+}
+
+/**
+ * cogl_line:
+ * @x1: X coordinate of the start line vertex
+ * @y1: Y coordinate of the start line vertex
+ * @x2: X coordinate of the end line vertex
+ * @y2: Y coordinate of the end line vertex
+ *
+ * Clears the previously constructed shape and constructs a straight
+ * line shape start and ending at the given coordinates.
+ **/
+void
+cogl_line (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2)
+{
+ cogl_path_move_to (x1, y1);
+ cogl_path_line_to (x2, y2);
+}
+
+/**
+ * cogl_polyline:
+ * @coords: A pointer to the first element of an array of fixed-point
+ * values that specify the vertex coordinates.
+ * @num_points: The total number of vertices.
+ *
+ * Clears the previously constructed shape and constructs a series of straight
+ * line segments, starting from the first given vertex coordinate. Each
+ * subsequent segment stars where the previous one ended and ends at the next
+ * given vertex coordinate.
+ *
+ * The coords array must contain 2 * num_points values. The first value
+ * represents the X coordinate of the first vertex, the second value
+ * represents the Y coordinate of the first vertex, continuing in the same
+ * fashion for the rest of the vertices. (num_points - 1) segments will
+ * be constructed.
+ **/
+void
+cogl_polyline (ClutterFixed *coords,
+ gint num_points)
+{
+ gint c = 0;
+
+ cogl_path_move_to(coords[0], coords[1]);
+
+ for (c = 1; c < num_points; ++c)
+ cogl_path_line_to(coords[2*c], coords[2*c+1]);
+}
+
+/**
+ * cogl_polygon:
+ * @coords: A pointer to the first element of an array of fixed-point
+ * values that specify the vertex coordinates.
+ * @num_points: The total number of vertices.
+ *
+ * Clears the previously constructed shape and constructs a polygonal
+ * shape of the given number of vertices.
+ *
+ * The coords array must contain 2 * num_points values. The first value
+ * represents the X coordinate of the first vertex, the second value
+ * represents the Y coordinate of the first vertex, continuing in the same
+ * fashion for the rest of the vertices.
+ **/
+void
+cogl_polygon (ClutterFixed *coords,
+ gint num_points)
+{
+ cogl_polyline (coords, num_points);
+ cogl_path_close ();
+}
+
+/**
+ * cogl_rectangle:
+ * @x: X coordinate of the top-left corner.
+ * @y: Y coordinate of the top-left corner.
+ * @width: Rectangle width.
+ * @height: Rectangle height.
+ *
+ * Clears the previously constructed shape and constructs a rectangular
+ * shape at the given coordinates.
+ **/
+void
+cogl_rectangle (ClutterFixed x,
+ ClutterFixed y,
+ ClutterFixed width,
+ ClutterFixed height)
+{
+ cogl_path_move_to (x, y);
+ cogl_path_line_to (x + width, y);
+ cogl_path_line_to (x + width, y + height);
+ cogl_path_line_to (x, y + height);
+ cogl_path_close ();
+}
+
+static void
+_cogl_arc (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_1,
+ ClutterAngle angle_2,
+ ClutterAngle angle_step,
+ guint move_first)
+{
+ ClutterAngle a = 0x0;
+ ClutterAngle temp = 0x0;
+ ClutterFixed cosa = 0x0;
+ ClutterFixed sina = 0x0;
+ ClutterFixed px = 0x0;
+ ClutterFixed py = 0x0;
+
+ /* Fix invalid angles */
+
+ if (angle_1 == angle_2 || angle_step == 0x0)
+ return;
+
+ if (angle_step < 0x0)
+ angle_step = -angle_step;
+
+ if (angle_2 < angle_1)
+ {
+ temp = angle_1;
+ angle_1 = angle_2;
+ angle_2 = temp;
+ }
+
+ /* Walk the arc by given step */
+
+ for (a = angle_1; a < angle_2; a += angle_step)
+ {
+ cosa = clutter_cosi (a);
+ sina = clutter_sini (a);
+
+ px = center_x + CFX_MUL (cosa, radius_x);
+ py = center_y + CFX_MUL (sina, radius_y);
+
+ if (a == angle_1 && move_first)
+ cogl_path_move_to (px, py);
+ else
+ cogl_path_line_to (px, py);
+ }
+}
+
+/**
+ * cogl_path_arc:
+ * @center_x: X coordinate of the elliptical arc center
+ * @center_y: Y coordinate of the elliptical arc center
+ * @radius_x: X radius of the elliptical arc
+ * @radius_y: Y radious of the elliptical arc
+ * @angle_1: Angle in the unit-circle at which the arc begin
+ * @angle_2: Angle in the unit-circle at which the arc ends
+ * @angle_step: Angle increment resolution for subdivision
+ *
+ * Adds an elliptical arc segment to the current path. A straight line
+ * segment will link the current pen location with the first vertex
+ * of the arc.
+ **/
+void
+cogl_path_arc (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_1,
+ ClutterAngle angle_2,
+ ClutterAngle angle_step)
+{
+ _cogl_arc (center_x, center_y,
+ radius_x, radius_y,
+ angle_1, angle_2,
+ angle_step, 0 /* no move */);
+}
+
+/**
+ * cogl_path_arc_rel:
+ * @center_x: X offset from the current pen location of the elliptical
+ * arc center
+ * @center_y: Y offset from the current pen location of the elliptical
+ * arc center
+ * @radius_x: X radius of the elliptical arc
+ * @radius_y: Y radious of the elliptical arc
+ * @angle_1: Angle in the unit-circle at which the arc begin
+ * @angle_2: Angle in the unit-circle at which the arc ends
+ * @angle_step: Angle increment resolution for subdivision
+ *
+ * Adds an elliptical arc segment to the current path. A straight line
+ * segment will link the current pen location with the first vertex
+ * of the arc.
+ **/
+void
+cogl_path_arc_rel (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_1,
+ ClutterAngle angle_2,
+ ClutterAngle angle_step)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ _cogl_arc (ctx->path_pen.x + center_x,
+ ctx->path_pen.y + center_y,
+ radius_x, radius_y,
+ angle_1, angle_2,
+ angle_step, 0 /* no move */);
+}
+
+/**
+ * cogl_arc:
+ * @center_x: X coordinate of the elliptical arc center
+ * @center_y: Y coordinate of the elliptical arc center
+ * @radius_x: X radius of the elliptical arc
+ * @radius_y: Y radious of the elliptical arc
+ * @angle_1: Angle in the unit-circle at which the arc begin
+ * @angle_2: Angle in the unit-circle at which the arc ends
+ * @angle_step: Angle increment resolution for subdivision
+ *
+ * Clears the previously constructed shape and constructs and elliptical arc
+ * shape.
+ **/
+void
+cogl_arc (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_1,
+ ClutterAngle angle_2,
+ ClutterAngle angle_step)
+{
+ _cogl_arc (center_x, center_y,
+ radius_x, radius_y,
+ angle_1, angle_2,
+ angle_step, 1 /* move first */);
+}
+
+/**
+ * cogl_ellipse:
+ * @center_x: X coordinate of the ellipse center
+ * @center_y: Y coordinate of the ellipse center
+ * @radius_x: X radius of the ellipse
+ * @radius_y: Y radius of the ellipse
+ * @angle_step: Angle increment resolution for subdivision
+ *
+ * Clears the previously constructed shape and constructs an ellipse
+ * shape.
+ **/
+void
+cogl_ellipse (ClutterFixed center_x,
+ ClutterFixed center_y,
+ ClutterFixed radius_x,
+ ClutterFixed radius_y,
+ ClutterAngle angle_step)
+{
+
+ /* FIXME: if shows to be slow might be optimized
+ * by mirroring just a quarter of it */
+
+ _cogl_arc (center_x, center_y,
+ radius_x, radius_y,
+ 0, CLUTTER_ANGLE_FROM_DEG(360),
+ angle_step, 1 /* move first */);
+
+ cogl_path_close();
+}
+
+/**
+ * cogl_round_rectangle:
+ * @x: X coordinate of the top-left corner
+ * @y: Y coordinate of the top-left corner
+ * @width: Width of the rectangle
+ * @height: Height of the rectangle
+ * @radius: Radius of the corner arcs.
+ * @arc_step: Angle increment resolution for subdivision of
+ * the corner arcs.
+ *
+ * Clears the previously constructed shape and constructs a rectangular
+ * shape with rounded corners.
+ **/
+void
+cogl_round_rectangle (ClutterFixed x,
+ ClutterFixed y,
+ ClutterFixed width,
+ ClutterFixed height,
+ ClutterFixed radius,
+ ClutterAngle arc_step)
+{
+ ClutterFixed inner_width = width - (radius << 1);
+ ClutterFixed inner_height = height - (radius << 1);
+
+ cogl_path_move_to (x, y + radius);
+ cogl_path_arc_rel (radius, 0,
+ radius, radius,
+ CLUTTER_ANGLE_FROM_DEG (180),
+ CLUTTER_ANGLE_FROM_DEG (270),
+ arc_step);
+
+ cogl_path_h_line_to_rel (inner_width);
+ cogl_path_arc_rel (0, radius,
+ radius, radius,
+ CLUTTER_ANGLE_FROM_DEG (-90),
+ CLUTTER_ANGLE_FROM_DEG (0),
+ arc_step);
+
+ cogl_path_v_line_to_rel (inner_height);
+ cogl_path_arc_rel (-radius, 0,
+ radius, radius,
+ CLUTTER_ANGLE_FROM_DEG (0),
+ CLUTTER_ANGLE_FROM_DEG (90),
+ arc_step);
+
+ cogl_path_h_line_to_rel (-inner_width);
+ cogl_path_arc_rel (0, -radius,
+ radius, radius,
+ CLUTTER_ANGLE_FROM_DEG (90),
+ CLUTTER_ANGLE_FROM_DEG (180),
+ arc_step);
+
+ cogl_path_close ();
+}
+
+static void
+_cogl_path_bezier2_sub (CoglBezQuad *quad)
+{
+ CoglBezQuad quads[_COGL_MAX_BEZ_RECURSE_DEPTH];
+ CoglBezQuad *qleft;
+ CoglBezQuad *qright;
+ CoglBezQuad *q;
+ CoglFixedVec2 mid;
+ CoglFixedVec2 dif;
+ CoglFixedVec2 c1;
+ CoglFixedVec2 c2;
+ CoglFixedVec2 c3;
+ gint qindex;
+
+ /* Put first curve on stack */
+ quads[0] = *quad;
+ qindex = 0;
+
+ /* While stack is not empty */
+ while (qindex >= 0)
+ {
+
+ q = &quads[qindex];
+
+ /* Calculate distance of control point from its
+ * counterpart on the line between end points */
+ mid.x = ((q->p1.x + q->p3.x) >> 1);
+ mid.y = ((q->p1.y + q->p3.y) >> 1);
+ dif.x = (q->p2.x - mid.x);
+ dif.y = (q->p2.y - mid.y);
+ if (dif.x < 0) dif.x = -dif.x;
+ if (dif.y < 0) dif.y = -dif.y;
+
+ /* Cancel if the curve is flat enough */
+ if (dif.x + dif.y <= CFX_ONE
+ || qindex == _COGL_MAX_BEZ_RECURSE_DEPTH - 1)
+ {
+ /* Add subdivision point (skip last) */
+ if (qindex == 0) return;
+ _cogl_path_add_node (q->p3.x, q->p3.y);
+ --qindex; continue;
+ }
+
+ /* Left recursion goes on top of stack! */
+ qright = q; qleft = &quads[++qindex];
+
+ /* Subdivide into 2 sub-curves */
+ c1.x = ((q->p1.x + q->p2.x) >> 1);
+ c1.y = ((q->p1.y + q->p2.y) >> 1);
+ c3.x = ((q->p2.x + q->p3.x) >> 1);
+ c3.y = ((q->p2.y + q->p3.y) >> 1);
+ c2.x = ((c1.x + c3.x) >> 1);
+ c2.y = ((c1.y + c3.y) >> 1);
+
+ /* Add left recursion onto stack */
+ qleft->p1 = q->p1;
+ qleft->p2 = c1;
+ qleft->p3 = c2;
+
+ /* Add right recursion onto stack */
+ qright->p1 = c2;
+ qright->p2 = c3;
+ qright->p3 = q->p3;
+ }
+}
+
+static void
+_cogl_path_bezier3_sub (CoglBezCubic *cubic)
+{
+ CoglBezCubic cubics[_COGL_MAX_BEZ_RECURSE_DEPTH];
+ CoglBezCubic *cleft;
+ CoglBezCubic *cright;
+ CoglBezCubic *c;
+ CoglFixedVec2 dif1;
+ CoglFixedVec2 dif2;
+ CoglFixedVec2 mm;
+ CoglFixedVec2 c1;
+ CoglFixedVec2 c2;
+ CoglFixedVec2 c3;
+ CoglFixedVec2 c4;
+ CoglFixedVec2 c5;
+ gint cindex;
+
+ /* Put first curve on stack */
+ cubics[0] = *cubic;
+ cindex = 0;
+
+ while (cindex >= 0)
+ {
+ c = &cubics[cindex];
+
+#define CFX_MUL2(x) ((x) << 1)
+#define CFX_MUL3(x) (((x) << 1) + (x))
+#define CFX_SQ(x) CFX_MUL (x, x)
+
+ /* Calculate distance of control points from their
+ * counterparts on the line between end points */
+ dif1.x = CFX_MUL3 (c->p2.x) - CFX_MUL2 (c->p1.x) - c->p4.x;
+ dif1.y = CFX_MUL3 (c->p2.y) - CFX_MUL2 (c->p1.y) - c->p4.y;
+ dif2.x = CFX_MUL3 (c->p3.x) - CFX_MUL2 (c->p4.x) - c->p1.x;
+ dif2.y = CFX_MUL3 (c->p3.y) - CFX_MUL2 (c->p4.y) - c->p1.y;
+ if (dif1.x < 0) dif1.x = -dif1.x;
+ if (dif1.y < 0) dif1.y = -dif1.y;
+ if (dif2.x < 0) dif2.x = -dif2.x;
+ if (dif2.y < 0) dif2.y = -dif2.y;
+
+#undef CFX_MUL2
+#undef CFX_MUL3
+#undef CFX_SQ
+
+ /* Pick the greatest of two distances */
+ if (dif1.x < dif2.x) dif1.x = dif2.x;
+ if (dif1.y < dif2.y) dif1.y = dif2.y;
+
+ /* Cancel if the curve is flat enough */
+ if (dif1.x + dif1.y <= CFX_ONE
+ || cindex == _COGL_MAX_BEZ_RECURSE_DEPTH-1)
+ {
+ /* Add subdivision point (skip last) */
+ if (cindex == 0) return;
+ _cogl_path_add_node (c->p4.x, c->p4.y);
+ --cindex; continue;
+ }
+
+ /* Left recursion goes on top of stack! */
+ cright = c; cleft = &cubics[++cindex];
+
+ /* Subdivide into 2 sub-curves */
+ c1.x = ((c->p1.x + c->p2.x) >> 1);
+ c1.y = ((c->p1.y + c->p2.y) >> 1);
+ mm.x = ((c->p2.x + c->p3.x) >> 1);
+ mm.y = ((c->p2.y + c->p3.y) >> 1);
+ c5.x = ((c->p3.x + c->p4.x) >> 1);
+ c5.y = ((c->p3.y + c->p4.y) >> 1);
+
+ c2.x = ((c1.x + mm.x) >> 1);
+ c2.y = ((c1.y + mm.y) >> 1);
+ c4.x = ((mm.x + c5.x) >> 1);
+ c4.y = ((mm.y + c5.y) >> 1);
+
+ c3.x = ((c2.x + c4.x) >> 1);
+ c3.y = ((c2.y + c4.y) >> 1);
+
+ /* Add left recursion to stack */
+ cleft->p1 = c->p1;
+ cleft->p2 = c1;
+ cleft->p3 = c2;
+ cleft->p4 = c3;
+
+ /* Add right recursion to stack */
+ cright->p1 = c3;
+ cright->p2 = c4;
+ cright->p3 = c5;
+ cright->p4 = c->p4;
+ }
+}
+
+/**
+ * cogl_path_bezier2_to:
+ * @x1: X coordinate of the second bezier control point
+ * @y1: Y coordinate of the second bezier control point
+ * @x2: X coordinate of the third bezier control point
+ * @y2: Y coordinate of the third bezier control point
+ *
+ * Adds a quadratic bezier curve segment to the current path with the given
+ * second and third control points and using current pen location as the
+ * first control point.
+ **/
+void
+cogl_path_bezier2_to (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ CoglBezQuad quad;
+
+ /* Prepare quadratic curve */
+ quad.p1 = ctx->path_pen;
+ quad.p2.x = x1;
+ quad.p2.y = y1;
+ quad.p3.x = x2;
+ quad.p3.y = y2;
+
+ /* Run subdivision */
+ _cogl_path_bezier2_sub (&quad);
+
+ /* Add last point */
+ _cogl_path_add_node (quad.p3.x, quad.p3.y);
+ ctx->path_pen = quad.p3;
+}
+
+/**
+ * cogl_path_bezier3_to:
+ * @x1: X coordinate of the second bezier control point
+ * @y1: Y coordinate of the second bezier control point
+ * @x2: X coordinate of the third bezier control point
+ * @y2: Y coordinate of the third bezier control point
+ * @x3: X coordinate of the fourth bezier control point
+ * @y3: Y coordinate of the fourth bezier control point
+ *
+ * Adds a cubic bezier curve segment to the current path with the given
+ * second, third and fourth control points and using current pen location
+ * as the first control point.
+ **/
+void
+cogl_path_bezier3_to (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed x3,
+ ClutterFixed y3)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ CoglBezCubic cubic;
+
+ /* Prepare cubic curve */
+ cubic.p1 = ctx->path_pen;
+ cubic.p2.x = x1;
+ cubic.p2.y = y1;
+ cubic.p3.x = x2;
+ cubic.p3.y = y2;
+ cubic.p4.x = x3;
+ cubic.p4.y = y3;
+
+ /* Run subdivision */
+ _cogl_path_bezier3_sub (&cubic);
+
+ /* Add last point */
+ _cogl_path_add_node (cubic.p4.x, cubic.p4.y);
+ ctx->path_pen = cubic.p4;
+}
+
+/**
+ * cogl_path_bezier2_to_rel:
+ * @x1: X coordinate of the second bezier control point
+ * @y1: Y coordinate of the second bezier control point
+ * @x2: X coordinate of the third bezier control point
+ * @y2: Y coordinate of the third bezier control point
+ *
+ * Adds a quadratic bezier curve segment to the current path with the given
+ * second and third control points and using current pen location as the
+ * first control point. The given coordinates are relative to the current
+ * pen location.
+ **/
+void
+cogl_path_bezier2_to_rel (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_bezier2_to (ctx->path_pen.x + x1,
+ ctx->path_pen.y + y2,
+ ctx->path_pen.x + x2,
+ ctx->path_pen.y + y2);
+}
+
+/**
+ * cogl_path_bezier3_to_rel:
+ * @x1: X coordinate of the second bezier control point
+ * @y1: Y coordinate of the second bezier control point
+ * @x2: X coordinate of the third bezier control point
+ * @y2: Y coordinate of the third bezier control point
+ * @x3: X coordinate of the fourth bezier control point
+ * @y3: Y coordinate of the fourth bezier control point
+ *
+ * Adds a cubic bezier curve segment to the current path with the given
+ * second, third and fourth control points and using current pen location
+ * as the first control point. The given coordinates are relative to the
+ * current pen location.
+ **/
+void
+cogl_path_bezier3_to_rel (ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed x3,
+ ClutterFixed y3)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ cogl_path_bezier3_to (ctx->path_pen.x + x1,
+ ctx->path_pen.y + y2,
+ ctx->path_pen.x + x2,
+ ctx->path_pen.y + y2,
+ ctx->path_pen.x + x3,
+ ctx->path_pen.y + y3);
+}
diff --git a/gles/cogl-primitives.h b/gles/cogl-primitives.h
new file mode 100644
index 00000000..4c96f409
--- /dev/null
+++ b/gles/cogl-primitives.h
@@ -0,0 +1,54 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_PRIMITIVES_H
+#define __COGL_PRIMITIVES_H
+
+typedef struct _CoglFixedVec2 CoglFixedVec2;
+typedef struct _CoglBezQuad CoglBezQuad;
+typedef struct _CoglBezCubic CoglBezCubic;
+
+struct _CoglFixedVec2
+{
+ ClutterFixed x;
+ ClutterFixed y;
+};
+
+struct _CoglBezQuad
+{
+ CoglFixedVec2 p1;
+ CoglFixedVec2 p2;
+ CoglFixedVec2 p3;
+};
+
+struct _CoglBezCubic
+{
+ CoglFixedVec2 p1;
+ CoglFixedVec2 p2;
+ CoglFixedVec2 p3;
+ CoglFixedVec2 p4;
+};
+
+#endif /* __COGL_PRIMITIVES_H */
diff --git a/gles/cogl-texture.c b/gles/cogl-texture.c
new file mode 100644
index 00000000..2eae2c9b
--- /dev/null
+++ b/gles/cogl-texture.c
@@ -0,0 +1,2185 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-util.h"
+#include "cogl-bitmap.h"
+#include "cogl-texture.h"
+#include "cogl-context.h"
+
+#include <string.h>
+#include <stdlib.h>
+/*
+#define COGL_DEBUG 1
+
+#define GE(x) \
+{ \
+ glGetError(); x; \
+ GLuint err = glGetError(); \
+ if (err != 0) \
+ printf("err: 0x%x\n", err); \
+} */
+
+struct _CoglSpanIter
+{
+ gint index;
+ GArray *array;
+ CoglTexSliceSpan *span;
+ ClutterFixed pos;
+ ClutterFixed next_pos;
+ ClutterFixed origin;
+ ClutterFixed cover_start;
+ ClutterFixed cover_end;
+ ClutterFixed intersect_start;
+ ClutterFixed intersect_end;
+ ClutterFixed intersect_start_local;
+ ClutterFixed intersect_end_local;
+ gboolean intersects;
+};
+
+/*
+ * _cogl_texture_handle_find:
+ * @handle: A texture handle
+ *
+ * Returns the index of the given CoglHandle if found in the
+ * handle array.
+ */
+static gint
+_cogl_texture_handle_find (CoglHandle handle)
+{
+ _COGL_GET_CONTEXT (ctx, -1);
+
+ gint i;
+
+ if (ctx->texture_handles == NULL)
+ return -1;
+
+ for (i=0; i < ctx->texture_handles->len; ++i)
+ if (g_array_index (ctx->texture_handles, CoglHandle, i) == handle)
+ return i;
+
+ return -1;
+}
+
+/*
+ * _cogl_texture_handle_new:
+ * @tex: A pointer to an allocated CoglTexture structure
+ *
+ * Returns a new CoglHandle for the given CoglTexture
+ * object.
+ */
+static CoglHandle
+_cogl_texture_handle_new (CoglTexture *tex)
+{
+ _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
+
+ CoglHandle handle = (CoglHandle)tex;
+
+ if (ctx->texture_handles == NULL)
+ ctx->texture_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle));
+
+ g_array_append_val (ctx->texture_handles, handle);
+
+ return handle;
+}
+
+/*
+ * _cogl_texture_handle_release:
+ * @handle: A valid CoglHandle
+ *
+ * Frees the given CoglHandle for use with another object.
+ */
+static void
+_cogl_texture_handle_release (CoglHandle handle)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ gint i;
+
+ if ( (i = _cogl_texture_handle_find (handle)) == -1)
+ return;
+
+ g_array_remove_index_fast (ctx->texture_handles, i);
+}
+
+/*
+ * _cogl_texture_pointer_from_handle:
+ * @handle: A valid CoglHandle
+ *
+ * Returns a pointer to the texture object referenced by
+ * given handle.
+ */
+CoglTexture *
+_cogl_texture_pointer_from_handle (CoglHandle handle)
+{
+ return (CoglTexture*) handle;
+}
+
+CoglHandle
+_cogl_texture_handle_from_pointer (CoglTexture *tex)
+{
+ return (CoglHandle) tex;
+}
+
+gboolean
+cogl_is_texture (CoglHandle handle)
+{
+ if (handle == COGL_INVALID_HANDLE)
+ return FALSE;
+
+ return _cogl_texture_handle_find (handle) >= 0;
+}
+
+static void
+_cogl_texture_bitmap_free (CoglTexture *tex)
+{
+ if (tex->bitmap.data != NULL && tex->bitmap_owner)
+ g_free (tex->bitmap.data);
+
+ tex->bitmap.data = NULL;
+ tex->bitmap_owner = FALSE;
+}
+
+static void
+_cogl_texture_bitmap_swap (CoglTexture *tex,
+ CoglBitmap *new_bitmap)
+{
+ if (tex->bitmap.data != NULL && tex->bitmap_owner)
+ g_free (tex->bitmap.data);
+
+ tex->bitmap = *new_bitmap;
+ tex->bitmap_owner = TRUE;
+}
+
+static void
+_cogl_span_iter_update (CoglSpanIter *iter)
+{
+ /* Pick current span */
+ iter->span = &g_array_index (iter->array,
+ CoglTexSliceSpan,
+ iter->index);
+
+ /* Offset next position by span size */
+ iter->next_pos = iter->pos +
+ CLUTTER_INT_TO_FIXED (iter->span->size - iter->span->waste);
+
+ /* Check if span intersects the area to cover */
+ if (iter->next_pos <= iter->cover_start ||
+ iter->pos >= iter->cover_end)
+ {
+ /* Intersection undefined */
+ iter->intersects = FALSE;
+ return;
+ }
+
+ iter->intersects = TRUE;
+
+ /* Clip start position to coverage area */
+ if (iter->pos < iter->cover_start)
+ iter->intersect_start = iter->cover_start;
+ else
+ iter->intersect_start = iter->pos;
+
+ /* Clip end position to coverage area */
+ if (iter->next_pos > iter->cover_end)
+ iter->intersect_end = iter->cover_end;
+ else
+ iter->intersect_end = iter->next_pos;
+}
+
+static void
+_cogl_span_iter_begin (CoglSpanIter *iter,
+ GArray *array,
+ ClutterFixed origin,
+ ClutterFixed cover_start,
+ ClutterFixed cover_end)
+{
+ /* Copy info */
+ iter->index = 0;
+ iter->array = array;
+ iter->span = NULL;
+ iter->origin = origin;
+ iter->cover_start = cover_start;
+ iter->cover_end = cover_end;
+ iter->pos = iter->origin;
+
+ /* Update intersection */
+ _cogl_span_iter_update (iter);
+}
+
+void
+_cogl_span_iter_next (CoglSpanIter *iter)
+{
+ /* Move current position */
+ iter->pos = iter->next_pos;
+
+ /* Pick next slice (wrap when last reached) */
+ iter->index = (iter->index + 1) % iter->array->len;
+
+ /* Update intersection */
+ _cogl_span_iter_update (iter);
+}
+
+static gboolean
+_cogl_span_iter_end (CoglSpanIter *iter)
+{
+ /* End reached when whole area covered */
+ return iter->pos >= iter->cover_end;
+}
+
+static gboolean
+_cogl_texture_upload_to_gl (CoglTexture *tex)
+{
+ CoglTexSliceSpan *x_span;
+ CoglTexSliceSpan *y_span;
+ GLuint gl_handle;
+ gint bpp;
+ gint x,y;
+ CoglBitmap slice_bmp;
+
+ /* FIXME: might optimize by not copying to intermediate slice
+ bitmap when source rowstride = bpp * width and the texture
+ image is not sliced */
+
+ bpp = _cogl_get_format_bpp (tex->bitmap.format);
+
+ /* Iterate vertical slices */
+ for (y = 0; y < tex->slice_y_spans->len; ++y)
+ {
+ y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y);
+
+ /* Iterate horizontal slices */
+ for (x = 0; x < tex->slice_x_spans->len; ++x)
+ {
+ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x);
+
+ /* Pick the gl texture object handle */
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint,
+ y * tex->slice_x_spans->len + x);
+
+ /* Setup temp bitmap for slice subregion */
+ slice_bmp.format = tex->bitmap.format;
+ slice_bmp.width = x_span->size - x_span->waste;
+ slice_bmp.height = y_span->size - y_span->waste;
+ slice_bmp.rowstride = bpp * slice_bmp.width;
+ slice_bmp.data = (guchar*) g_malloc (slice_bmp.rowstride *
+ slice_bmp.height);
+
+ /* Copy subregion data */
+ _cogl_bitmap_copy_subregion (&tex->bitmap,
+ &slice_bmp,
+ x_span->start,
+ y_span->start,
+ 0, 0,
+ slice_bmp.width,
+ slice_bmp.height);
+
+ /* Upload new image data */
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+ GE( glPixelStorei (GL_UNPACK_ALIGNMENT, 1) );
+
+ GE( glTexSubImage2D (tex->gl_target, 0, 0, 0,
+ slice_bmp.width,
+ slice_bmp.height,
+ tex->gl_format, tex->gl_type,
+ slice_bmp.data) );
+
+ /* Free temp bitmap */
+ g_free (slice_bmp.data);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_cogl_texture_download_from_gl (CoglTexture *tex,
+ CoglBitmap *target_bmp,
+ GLuint target_gl_format,
+ GLuint target_gl_type)
+{
+ gint bpp;
+ GLint viewport[4];
+ CoglHandle handle;
+ ClutterColor cback = {0x0, 0x0, 0x0, 0x0};
+ ClutterColor cwhite = {0xFF, 0xFF, 0xFF, 0xFF};
+ CoglBitmap rect_bmp;
+ ClutterFixed rx1, ry1;
+ ClutterFixed rx2, ry2;
+ ClutterFixed tx1, ty1;
+ ClutterFixed tx2, ty2;
+ int bw, bh;
+
+ handle = _cogl_texture_handle_from_pointer (tex);
+ bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888);
+
+ /* Viewport needs to have some size and be inside the window for this */
+ GE( glGetIntegerv (GL_VIEWPORT, viewport) );
+
+ if (viewport[0] < 0 || viewport[1] < 0 ||
+ viewport[2] <= 0 || viewport[3] <= 0)
+ return FALSE;
+
+ /* Setup orthographic projection into current viewport
+ (0,0 in bottom-left corner to draw the texture
+ upside-down so we match the way glReadPixels works) */
+
+ GE( glMatrixMode (GL_PROJECTION) );
+ GE( glPushMatrix () );
+ GE( glLoadIdentity () );
+
+ GE( glOrthox (0, CLUTTER_INT_TO_FIXED (viewport[2]),
+ 0, CLUTTER_INT_TO_FIXED (viewport[3]),
+ CLUTTER_INT_TO_FIXED (0),
+ CLUTTER_INT_TO_FIXED (100)) );
+
+ GE( glMatrixMode (GL_MODELVIEW) );
+ GE( glPushMatrix () );
+ GE( glLoadIdentity () );
+
+ /* Draw to all channels */
+ cogl_draw_buffer (COGL_WINDOW_BUFFER | COGL_MASK_BUFFER, 0);
+
+
+ /* If whole image fits into the viewport and target buffer
+ has got no special rowstride, we can do it in one pass */
+ if (tex->bitmap.width < viewport[2] - viewport[0] &&
+ tex->bitmap.height < viewport[3] - viewport[1] &&
+ tex->bitmap.rowstride == bpp * tex->bitmap.width)
+ {
+ /* Clear buffer with transparent black, draw with white
+ for direct copy to framebuffer */
+ cogl_paint_init (&cback);
+ cogl_color (&cwhite);
+
+ /* Draw the texture image */
+ cogl_texture_rectangle (handle,
+ 0, 0,
+ CLUTTER_INT_TO_FIXED (tex->bitmap.width),
+ CLUTTER_INT_TO_FIXED (tex->bitmap.height),
+ 0, 0, CFX_ONE, CFX_ONE);
+
+ /* Read into target bitmap */
+ GE( glPixelStorei (GL_PACK_ALIGNMENT, 1) );
+ GE( glReadPixels (viewport[0], viewport[1],
+ tex->bitmap.width,
+ tex->bitmap.height,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ target_bmp->data) );
+ }
+ else
+ {
+ ry1 = 0; ry2 = 0;
+ ty1 = 0; ty2 = 0;
+
+#define CFIX CLUTTER_INT_TO_FIXED
+
+ /* Walk Y axis until whole bitmap height consumed */
+ for (bh = tex->bitmap.height; bh > 0; bh -= viewport[3])
+ {
+ /* Rectangle Y coords */
+ ry1 = ry2;
+ ry2 += (bh < viewport[3]) ? bh : viewport[3];
+
+ /* Normalized texture Y coords */
+ ty1 = ty2;
+ ty2 = CFX_QDIV (CFIX (ry2), CFIX (tex->bitmap.height));
+
+ rx1 = 0; rx2 = 0;
+ tx1 = 0; tx2 = 0;
+
+ /* Walk X axis until whole bitmap width consumed */
+ for (bw = tex->bitmap.width; bw > 0; bw-=viewport[2])
+ {
+ /* Rectangle X coords */
+ rx1 = rx2;
+ rx2 += (bw < viewport[2]) ? bw : viewport[2];
+
+ /* Normalized texture X coords */
+ tx1 = tx2;
+ tx2 = CFX_QDIV (CFIX (rx2), CFIX (tex->bitmap.width));
+
+ /* Clear buffer with transparent black, draw with white
+ for direct copy to framebuffer */
+ cogl_paint_init (&cback);
+ cogl_color (&cwhite);
+
+ /* Draw a portion of texture */
+ cogl_texture_rectangle (handle,
+ 0, 0,
+ CFIX (rx2 - rx1),
+ CFIX (ry2 - ry1),
+ tx1, ty1,
+ tx2, ty2);
+
+ /* Read into a temporary bitmap */
+ rect_bmp.format = COGL_PIXEL_FORMAT_RGBA_8888;
+ rect_bmp.width = rx2 - rx1;
+ rect_bmp.height = ry2 - ry1;
+ rect_bmp.rowstride = bpp * rect_bmp.width;
+ rect_bmp.data = (guchar*) g_malloc (rect_bmp.rowstride *
+ rect_bmp.height);
+
+ GE( glPixelStorei (GL_PACK_ALIGNMENT, 1) );
+ GE( glReadPixels (viewport[0], viewport[1],
+ rect_bmp.width,
+ rect_bmp.height,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ rect_bmp.data) );
+
+ /* Copy to target bitmap */
+ _cogl_bitmap_copy_subregion (&rect_bmp,
+ target_bmp,
+ 0,0,
+ rx1,ry1,
+ rect_bmp.width,
+ rect_bmp.height);
+
+ /* Free temp bitmap */
+ g_free (rect_bmp.data);
+ }
+ }
+
+#undef CFIX
+ }
+
+ glMatrixMode (GL_PROJECTION);
+ glPopMatrix ();
+ glMatrixMode (GL_MODELVIEW);
+ glPopMatrix ();
+
+ cogl_draw_buffer (COGL_WINDOW_BUFFER, 0);
+
+ return TRUE;
+}
+
+static void
+_cogl_texture_set_alignment_for_rowstride (gint rowstride)
+{
+ gint alignment = 1;
+
+ while ((rowstride & 1) == 0 && alignment < 8)
+ {
+ alignment <<= 1;
+ rowstride >>= 1;
+ }
+
+ GE( glPixelStorei (GL_UNPACK_ALIGNMENT, alignment) );
+}
+
+static gboolean
+_cogl_texture_upload_subregion_to_gl (CoglTexture *tex,
+ gint src_x,
+ gint src_y,
+ gint dst_x,
+ gint dst_y,
+ gint width,
+ gint height,
+ CoglBitmap *source_bmp,
+ GLuint source_gl_format,
+ GLuint source_gl_type)
+{
+ gint bpp;
+ CoglSpanIter x_iter;
+ CoglSpanIter y_iter;
+ GLuint gl_handle;
+ gint source_x = 0, source_y = 0;
+ gint inter_w = 0, inter_h = 0;
+ gint local_x = 0, local_y = 0;
+ CoglBitmap slice_bmp;
+
+ bpp = _cogl_get_format_bpp (source_bmp->format);
+
+ /* FIXME: might optimize by not copying to intermediate slice
+ bitmap when source rowstride = bpp * width and the texture
+ image is not sliced */
+
+ /* Iterate vertical spans */
+ for (source_y = src_y,
+ _cogl_span_iter_begin (&y_iter, tex->slice_y_spans,
+ 0, CLUTTER_INT_TO_FIXED (dst_y),
+ CLUTTER_INT_TO_FIXED (dst_y + height));
+
+ !_cogl_span_iter_end (&y_iter);
+
+ _cogl_span_iter_next (&y_iter),
+ source_y += inter_h )
+ {
+ /* Skip non-intersecting ones */
+ if (!y_iter.intersects)
+ {
+ inter_h = 0;
+ continue;
+ }
+
+ /* Iterate horizontal spans */
+ for (source_x = src_x,
+ _cogl_span_iter_begin (&x_iter, tex->slice_x_spans,
+ 0, CLUTTER_INT_TO_FIXED (dst_x),
+ CLUTTER_INT_TO_FIXED (dst_x + width));
+
+ !_cogl_span_iter_end (&x_iter);
+
+ _cogl_span_iter_next (&x_iter),
+ source_x += inter_w )
+ {
+ /* Skip non-intersecting ones */
+ if (!x_iter.intersects)
+ {
+ inter_w = 0;
+ continue;
+ }
+
+ /* Pick intersection width and height */
+ inter_w = CLUTTER_FIXED_TO_INT (x_iter.intersect_end -
+ x_iter.intersect_start);
+ inter_h = CLUTTER_FIXED_TO_INT (y_iter.intersect_end -
+ y_iter.intersect_start);
+
+ /* Localize intersection top-left corner to slice*/
+ local_x = CLUTTER_FIXED_TO_INT (x_iter.intersect_start -
+ x_iter.pos);
+ local_y = CLUTTER_FIXED_TO_INT (y_iter.intersect_start -
+ y_iter.pos);
+
+ /* Pick slice GL handle */
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint,
+ y_iter.index * tex->slice_x_spans->len +
+ x_iter.index);
+
+ /* Setup temp bitmap for slice subregion */
+ slice_bmp.format = tex->bitmap.format;
+ slice_bmp.width = inter_w;
+ slice_bmp.height = inter_h;
+ slice_bmp.rowstride = bpp * slice_bmp.width;
+ slice_bmp.data = (guchar*) malloc (slice_bmp.rowstride *
+ slice_bmp.height);
+
+ /* Copy subregion data */
+ _cogl_bitmap_copy_subregion (source_bmp,
+ &slice_bmp,
+ source_x,
+ source_y,
+ 0, 0,
+ slice_bmp.width,
+ slice_bmp.height);
+
+ /* Upload new image data */
+ GE( glPixelStorei (GL_UNPACK_ALIGNMENT, 1) );
+
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+ GE( glTexSubImage2D (tex->gl_target, 0,
+ local_x, local_y,
+ inter_w, inter_h,
+ source_gl_format,
+ source_gl_type,
+ slice_bmp.data) );
+
+ /* Free temp bitmap */
+ g_free (slice_bmp.data);
+ }
+ }
+
+ return TRUE;
+}
+
+static gint
+_cogl_pot_slices_for_size (gint size_to_fill,
+ gint max_span_size,
+ gint max_waste,
+ GArray *out_spans)
+{
+ gint n_spans = 0;
+ CoglTexSliceSpan span;
+
+ /* Init first slice span */
+ span.start = 0;
+ span.size = max_span_size;
+ span.waste = 0;
+
+ /* Fix invalid max_waste */
+ if (max_waste < 0) max_waste = 0;
+
+ while (TRUE)
+ {
+ /* Is the whole area covered? */
+ if (size_to_fill > span.size)
+ {
+ /* Not yet - add a span of this size */
+ if (out_spans) g_array_append_val (out_spans, span);
+ span.start += span.size;
+ size_to_fill -= span.size;
+ n_spans++;
+ }
+ else if (span.size - size_to_fill <= max_waste)
+ {
+ /* Yes and waste is small enough */
+ span.waste = span.size - size_to_fill;
+ if (out_spans) g_array_append_val (out_spans, span);
+ return ++n_spans;
+ }
+ else
+ {
+ /* Yes but waste is too large */
+ while (span.size - size_to_fill > max_waste)
+ {
+ span.size /= 2;
+ g_assert (span.size > 0);
+ }
+ }
+ }
+
+ /* Can't get here */
+ return 0;
+}
+
+static gboolean
+_cogl_texture_size_supported (GLenum gl_target,
+ GLenum gl_format,
+ GLenum gl_type,
+ int width,
+ int height)
+{
+ return TRUE;
+}
+
+static gboolean
+_cogl_texture_slices_create (CoglTexture *tex)
+{
+ gint bpp;
+ gint max_width;
+ gint max_height;
+ GLuint *gl_handles;
+ gint n_x_slices;
+ gint n_y_slices;
+ gint n_slices;
+ gint x, y;
+ CoglTexSliceSpan *x_span;
+ CoglTexSliceSpan *y_span;
+
+ gint (*slices_for_size) (gint, gint, gint, GArray*);
+
+ bpp = _cogl_get_format_bpp (tex->bitmap.format);
+
+ /* Initialize size of largest slice according to supported features */
+ max_width = cogl_util_next_p2 (tex->bitmap.width);
+ max_height = cogl_util_next_p2 (tex->bitmap.height);
+ tex->gl_target = GL_TEXTURE_2D;
+ slices_for_size = _cogl_pot_slices_for_size;
+
+ /* Negative number means no slicing forced by the user */
+ if (tex->max_waste <= -1)
+ {
+ CoglTexSliceSpan span;
+
+ /* Check if size supported else bail out */
+ if (!_cogl_texture_size_supported (tex->gl_target,
+ tex->gl_format,
+ tex->gl_type,
+ max_width,
+ max_height))
+ {
+ return FALSE;
+ }
+
+ n_x_slices = 1;
+ n_y_slices = 1;
+
+ /* Init span arrays */
+ tex->slice_x_spans = g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan),
+ 1);
+
+ tex->slice_y_spans = g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan),
+ 1);
+
+ /* Add a single span for width and height */
+ span.start = 0;
+ span.size = max_width;
+ span.waste = max_width - tex->bitmap.width;
+ g_array_append_val (tex->slice_x_spans, span);
+
+ span.size = max_height;
+ span.waste = max_height - tex->bitmap.height;
+ g_array_append_val (tex->slice_y_spans, span);
+ }
+ else
+ {
+ /* Decrease the size of largest slice until supported by GL */
+ while (!_cogl_texture_size_supported (tex->gl_target,
+ tex->gl_format,
+ tex->gl_type,
+ max_width,
+ max_height))
+ {
+ /* Alternate between width and height */
+ if (max_width > max_height)
+ max_width /= 2;
+ else
+ max_height /= 2;
+
+ if (max_width == 0 || max_height == 0)
+ return FALSE;
+ }
+
+ /* Determine the slices required to cover the bitmap area */
+ n_x_slices = slices_for_size (tex->bitmap.width,
+ max_width, tex->max_waste,
+ NULL);
+
+ n_y_slices = slices_for_size (tex->bitmap.height,
+ max_height, tex->max_waste,
+ NULL);
+
+ /* Init span arrays with reserved size */
+ tex->slice_x_spans = g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan),
+ n_x_slices);
+
+ tex->slice_y_spans = g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan),
+ n_y_slices);
+
+ /* Fill span arrays with info */
+ slices_for_size (tex->bitmap.width,
+ max_width, tex->max_waste,
+ tex->slice_x_spans);
+
+ slices_for_size (tex->bitmap.height,
+ max_height, tex->max_waste,
+ tex->slice_y_spans);
+ }
+
+ /* Init and resize GL handle array */
+ n_slices = n_x_slices * n_y_slices;
+
+ tex->slice_gl_handles = g_array_sized_new (FALSE, FALSE,
+ sizeof (GLuint),
+ n_slices);
+
+ g_array_set_size (tex->slice_gl_handles, n_slices);
+
+ /* Generate a "working set" of GL texture objects
+ * (some implementations might supported faster
+ * re-binding between textures inside a set) */
+ gl_handles = (GLuint*) tex->slice_gl_handles->data;
+
+ GE( glGenTextures (n_slices, gl_handles) );
+
+
+ /* Init each GL texture object */
+ for (y = 0; y < n_y_slices; ++y)
+ {
+ y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y);
+
+ for (x = 0; x < n_x_slices; ++x)
+ {
+ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x);
+
+#if COGL_DEBUG
+ printf ("CREATE SLICE (%d,%d)\n", x,y);
+ printf ("size: (%d x %d)\n",
+ x_span->size - x_span->waste,
+ y_span->size - y_span->waste);
+#endif
+ /* Setup texture parameters */
+ GE( glBindTexture (tex->gl_target, gl_handles[y * n_x_slices + x]) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) );
+
+ /* Pass NULL data to init size and internal format */
+ GE( glTexImage2D (tex->gl_target, 0, tex->gl_intformat,
+ x_span->size, y_span->size, 0,
+ tex->gl_format, tex->gl_type, 0) );
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+_cogl_texture_slices_free (CoglTexture *tex)
+{
+ if (tex->slice_x_spans != NULL)
+ g_array_free (tex->slice_x_spans, TRUE);
+
+ if (tex->slice_y_spans != NULL)
+ g_array_free (tex->slice_y_spans, TRUE);
+
+ if (tex->slice_gl_handles != NULL)
+ {
+ if (tex->is_foreign == FALSE)
+ {
+ GE( glDeleteTextures (tex->slice_gl_handles->len,
+ (GLuint*) tex->slice_gl_handles->data) );
+ }
+
+ g_array_free (tex->slice_gl_handles, TRUE);
+ }
+}
+
+static gboolean
+_cogl_pixel_format_from_gl_internal (GLenum gl_int_format,
+ CoglPixelFormat *out_format)
+{
+ /* It doesn't really matter we convert to exact same
+ format (some have no cogl match anyway) since format
+ is re-matched against cogl when getting or setting
+ texture image data.
+ */
+
+ switch (gl_int_format)
+ {
+ case GL_ALPHA:
+
+ *out_format = COGL_PIXEL_FORMAT_A_8;
+ return TRUE;
+
+ case GL_LUMINANCE:
+
+ *out_format = COGL_PIXEL_FORMAT_G_8;
+ return TRUE;
+
+ case GL_RGB:
+
+ *out_format = COGL_PIXEL_FORMAT_RGB_888;
+ return TRUE;
+
+ case GL_RGBA:
+
+ *out_format = COGL_PIXEL_FORMAT_RGBA_8888;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static CoglPixelFormat
+_cogl_pixel_format_to_gl (CoglPixelFormat format,
+ GLenum *out_glintformat,
+ GLenum *out_glformat,
+ GLenum *out_gltype)
+{
+ CoglPixelFormat required_format = format;
+ GLenum glintformat = 0;
+ GLenum glformat = 0;
+ GLenum gltype = 0;
+
+ /* No premultiplied formats accepted by GL
+ * (FIXME: latest hardware?) */
+
+ if (format & COGL_PREMULT_BIT)
+ format = (format & COGL_UNPREMULT_MASK);
+
+ /* Find GL equivalents */
+ switch (format)
+ {
+ case COGL_PIXEL_FORMAT_A_8:
+ glintformat = GL_ALPHA;
+ glformat = GL_ALPHA;
+ gltype = GL_UNSIGNED_BYTE;
+ break;
+ case COGL_PIXEL_FORMAT_G_8:
+ glintformat = GL_LUMINANCE;
+ glformat = GL_LUMINANCE;
+ gltype = GL_UNSIGNED_BYTE;
+ break;
+
+ /* Just one 24-bit ordering supported */
+ case COGL_PIXEL_FORMAT_RGB_888:
+ case COGL_PIXEL_FORMAT_BGR_888:
+ glintformat = GL_RGB;
+ glformat = GL_RGB;
+ gltype = GL_UNSIGNED_BYTE;
+ required_format = COGL_PIXEL_FORMAT_RGB_888;
+ break;
+
+ /* Just one 32-bit ordering supported */
+ case COGL_PIXEL_FORMAT_RGBA_8888:
+ case COGL_PIXEL_FORMAT_BGRA_8888:
+ case COGL_PIXEL_FORMAT_ARGB_8888:
+ case COGL_PIXEL_FORMAT_ABGR_8888:
+ glintformat = GL_RGBA;
+ glformat = GL_RGBA;
+ gltype = GL_UNSIGNED_BYTE;
+ required_format = COGL_PIXEL_FORMAT_RGBA_8888;
+ break;
+
+ /* The following three types of channel ordering
+ * are always defined using system word byte
+ * ordering (even according to GLES spec) */
+ case COGL_PIXEL_FORMAT_RGB_565:
+ glintformat = GL_RGB;
+ glformat = GL_RGB;
+ gltype = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case COGL_PIXEL_FORMAT_RGBA_4444:
+ glintformat = GL_RGBA;
+ glformat = GL_RGBA;
+ gltype = GL_UNSIGNED_SHORT_4_4_4_4;
+ break;
+ case COGL_PIXEL_FORMAT_RGBA_5551:
+ glintformat = GL_RGBA;
+ glformat = GL_RGBA;
+ gltype = GL_UNSIGNED_SHORT_5_5_5_1;
+ break;
+
+ /* FIXME: check extensions for YUV support */
+ default:
+ break;
+ }
+
+ if (out_glintformat != NULL)
+ *out_glintformat = glintformat;
+ if (out_glformat != NULL)
+ *out_glformat = glformat;
+ if (out_gltype != NULL)
+ *out_gltype = gltype;
+
+ return required_format;
+}
+
+static gboolean
+_cogl_texture_bitmap_prepare (CoglTexture *tex,
+ CoglPixelFormat internal_format)
+{
+ CoglBitmap new_bitmap;
+ CoglPixelFormat new_data_format;
+ gboolean success;
+
+ /* Was there any internal conversion requested? */
+ if (internal_format == COGL_PIXEL_FORMAT_ANY)
+ internal_format = tex->bitmap.format;
+
+ /* Find closest format accepted by GL */
+ new_data_format = _cogl_pixel_format_to_gl (internal_format,
+ &tex->gl_intformat,
+ &tex->gl_format,
+ &tex->gl_type);
+
+ /* Convert to internal format */
+ if (new_data_format != tex->bitmap.format)
+ {
+ success = _cogl_bitmap_convert_and_premult (&tex->bitmap,
+ &new_bitmap,
+ new_data_format);
+
+ if (!success)
+ return FALSE;
+
+ /* Update texture with new data */
+ _cogl_texture_bitmap_swap (tex, &new_bitmap);
+ }
+
+ return TRUE;
+}
+
+static void
+_cogl_texture_free (CoglTexture *tex)
+{
+ /* Frees texture resources but its handle is not
+ released! Do that separately before this! */
+ _cogl_texture_bitmap_free (tex);
+ _cogl_texture_slices_free (tex);
+ g_free (tex);
+}
+
+CoglHandle
+cogl_texture_new_with_size (guint width,
+ guint height,
+ gint max_waste,
+ CoglPixelFormat internal_format)
+{
+ CoglTexture *tex;
+ gint bpp;
+ gint rowstride;
+
+ /* Since no data, we need some internal format */
+ if (internal_format == COGL_PIXEL_FORMAT_ANY)
+ return COGL_INVALID_HANDLE;
+
+ /* Rowstride from width */
+ bpp = _cogl_get_format_bpp (internal_format);
+ rowstride = width * bpp;
+
+ /* Init texture with empty bitmap */
+ tex = (CoglTexture*) g_malloc (sizeof (CoglTexture));
+
+ tex->ref_count = 1;
+
+ tex->is_foreign = FALSE;
+
+ tex->bitmap.width = width;
+ tex->bitmap.height = height;
+ tex->bitmap.format = internal_format;
+ tex->bitmap.rowstride = rowstride;
+ tex->bitmap.data = NULL;
+ tex->bitmap_owner = FALSE;
+
+ tex->slice_x_spans = NULL;
+ tex->slice_y_spans = NULL;
+ tex->slice_gl_handles = NULL;
+
+ tex->max_waste = max_waste;
+ tex->min_filter = CGL_NEAREST;
+ tex->mag_filter = CGL_NEAREST;
+
+ /* Find closest GL format match */
+ tex->bitmap.format =
+ _cogl_pixel_format_to_gl (internal_format,
+ &tex->gl_intformat,
+ &tex->gl_format,
+ &tex->gl_type);
+
+ if (!_cogl_texture_slices_create (tex))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ return _cogl_texture_handle_new (tex);
+}
+
+CoglHandle
+cogl_texture_new_from_data (guint width,
+ guint height,
+ gint max_waste,
+ CoglPixelFormat format,
+ CoglPixelFormat internal_format,
+ guint rowstride,
+ const guchar *data)
+{
+ CoglTexture *tex;
+ gint bpp;
+
+ if (format == COGL_PIXEL_FORMAT_ANY)
+ return COGL_INVALID_HANDLE;
+
+ if (data == NULL)
+ return COGL_INVALID_HANDLE;
+
+ /* Rowstride from width if not given */
+ bpp = _cogl_get_format_bpp (format);
+ if (rowstride == 0) rowstride = width * bpp;
+
+ /* Create new texture and fill with given data */
+ tex = (CoglTexture*) g_malloc (sizeof (CoglTexture));
+
+ tex->ref_count = 1;
+
+ tex->is_foreign = FALSE;
+
+ tex->bitmap.width = width;
+ tex->bitmap.height = height;
+ tex->bitmap.data = (guchar*)data;
+ tex->bitmap.format = format;
+ tex->bitmap.rowstride = rowstride;
+ tex->bitmap_owner = FALSE;
+
+ tex->slice_x_spans = NULL;
+ tex->slice_y_spans = NULL;
+ tex->slice_gl_handles = NULL;
+
+ tex->max_waste = max_waste;
+ tex->min_filter = CGL_NEAREST;
+ tex->mag_filter = CGL_NEAREST;
+
+ /* FIXME: If upload fails we should set some kind of
+ * error flag but still return texture handle (this
+ * is to keep the behavior equal to _new_from_file;
+ * see below) */
+
+ if (!_cogl_texture_bitmap_prepare (tex, internal_format))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ if (!_cogl_texture_slices_create (tex))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ if (!_cogl_texture_upload_to_gl (tex))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ _cogl_texture_bitmap_free (tex);
+
+ return _cogl_texture_handle_new (tex);;
+}
+
+CoglHandle
+cogl_texture_new_from_file (const gchar *filename,
+ gint max_waste,
+ CoglPixelFormat internal_format,
+ GError **error)
+{
+ CoglBitmap bmp;
+ CoglTexture *tex;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE);
+
+ /* Try loading with imaging backend */
+ if (!_cogl_bitmap_from_file (&bmp, filename, error))
+ {
+ /* Try fallback */
+ if (!_cogl_bitmap_fallback_from_file (&bmp, filename))
+ return COGL_INVALID_HANDLE;
+ else if (error && *error)
+ {
+ g_error_free (*error);
+ *error = NULL;
+ }
+ }
+
+ /* Create new texture and fill with loaded data */
+ tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture));
+
+ tex->ref_count = 1;
+
+ tex->is_foreign = FALSE;
+
+ tex->bitmap = bmp;
+ tex->bitmap_owner = TRUE;
+
+ tex->slice_x_spans = NULL;
+ tex->slice_y_spans = NULL;
+ tex->slice_gl_handles = NULL;
+
+ tex->max_waste = max_waste;
+ tex->min_filter = CGL_NEAREST;
+ tex->mag_filter = CGL_NEAREST;
+
+ /* FIXME: If upload fails we should set some kind of
+ * error flag but still return texture handle if the
+ * user decides to destroy another texture and upload
+ * this one instead (reloading from file is not needed
+ * in that case). As a rule then, everytime a valid
+ * CoglHandle is returned, it should also be destroyed
+ * with cogl_texture_unref at some point! */
+
+ if (!_cogl_texture_bitmap_prepare (tex, internal_format))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ if (!_cogl_texture_slices_create (tex))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ if (!_cogl_texture_upload_to_gl (tex))
+ {
+ _cogl_texture_free (tex);
+ return COGL_INVALID_HANDLE;
+ }
+
+ _cogl_texture_bitmap_free (tex);
+
+ return _cogl_texture_handle_new (tex);
+}
+
+CoglHandle
+cogl_texture_new_from_foreign (GLuint gl_handle,
+ GLenum gl_target,
+ GLuint width,
+ GLuint height,
+ GLuint x_pot_waste,
+ GLuint y_pot_waste,
+ CoglPixelFormat format)
+{
+ GLenum gl_error = 0;
+ GLboolean gl_istexture;
+ GLint gl_min_filter;
+ GLint gl_mag_filter;
+ guint bpp;
+ CoglTexture *tex;
+ CoglTexSliceSpan x_span;
+ CoglTexSliceSpan y_span;
+
+ /* Allow 2-dimensional textures only */
+ if (gl_target != GL_TEXTURE_2D)
+ return COGL_INVALID_HANDLE;
+
+ /* Make sure it is a valid GL texture object */
+ gl_istexture = GE( glIsTexture (gl_handle) );
+ if (gl_istexture == GL_FALSE)
+ return COGL_INVALID_HANDLE;
+
+ /* Make sure binding succeeds */
+ gl_error = glGetError ();
+ glBindTexture (gl_target, gl_handle);
+ if (glGetError () != GL_NO_ERROR)
+ return COGL_INVALID_HANDLE;
+
+ /* Obtain texture parameters
+ (only level 0 we are interested in) */
+
+ /* These are not queriable in GLES :(
+ GE( glGetTexLevelParameteriv (gl_target, 0,
+ GL_TEXTURE_COMPRESSED,
+ &gl_compressed) );
+
+ GE( glGetTexLevelParameteriv (gl_target, 0,
+ GL_TEXTURE_INTERNAL_FORMAT,
+ &gl_int_format) );
+ */
+ GE( glGetTexParameteriv (gl_target,
+ GL_TEXTURE_MIN_FILTER,
+ &gl_min_filter));
+
+ GE( glGetTexParameteriv (gl_target,
+ GL_TEXTURE_MAG_FILTER,
+ &gl_mag_filter));
+
+ /* Validate width and height */
+ if (width <= 0 || height <= 0)
+ return COGL_INVALID_HANDLE;
+
+ /* Validate pot waste */
+ if (x_pot_waste < 0 || x_pot_waste >= width ||
+ y_pot_waste < 0 || y_pot_waste >= height)
+ return COGL_INVALID_HANDLE;
+
+ /* Create new texture */
+ tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture));
+
+ tex->ref_count = 1;
+#if COGL_DEBUG
+ printf ("COGL TEX new %p %i\n", tex, tex->ref_count);
+#endif
+
+ /* Setup bitmap info */
+ tex->is_foreign = TRUE;
+
+ bpp = _cogl_get_format_bpp (format);
+ tex->bitmap.format = format;
+ tex->bitmap.width = width;
+ tex->bitmap.height = height;
+ tex->bitmap.rowstride = tex->bitmap.width * bpp;
+ tex->bitmap_owner = FALSE;
+
+ tex->gl_target = gl_target;
+ tex->gl_intformat = 0;
+ tex->gl_format = 0;
+ tex->gl_type = 0;
+
+ tex->min_filter = gl_min_filter;
+ tex->mag_filter = gl_mag_filter;
+ tex->max_waste = 0;
+
+ /* Create slice arrays */
+ tex->slice_x_spans =
+ g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan), 1);
+
+ tex->slice_y_spans =
+ g_array_sized_new (FALSE, FALSE,
+ sizeof (CoglTexSliceSpan), 1);
+
+ tex->slice_gl_handles =
+ g_array_sized_new (FALSE, FALSE,
+ sizeof (GLuint), 1);
+
+ /* Store info for a single slice */
+ x_span.start = 0;
+ x_span.size = width + x_pot_waste;
+ x_span.waste = x_pot_waste;
+ g_array_append_val (tex->slice_x_spans, x_span);
+
+ y_span.start = 0;
+ y_span.size = height + x_pot_waste;
+ y_span.waste = y_pot_waste;
+ g_array_append_val (tex->slice_y_spans, y_span);
+
+ g_array_append_val (tex->slice_gl_handles, gl_handle);
+
+ /* Replace mipmap min filter modes with single level ones */
+ if (gl_min_filter != GL_NEAREST && gl_min_filter != GL_LINEAR)
+ {
+ if (gl_min_filter == GL_NEAREST_MIPMAP_NEAREST)
+ {
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST) );
+ tex->min_filter = CGL_NEAREST;
+ }
+ else
+ {
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR) );
+ tex->min_filter = CGL_LINEAR;
+ }
+ }
+
+ /* Force appropriate wrap parameter */
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) );
+
+ return _cogl_texture_handle_new (tex);
+}
+
+CoglHandle
+cogl_texture_ref (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return COGL_INVALID_HANDLE;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ tex->ref_count++;
+
+ return handle;
+}
+
+void
+cogl_texture_unref (CoglHandle handle)
+{
+ /* Check if valid texture handle */
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ if (--tex->ref_count < 1)
+ {
+ /* Free texture handle and resources */
+ _cogl_texture_handle_release (tex);
+ _cogl_texture_free (tex);
+ }
+}
+
+void
+cogl_texture_get_properties (CoglHandle handle,
+ guint *out_width,
+ guint *out_height,
+ CoglPixelFormat *out_format,
+ guint *out_rowstride,
+ guint *out_max_waste)
+{
+ CoglTexture *tex;
+
+ /* Check if valid texture handle */
+ if (!cogl_is_texture (handle))
+ return;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* Output requested properties */
+ if (out_width != NULL)
+ *out_width = tex->bitmap.width;
+
+ if (out_height != NULL)
+ *out_height = tex->bitmap.height;
+
+ if (out_format != NULL)
+ *out_format = tex->bitmap.format;
+
+ if (out_rowstride != NULL)
+ *out_rowstride = tex->bitmap.rowstride;
+
+ if (out_max_waste != NULL)
+ *out_max_waste = tex->max_waste;
+}
+
+guint
+cogl_texture_get_width (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->bitmap.width;
+}
+
+guint
+cogl_texture_get_height (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->bitmap.height;
+}
+
+CoglPixelFormat
+cogl_texture_get_format (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return COGL_PIXEL_FORMAT_ANY;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->bitmap.format;
+}
+
+guint
+cogl_texture_get_rowstride (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->bitmap.rowstride;
+}
+
+gint
+cogl_texture_get_max_waste (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->max_waste;
+}
+
+gboolean
+cogl_texture_is_sliced (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return FALSE;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ if (tex->slice_gl_handles == NULL)
+ return FALSE;
+
+ if (tex->slice_gl_handles->len <= 1)
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+cogl_texture_get_gl_texture (CoglHandle handle,
+ GLuint *out_gl_handle,
+ GLenum *out_gl_target)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return FALSE;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ if (tex->slice_gl_handles == NULL)
+ return FALSE;
+
+ if (tex->slice_gl_handles->len < 1)
+ return FALSE;
+
+ if (out_gl_handle != NULL)
+ *out_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0);
+
+ if (out_gl_target != NULL)
+ *out_gl_target = tex->gl_target;
+
+ return TRUE;
+}
+
+COGLenum
+cogl_texture_get_min_filter (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->min_filter;
+}
+
+COGLenum
+cogl_texture_get_mag_filter (CoglHandle handle)
+{
+ CoglTexture *tex;
+
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ return tex->mag_filter;
+}
+
+void
+cogl_texture_set_filters (CoglHandle handle,
+ COGLenum min_filter,
+ COGLenum mag_filter)
+{
+ CoglTexture *tex;
+ GLuint gl_handle;
+ int i;
+
+ if (!cogl_is_texture (handle))
+ return;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* Store new values */
+ tex->min_filter = min_filter;
+ tex->mag_filter = mag_filter;
+
+ /* Make sure slices were created */
+ if (tex->slice_gl_handles == NULL)
+ return;
+
+ /* Apply new filters to every slice */
+ for (i=0; i<tex->slice_gl_handles->len; ++i)
+ {
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, i);
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) );
+ GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) );
+ }
+}
+
+gboolean
+cogl_texture_set_region (CoglHandle handle,
+ gint src_x,
+ gint src_y,
+ gint dst_x,
+ gint dst_y,
+ guint dst_width,
+ guint dst_height,
+ gint width,
+ gint height,
+ CoglPixelFormat format,
+ guint rowstride,
+ const guchar *data)
+{
+ CoglTexture *tex;
+ gint bpp;
+ CoglBitmap source_bmp;
+ CoglBitmap temp_bmp;
+ gboolean source_bmp_owner = FALSE;
+ CoglPixelFormat closest_format;
+ GLenum closest_gl_format;
+ GLenum closest_gl_type;
+ gboolean success;
+
+ /* Check if valid texture handle */
+ if (!cogl_is_texture (handle))
+ return FALSE;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* Check for valid format */
+ if (format == COGL_PIXEL_FORMAT_ANY)
+ return FALSE;
+
+ /* Init source bitmap */
+ source_bmp.width = width;
+ source_bmp.height = height;
+ source_bmp.format = format;
+ source_bmp.data = (guchar*)data;
+
+ /* Rowstride from width if none specified */
+ bpp = _cogl_get_format_bpp (format);
+ source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride;
+
+ /* Find closest format to internal that's supported by GL */
+ closest_format = _cogl_pixel_format_to_gl (tex->bitmap.format,
+ NULL, /* don't need */
+ &closest_gl_format,
+ &closest_gl_type);
+
+ /* If no direct match, convert */
+ if (closest_format != format)
+ {
+ /* Convert to required format */
+ success = _cogl_bitmap_convert_and_premult (&source_bmp,
+ &temp_bmp,
+ closest_format);
+
+ /* Swap bitmaps if succeeded */
+ if (!success) return FALSE;
+ source_bmp = temp_bmp;
+ source_bmp_owner = TRUE;
+ }
+
+ /* Send data to GL */
+ _cogl_texture_upload_subregion_to_gl (tex,
+ src_x, src_y,
+ dst_x, dst_y,
+ dst_width, dst_height,
+ &source_bmp,
+ closest_gl_format,
+ closest_gl_type);
+
+ /* Free data if owner */
+ if (source_bmp_owner)
+ g_free (source_bmp.data);
+
+ return TRUE;
+}
+
+gint
+cogl_texture_get_data (CoglHandle handle,
+ CoglPixelFormat format,
+ guint rowstride,
+ guchar *data)
+{
+ CoglTexture *tex;
+ gint bpp;
+ gint byte_size;
+ CoglPixelFormat closest_format;
+ gint closest_bpp;
+ GLenum closest_gl_format;
+ GLenum closest_gl_type;
+ CoglBitmap target_bmp;
+ CoglBitmap new_bmp;
+ gboolean success;
+ guchar *src;
+ guchar *dst;
+ gint y;
+
+ /* Check if valid texture handle */
+ if (!cogl_is_texture (handle))
+ return 0;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* Default to internal format if none specified */
+ if (format == COGL_PIXEL_FORMAT_ANY)
+ format = tex->bitmap.format;
+
+ /* Rowstride from texture width if none specified */
+ bpp = _cogl_get_format_bpp (format);
+ if (rowstride == 0) rowstride = tex->bitmap.width * bpp;
+
+ /* Return byte size if only that requested */
+ byte_size = tex->bitmap.height * rowstride;
+ if (data == NULL) return byte_size;
+
+ /* Find closest format that's supported by GL
+ (Can't use _cogl_pixel_format_to_gl since available formats
+ when reading pixels on GLES are severely limited) */
+ closest_format = COGL_PIXEL_FORMAT_RGBA_8888;
+ closest_gl_format = GL_RGBA;
+ closest_gl_type = GL_UNSIGNED_BYTE;
+ closest_bpp = _cogl_get_format_bpp (closest_format);
+
+ /* Is the requested format supported? */
+ if (closest_format == format)
+ {
+ /* Target user data directly */
+ target_bmp = tex->bitmap;
+ target_bmp.format = format;
+ target_bmp.rowstride = rowstride;
+ target_bmp.data = data;
+ }
+ else
+ {
+ /* Target intermediate buffer */
+ target_bmp = tex->bitmap;
+ target_bmp.format = closest_format;
+ target_bmp.rowstride = target_bmp.width * closest_bpp;
+ target_bmp.data = (guchar*) g_malloc (target_bmp.height
+ * target_bmp.rowstride);
+ }
+
+ /* Retrieve data from slices */
+ _cogl_texture_download_from_gl (tex, &target_bmp,
+ closest_gl_format,
+ closest_gl_type);
+
+ /* Was intermediate used? */
+ if (closest_format != format)
+ {
+ /* Convert to requested format */
+ success = _cogl_bitmap_convert_and_premult (&target_bmp,
+ &new_bmp,
+ format);
+
+ /* Free intermediate data and return if failed */
+ g_free (target_bmp.data);
+ if (!success) return 0;
+
+ /* Copy to user buffer */
+ for (y = 0; y < new_bmp.height; ++y)
+ {
+ src = new_bmp.data + y * new_bmp.rowstride;
+ dst = data + y * rowstride;
+ memcpy (dst, src, new_bmp.width);
+ }
+
+ /* Free converted data */
+ g_free (new_bmp.data);
+ }
+
+ return byte_size;
+}
+
+static void
+_cogl_texture_quad_sw (CoglTexture *tex,
+ ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed tx1,
+ ClutterFixed ty1,
+ ClutterFixed tx2,
+ ClutterFixed ty2)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ CoglSpanIter iter_x , iter_y;
+ ClutterFixed tw , th;
+ ClutterFixed tqx , tqy;
+ ClutterFixed first_tx , first_ty;
+ ClutterFixed first_qx , first_qy;
+ ClutterFixed slice_tx1 , slice_ty1;
+ ClutterFixed slice_tx2 , slice_ty2;
+ ClutterFixed slice_qx1 , slice_qy1;
+ ClutterFixed slice_qx2 , slice_qy2;
+ GLfixed tex_coords[8];
+ GLfixed quad_coords[8];
+ GLuint gl_handle;
+
+#if COGL_DEBUG
+ printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n");
+#endif
+
+ /* Prepare GL state */
+ gulong enable_flags = (COGL_ENABLE_TEXTURE_2D
+ | COGL_ENABLE_VERTEX_ARRAY
+ | COGL_ENABLE_TEXCOORD_ARRAY);
+
+ if (ctx->color_alpha < 255
+ || tex->bitmap.format & COGL_A_BIT)
+ {
+ enable_flags |= COGL_ENABLE_BLEND;
+ }
+
+ cogl_enable (enable_flags);
+
+ GE( glTexCoordPointer (2, GL_FIXED, 0, tex_coords) );
+ GE( glVertexPointer (2, GL_FIXED, 0, quad_coords) );
+
+ /* Scale ratio from texture to quad widths */
+ tw = CLUTTER_INT_TO_FIXED (tex->bitmap.width);
+ th = CLUTTER_INT_TO_FIXED (tex->bitmap.height);
+
+ tqx = CFX_QDIV (x2-x1, CFX_QMUL (tw, (tx2 - tx1)));
+ tqy = CFX_QDIV (y2-y1, CFX_QMUL (th, (ty2 - ty1)));
+
+ /* Integral texture coordinate for first tile */
+ first_tx = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (tx1));
+ first_ty = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (ty1));
+
+ /* Denormalize texture coordinates */
+ first_tx = CFX_QMUL (first_tx, tw);
+ first_ty = CFX_QMUL (first_ty, th);
+ tx1 = CFX_QMUL (tx1, tw);
+ ty1 = CFX_QMUL (ty1, th);
+ tx2 = CFX_QMUL (tx2, tw);
+ ty2 = CFX_QMUL (ty2, th);
+
+ /* Quad coordinate of the first tile */
+ first_qx = x1 - CFX_QMUL (tx1 - first_tx, tqx);
+ first_qy = y1 - CFX_QMUL (ty1 - first_ty, tqy);
+
+
+ /* Iterate until whole quad height covered */
+ for (_cogl_span_iter_begin (&iter_y, tex->slice_y_spans,
+ first_ty, ty1, ty2) ;
+ !_cogl_span_iter_end (&iter_y) ;
+ _cogl_span_iter_next (&iter_y) )
+ {
+ /* Discard slices out of quad early */
+ if (!iter_y.intersects) continue;
+
+ /* Span-quad intersection in quad coordinates */
+ slice_qy1 = first_qy +
+ CFX_QMUL (iter_y.intersect_start - first_ty, tqy);
+
+ slice_qy2 = first_qy +
+ CFX_QMUL (iter_y.intersect_end - first_ty, tqy);
+
+ /* Localize slice texture coordinates */
+ slice_ty1 = iter_y.intersect_start - iter_y.pos;
+ slice_ty2 = iter_y.intersect_end - iter_y.pos;
+
+ /* Normalize texture coordinates to current slice */
+ slice_ty1 /= iter_y.span->size;
+ slice_ty2 /= iter_y.span->size;
+
+
+ /* Iterate until whole quad width covered */
+ for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans,
+ first_tx, tx1, tx2) ;
+ !_cogl_span_iter_end (&iter_x) ;
+ _cogl_span_iter_next (&iter_x) )
+ {
+ /* Discard slices out of quad early */
+ if (!iter_x.intersects) continue;
+
+ /* Span-quad intersection in quad coordinates */
+ slice_qx1 = first_qx +
+ CFX_QMUL (iter_x.intersect_start - first_tx, tqx);
+
+ slice_qx2 = first_qx +
+ CFX_QMUL (iter_x.intersect_end - first_tx, tqx);
+
+ /* Localize slice texture coordinates */
+ slice_tx1 = iter_x.intersect_start - iter_x.pos;
+ slice_tx2 = iter_x.intersect_end - iter_x.pos;
+
+ /* Normalize texture coordinates to current slice */
+ slice_tx1 /= iter_x.span->size;
+ slice_tx2 /= iter_x.span->size;
+
+#if COGL_DEBUG
+ printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index);
+ printf("qx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx1));
+ printf("qy1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy1));
+ printf("qx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx2));
+ printf("qy2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy2));
+ printf("tx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx1));
+ printf("ty1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty1));
+ printf("tx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx2));
+ printf("ty2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty2));
+#endif
+
+ /* Pick and bind opengl texture object */
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint,
+ iter_y.index * iter_x.array->len +
+ iter_x.index);
+
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+ /* Draw textured quad */
+ tex_coords[0] = slice_tx1; tex_coords[1] = slice_ty1;
+ tex_coords[2] = slice_tx2; tex_coords[3] = slice_ty1;
+ tex_coords[4] = slice_tx1; tex_coords[5] = slice_ty2;
+ tex_coords[6] = slice_tx2; tex_coords[7] = slice_ty2;
+
+ quad_coords[0] = slice_qx1; quad_coords[1] = slice_qy1;
+ quad_coords[2] = slice_qx2; quad_coords[3] = slice_qy1;
+ quad_coords[4] = slice_qx1; quad_coords[5] = slice_qy2;
+ quad_coords[6] = slice_qx2; quad_coords[7] = slice_qy2;
+
+ GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) );
+ }
+ }
+}
+
+static void
+_cogl_texture_quad_hw (CoglTexture *tex,
+ ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed tx1,
+ ClutterFixed ty1,
+ ClutterFixed tx2,
+ ClutterFixed ty2)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ GLfixed tex_coords[8];
+ GLfixed quad_coords[8];
+ GLuint gl_handle;
+ CoglTexSliceSpan *x_span;
+ CoglTexSliceSpan *y_span;
+
+#if COGL_DEBUG
+ printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n");
+#endif
+
+ /* Prepare GL state */
+ gulong enable_flags = (COGL_ENABLE_TEXTURE_2D
+ | COGL_ENABLE_VERTEX_ARRAY
+ | COGL_ENABLE_TEXCOORD_ARRAY);
+
+ if (ctx->color_alpha < 255
+ || tex->bitmap.format & COGL_A_BIT)
+ {
+ enable_flags |= COGL_ENABLE_BLEND;
+ }
+
+ cogl_enable (enable_flags);
+
+ GE( glTexCoordPointer (2, GL_FIXED, 0, tex_coords) );
+ GE( glVertexPointer (2, GL_FIXED, 0, quad_coords) );
+
+ /* Pick and bind opengl texture object */
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0);
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+ /* Don't include the waste in the texture coordinates */
+ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0);
+ y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0);
+ tx1 = tx1 * (x_span->size - x_span->waste) / x_span->size;
+ tx2 = tx2 * (x_span->size - x_span->waste) / x_span->size;
+ ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size;
+ ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size;
+
+ /* Draw textured quad */
+ tex_coords[0] = tx1; tex_coords[1] = ty1;
+ tex_coords[2] = tx2; tex_coords[3] = ty1;
+ tex_coords[4] = tx1; tex_coords[5] = ty2;
+ tex_coords[6] = tx2; tex_coords[7] = ty2;
+
+ quad_coords[0] = x1; quad_coords[1] = y1;
+ quad_coords[2] = x2; quad_coords[3] = y1;
+ quad_coords[4] = x1; quad_coords[5] = y2;
+ quad_coords[6] = x2; quad_coords[7] = y2;
+
+ GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) );
+}
+
+void
+cogl_texture_rectangle (CoglHandle handle,
+ ClutterFixed x1,
+ ClutterFixed y1,
+ ClutterFixed x2,
+ ClutterFixed y2,
+ ClutterFixed tx1,
+ ClutterFixed ty1,
+ ClutterFixed tx2,
+ ClutterFixed ty2)
+{
+ CoglTexture *tex;
+ ClutterFixed tempx;
+
+ /* Check if valid texture */
+ if (!cogl_is_texture (handle))
+ return;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* Make sure we got stuff to draw */
+ if (tex->slice_gl_handles == NULL)
+ return;
+
+ if (tex->slice_gl_handles->len == 0)
+ return;
+
+ if (tx1 == tx2 || ty1 == ty2)
+ return;
+
+ /* Fix quad coord ordering */
+ if (x1 > x2)
+ {
+ tempx = x1;
+ x1 = x2;
+ x2 = tempx;
+ }
+
+ if (y1 > y2)
+ {
+ tempx = y1;
+ y1 = y2;
+ y2 = tempx;
+ }
+
+ /* Fix texture coord ordering */
+ if (tx1 > tx2)
+ {
+ tempx = tx1;
+ tx1 = tx2;
+ tx2 = tempx;
+ }
+
+ if (ty1 > ty2)
+ {
+ tempx = ty1;
+ ty1 = ty2;
+ ty2 = tempx;
+ }
+
+ /* Tile textured quads */
+ if (tex->slice_gl_handles->len == 1
+ && tx1 >= -CFX_ONE && tx2 <= CFX_ONE
+ && ty1 >= -CFX_ONE && ty2 <= CFX_ONE)
+ {
+ _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2);
+ }
+ else
+ {
+ _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2);
+ }
+}
+
+void
+cogl_texture_polygon (CoglHandle handle,
+ guint n_vertices,
+ CoglTextureVertex *vertices,
+ gboolean use_color)
+{
+ CoglTexture *tex;
+ GLuint gl_handle;
+ gulong enable_flags;
+ int i;
+ CoglTextureGLVertex *p;
+ CoglTexSliceSpan *x_span;
+ CoglTexSliceSpan *y_span;
+
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ /* Check if valid texture */
+ if (!cogl_is_texture (handle))
+ return;
+
+ tex = _cogl_texture_pointer_from_handle (handle);
+
+ /* GL ES has no GL_CLAMP_TO_BORDER wrap mode so the method used to
+ render sliced textures in the GL backend will not work. Therefore
+ cogl_texture_polygon is only supported if the texture is not
+ sliced */
+ if (tex->slice_gl_handles->len != 1)
+ {
+ static gboolean shown_warning = FALSE;
+
+ if (!shown_warning)
+ {
+ g_warning ("cogl_texture_polygon does not work for "
+ "sliced textures on GL ES");
+ shown_warning = TRUE;
+ }
+ return;
+ }
+
+ /* Make sure there is enough space in the global texture vertex
+ array. This is used so we can render the polygon with a single
+ call to OpenGL but still support any number of vertices */
+ if (ctx->texture_vertices_size < n_vertices)
+ {
+ guint nsize = ctx->texture_vertices_size;
+
+ if (nsize == 0)
+ nsize = 1;
+ do
+ nsize *= 2;
+ while (nsize < n_vertices);
+
+ ctx->texture_vertices_size = nsize;
+
+ if (ctx->texture_vertices)
+ ctx->texture_vertices = g_realloc (ctx->texture_vertices,
+ nsize
+ * sizeof (CoglTextureGLVertex));
+ else
+ ctx->texture_vertices = g_malloc (nsize
+ * sizeof (CoglTextureGLVertex));
+ }
+
+ /* Prepare GL state */
+ enable_flags = (COGL_ENABLE_TEXTURE_2D
+ | COGL_ENABLE_VERTEX_ARRAY
+ | COGL_ENABLE_TEXCOORD_ARRAY);
+
+ if ((tex->bitmap.format & COGL_A_BIT))
+ enable_flags |= COGL_ENABLE_BLEND;
+ else if (use_color)
+ {
+ for (i = 0; i < n_vertices; i++)
+ if (vertices[i].color.alpha < 255)
+ {
+ enable_flags |= COGL_ENABLE_BLEND;
+ break;
+ }
+ }
+ else if (ctx->color_alpha < 255)
+ enable_flags |= COGL_ENABLE_BLEND;
+
+ if (use_color)
+ {
+ enable_flags |= COGL_ENABLE_COLOR_ARRAY;
+ GE( glColorPointer (4, GL_FIXED, sizeof (CoglTextureGLVertex),
+ ctx->texture_vertices[0].c) );
+ }
+
+ GE( glVertexPointer (3, GL_FIXED, sizeof (CoglTextureGLVertex),
+ ctx->texture_vertices[0].v) );
+ GE( glTexCoordPointer (2, GL_FIXED, sizeof (CoglTextureGLVertex),
+ ctx->texture_vertices[0].t) );
+
+ cogl_enable (enable_flags);
+
+ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0);
+ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0);
+ y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0);
+
+ /* Convert the vertices into an array of GLfixeds ready to pass to
+ OpenGL */
+ for (i = 0, p = ctx->texture_vertices; i < n_vertices; i++, p++)
+ {
+ p->v[0] = vertices[i].x;
+ p->v[1] = vertices[i].y;
+ p->v[2] = vertices[i].z;
+ p->t[0] = vertices[i].tx * (x_span->size - x_span->waste) / x_span->size;
+ p->t[1] = vertices[i].ty * (y_span->size - y_span->waste) / y_span->size;
+ p->c[0] = (vertices[i].color.red << 16) / 0xff;
+ p->c[1] = (vertices[i].color.green << 16) / 0xff;
+ p->c[2] = (vertices[i].color.blue << 16) / 0xff;
+ p->c[3] = (vertices[i].color.alpha << 16) / 0xff;
+ }
+
+ GE( glBindTexture (tex->gl_target, gl_handle) );
+
+ GE( glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices) );
+
+ /* Set the last color so that the cache of the alpha value will work
+ properly */
+ if (use_color && n_vertices > 0)
+ cogl_color (&vertices[n_vertices - 1].color);
+}
diff --git a/gles/cogl-texture.h b/gles/cogl-texture.h
new file mode 100644
index 00000000..e2401be0
--- /dev/null
+++ b/gles/cogl-texture.h
@@ -0,0 +1,63 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_TEXTURE_H
+#define __COGL_TEXTURE_H
+
+#include "cogl-bitmap.h"
+
+typedef struct _CoglTexture CoglTexture;
+typedef struct _CoglTexSliceSpan CoglTexSliceSpan;
+typedef struct _CoglSpanIter CoglSpanIter;
+
+struct _CoglTexSliceSpan
+{
+ gint start;
+ gint size;
+ gint waste;
+};
+
+struct _CoglTexture
+{
+ guint ref_count;
+ CoglBitmap bitmap;
+ gboolean bitmap_owner;
+ GLenum gl_target;
+ GLenum gl_intformat;
+ GLenum gl_format;
+ GLenum gl_type;
+ GArray *slice_x_spans;
+ GArray *slice_y_spans;
+ GArray *slice_gl_handles;
+ gint max_waste;
+ COGLenum min_filter;
+ COGLenum mag_filter;
+ gboolean is_foreign;
+};
+
+CoglTexture*
+_cogl_texture_pointer_from_handle (CoglHandle handle);
+
+#endif /* __COGL_TEXTURE_H */
diff --git a/gles/cogl-util.c b/gles/cogl-util.c
new file mode 100644
index 00000000..b024e763
--- /dev/null
+++ b/gles/cogl-util.c
@@ -0,0 +1,51 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl.h"
+#include "cogl-internal.h"
+#include "cogl-util.h"
+
+/**
+ * cogl_util_next_p2:
+ * @a: Value to get the next power
+ *
+ * Calculates the next power greater than @a.
+ *
+ * Return value: The next power after @a.
+ */
+int
+cogl_util_next_p2 (int a)
+{
+ int rval=1;
+
+ while(rval < a)
+ rval <<= 1;
+
+ return rval;
+}
diff --git a/gles/cogl-util.h b/gles/cogl-util.h
new file mode 100644
index 00000000..759da8cc
--- /dev/null
+++ b/gles/cogl-util.h
@@ -0,0 +1,32 @@
+/*
+ * Clutter COGL
+ *
+ * A basic GL/GLES Abstraction/Utility Layer
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_UTIL_H
+#define __COGL_UTIL_H
+
+int
+cogl_util_next_p2 (int a);
+
+#endif /* __COGL_UTIL_H */
diff --git a/gles/cogl.c b/gles/cogl.c
index 495702ae..e4390889 100644
--- a/gles/cogl.c
+++ b/gles/cogl.c
@@ -32,16 +32,12 @@
#include <GLES/gl.h>
#include <string.h>
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-#define PIXEL_TYPE GL_UNSIGNED_BYTE
-#else
-#define PIXEL_TYPE GL_UNSIGNED_INT_8_8_8_8_REV
-#endif
-
-static gulong __enable_flags = 0;
+#include "cogl-internal.h"
+#include "cogl-util.h"
+#include "cogl-context.h"
-#define COGL_DEBUG 0
+/* GL error to string conversion */
#if COGL_DEBUG
struct token_string
{
@@ -75,20 +71,6 @@ error_string(GLenum errorCode)
}
#endif
-#if COGL_DEBUG
-#define GE(x...) { \
- GLenum err; \
- (x); \
- fprintf(stderr, "%s\n", #x); \
- while ((err = glGetError()) != GL_NO_ERROR) { \
- fprintf(stderr, "glError: %s caught at %s:%u\n", \
- (char *)error_string(err), \
- __FILE__, __LINE__); \
- } \
-}
-#else
-#define GE(x) (x);
-#endif
CoglFuncPtr
cogl_get_proc_address (const gchar* name)
@@ -117,9 +99,6 @@ cogl_paint_init (const ClutterColor *color)
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDisable (GL_LIGHTING);
glDisable (GL_FOG);
-
- cogl_enable (CGL_ENABLE_BLEND);
- glTexEnvx (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
/* FIXME: inline most of these */
@@ -173,53 +152,98 @@ cogl_rotate (gint angle, gint x, gint y, gint z)
CLUTTER_INT_TO_FIXED(z)) );
}
-void
-cogl_enable (gulong flags)
+static inline gboolean
+cogl_toggle_flag (CoglContext *ctx,
+ gulong new_flags,
+ gulong flag,
+ GLenum gl_flag)
{
- /* This function essentially caches glEnable state() in the
- * hope of lessening number GL traffic.
- */
- if (flags & CGL_ENABLE_BLEND)
+ /* Toggles and caches a single enable flag on or off
+ * by comparing to current state
+ */
+ if (new_flags & flag)
{
- if (!(__enable_flags & CGL_ENABLE_BLEND))
+ if (!(ctx->enable_flags & flag))
{
- GE( glEnable (GL_BLEND) );
- GE( glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) );
+ GE( glEnable (gl_flag) );
+ ctx->enable_flags |= flag;
+ return TRUE;
}
- __enable_flags |= CGL_ENABLE_BLEND;
}
- else if (__enable_flags & CGL_ENABLE_BLEND)
+ else if (ctx->enable_flags & flag)
{
- GE( glDisable (GL_BLEND) );
- __enable_flags &= ~CGL_ENABLE_BLEND;
+ GE( glDisable (gl_flag) );
+ ctx->enable_flags &= ~flag;
}
+
+ return FALSE;
+}
- if (flags & CGL_ENABLE_TEXTURE_2D)
+static inline gboolean
+cogl_toggle_client_flag (CoglContext *ctx,
+ gulong new_flags,
+ gulong flag,
+ GLenum gl_flag)
+{
+ /* Toggles and caches a single client-side enable flag
+ * on or off by comparing to current state
+ */
+ if (new_flags & flag)
{
- if (!(__enable_flags & CGL_ENABLE_TEXTURE_2D))
- GE( glEnable (GL_TEXTURE_2D) );
- __enable_flags |= CGL_ENABLE_TEXTURE_2D;
+ if (!(ctx->enable_flags & flag))
+ {
+ GE( glEnableClientState (gl_flag) );
+ ctx->enable_flags |= flag;
+ return TRUE;
+ }
}
- else if (__enable_flags & CGL_ENABLE_TEXTURE_2D)
+ else if (ctx->enable_flags & flag)
{
- GE( glDisable (GL_TEXTURE_2D) );
- __enable_flags &= ~CGL_ENABLE_TEXTURE_2D;
+ GE( glDisableClientState (gl_flag) );
+ ctx->enable_flags &= ~flag;
}
+
+ return FALSE;
+}
-#if 0
- if (flags & CGL_ENABLE_TEXTURE_RECT)
- {
- if (!(__enable_flags & CGL_ENABLE_TEXTURE_RECT))
- glEnable (GL_TEXTURE_RECTANGLE_);
-
- __enable_flags |= CGL_ENABLE_TEXTURE_RECT;
- }
- else if (__enable_flags & CGL_ENABLE_TEXTURE_RECT)
+void
+cogl_enable (gulong flags)
+{
+ /* This function essentially caches glEnable state() in the
+ * hope of lessening number GL traffic.
+ */
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (cogl_toggle_flag (ctx, flags,
+ COGL_ENABLE_BLEND,
+ GL_BLEND))
{
- glDisable (GL_TEXTURE_RECTANGLE_);
- __enable_flags &= ~CGL_ENABLE_TEXTURE_RECT;
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
-#endif
+
+ cogl_toggle_flag (ctx, flags,
+ COGL_ENABLE_TEXTURE_2D,
+ GL_TEXTURE_2D);
+
+ cogl_toggle_client_flag (ctx, flags,
+ COGL_ENABLE_VERTEX_ARRAY,
+ GL_VERTEX_ARRAY);
+
+ cogl_toggle_client_flag (ctx, flags,
+ COGL_ENABLE_TEXCOORD_ARRAY,
+ GL_TEXTURE_COORD_ARRAY);
+
+ cogl_toggle_client_flag (ctx, flags,
+ COGL_ENABLE_COLOR_ARRAY,
+ GL_COLOR_ARRAY);
+}
+
+gulong
+cogl_get_enable ()
+{
+ _COGL_GET_CONTEXT (ctx, 0);
+
+ return ctx->enable_flags;
}
void
@@ -242,6 +266,8 @@ cogl_enable_depth_test (gboolean setting)
void
cogl_color (const ClutterColor *color)
{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
#if 0 /*HAVE_GLES_COLOR4UB*/
/* NOTE: seems SDK_OGLES-1.1_LINUX_PCEMULATION_2.02.22.0756 has this call
@@ -267,25 +293,9 @@ cogl_color (const ClutterColor *color)
(color->blue << 16) / 0xff,
(color->alpha << 16) / 0xff));
#endif
-}
-
-static inline void
-cogl_rectangle_internal (ClutterFixed x,
- ClutterFixed y,
- ClutterFixed width,
- ClutterFixed height)
-{
- GLfixed rect_verts[8] = {
- x, y,
- x + width, y,
- x, y + height,
- x + width, y + height
- };
-
- GE( glEnableClientState (GL_VERTEX_ARRAY) );
- GE( glVertexPointer (2, GL_FIXED, 0, rect_verts) );
- GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) );
- GE( glDisableClientState (GL_VERTEX_ARRAY) );
+
+ /* Store alpha for proper blending enables */
+ ctx->color_alpha = color->alpha;
}
void
@@ -294,199 +304,57 @@ cogl_clip_set (ClutterFixed x_offset,
ClutterFixed width,
ClutterFixed height)
{
- GE( glEnable (GL_STENCIL_TEST) );
+ if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES))
+ {
+ GLfixed eqn_left[4] = { CFX_ONE, 0, 0, -x_offset };
+ GLfixed eqn_right[4] = { -CFX_ONE, 0, 0, x_offset + width };
+ GLfixed eqn_top[4] = { 0, CFX_ONE, 0, -y_offset };
+ GLfixed eqn_bottom[4] = { 0, -CFX_ONE, 0, y_offset + height };
+
+ GE( glClipPlanex (GL_CLIP_PLANE0, eqn_left) );
+ GE( glClipPlanex (GL_CLIP_PLANE1, eqn_right) );
+ GE( glClipPlanex (GL_CLIP_PLANE2, eqn_top) );
+ GE( glClipPlanex (GL_CLIP_PLANE3, eqn_bottom) );
+ GE( glEnable (GL_CLIP_PLANE0) );
+ GE( glEnable (GL_CLIP_PLANE1) );
+ GE( glEnable (GL_CLIP_PLANE2) );
+ GE( glEnable (GL_CLIP_PLANE3) );
+ }
+ else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER))
+ {
+ GE( glEnable (GL_STENCIL_TEST) );
- GE( glClearStencil (0) );
- GE( glClear (GL_STENCIL_BUFFER_BIT) );
+ GE( glClearStencil (0) );
+ GE( glClear (GL_STENCIL_BUFFER_BIT) );
- GE( glStencilFunc (GL_NEVER, 0x1, 0x1) );
- GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
+ GE( glStencilFunc (GL_NEVER, 0x1, 0x1) );
+ GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
- GE( glColor4x (CFX_ONE, CFX_ONE, CFX_ONE, CFX_ONE ) );
+ GE( glColor4x (CFX_ONE, CFX_ONE, CFX_ONE, CFX_ONE ) );
- cogl_rectangle_internal (x_offset, y_offset, width, height);
+ cogl_fast_fill_rectanglex (x_offset, y_offset, width, height);
- GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
- GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
+ GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
+ GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
+ }
}
void
cogl_clip_unset (void)
{
- GE( glDisable (GL_STENCIL_TEST) );
-}
-
-gboolean
-cogl_texture_can_size (COGLenum target,
- COGLenum pixel_format,
- COGLenum pixel_type,
- int width,
- int height)
-{
- /* FIXME: How we get this is likely GLES implementation dependant. */
- return TRUE;
-}
-
-void
-cogl_texture_quad (gint x1,
- gint x2,
- gint y1,
- gint y2,
- ClutterFixed tx1,
- ClutterFixed ty1,
- ClutterFixed tx2,
- ClutterFixed ty2)
-{
-#define FIX CLUTTER_INT_TO_FIXED
-
- GLfixed quadVerts[] = {
- FIX(x1), FIX(y1), 0,
- FIX(x2), FIX(y1), 0,
- FIX(x2), FIX(y2), 0,
- FIX(x2), FIX(y2), 0,
- FIX(x1), FIX(y2), 0,
- FIX(x1), FIX(y1), 0
- };
-
- GLfixed quadTex[] = {
- tx1, ty1,
- tx2, ty1,
- tx2, ty2,
- tx2, ty2,
- tx1, ty2,
- tx1, ty1
- };
-
-#undef FIX
-
- GE( glEnableClientState(GL_VERTEX_ARRAY) );
- GE( glEnableClientState(GL_TEXTURE_COORD_ARRAY) );
- GE( glVertexPointer(3, GL_FIXED, 0, quadVerts) );
- GE( glTexCoordPointer(2, GL_FIXED, 0, quadTex) );
- GE( glDrawArrays(GL_TRIANGLES, 0, 6) );
- GE( glDisableClientState(GL_TEXTURE_COORD_ARRAY) );
- GE( glDisableClientState(GL_VERTEX_ARRAY) );
-
- /* Note also see glDrawTexxOES for potential optimisation */
-}
-
-void
-cogl_textures_create (guint num, COGLuint *textures)
-{
- GE( glGenTextures (num, textures) );
-}
-
-void
-cogl_textures_destroy (guint num, const COGLuint *textures)
-{
- GE( glDeleteTextures (num, textures) );
-}
-
-void
-cogl_texture_bind (COGLenum target, COGLuint texture)
-{
- GE( glBindTexture (target, texture) );
-}
-
-void
-cogl_texture_set_alignment (COGLenum target,
- guint alignment,
- guint row_length)
-{
- /* GE( glPixelStorei (GL_UNPACK_ROW_LENGTH, row_length) ); */
- GE( glPixelStorei (GL_UNPACK_ALIGNMENT, alignment) );
-}
-
-void
-cogl_texture_set_filters (COGLenum target,
- COGLenum min_filter,
- COGLenum max_filter)
-{
- GE( glTexParameteri(target, GL_TEXTURE_MAG_FILTER, max_filter) );
- GE( glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filter) );
-}
-
-void
-cogl_texture_set_wrap (COGLenum target,
- COGLenum wrap_s,
- COGLenum wrap_t)
-{
- GE( glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap_s) );
- GE( glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap_s) );
-}
-
-void
-cogl_texture_image_2d (COGLenum target,
- COGLint internal_format,
- gint width,
- gint height,
- COGLenum format,
- COGLenum type,
- const guchar* pixels)
-{
- GE( glTexImage2D (target,
- 0,
- format, /* HACK: For gles we set the internal_format equal
- * to the pixel format. This is for RGB data (i.e
- * jpgs) which seem to need a matching internal
- * format rather than RGBA (which is used by GL)
- *.
- * This fix isn't ideal..
- */
- width,
- height,
- 0,
- format,
- type,
- pixels) );
-}
-
-void
-cogl_texture_sub_image_2d (COGLenum target,
- gint xoff,
- gint yoff,
- gint width,
- gint height,
- COGLenum format,
- COGLenum type,
- const guchar* pixels)
-{
- GE( glTexSubImage2D (target,
- 0,
- xoff,
- yoff,
- width,
- height,
- format,
- type,
- pixels));
-}
-
-void
-cogl_rectangle (gint x,
- gint y,
- guint width,
- guint height)
-{
- cogl_rectangle_internal (CLUTTER_INT_TO_FIXED (x),
- CLUTTER_INT_TO_FIXED (y),
- CLUTTER_INT_TO_FIXED (width),
- CLUTTER_INT_TO_FIXED (height));
-}
-
-/* FIXME: Should use ClutterReal or Fixed */
-void
-cogl_trapezoid (gint y1,
- gint x11,
- gint x21,
- gint y2,
- gint x12,
- gint x22)
-{
- /* FIXME */
+ if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES))
+ {
+ GE( glDisable (GL_CLIP_PLANE3) );
+ GE( glDisable (GL_CLIP_PLANE2) );
+ GE( glDisable (GL_CLIP_PLANE1) );
+ GE( glDisable (GL_CLIP_PLANE0) );
+ }
+ else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER))
+ {
+ GE( glDisable (GL_STENCIL_TEST) );
+ }
}
-
void
cogl_alpha_func (COGLenum func,
ClutterFixed ref)
@@ -591,11 +459,49 @@ cogl_setup_viewport (guint w,
GE( glTranslatex (0, -CFX_ONE * height, 0) );
}
+static void
+_cogl_features_init ()
+{
+ ClutterFeatureFlags flags = 0;
+ int stencil_bits = 0;
+ int max_clip_planes = 0;
+
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ flags = COGL_FEATURE_TEXTURE_READ_PIXELS;
+
+ GE( glGetIntegerv (GL_STENCIL_BITS, &stencil_bits) );
+ if (stencil_bits > 0)
+ flags |= COGL_FEATURE_STENCIL_BUFFER;
+
+ GE( glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) );
+ if (max_clip_planes >= 4)
+ flags |= COGL_FEATURE_FOUR_CLIP_PLANES;
+
+ ctx->feature_flags = flags;
+ ctx->features_cached = TRUE;
+}
+
ClutterFeatureFlags
cogl_get_features ()
{
- /* Suck */
- return 0;
+ _COGL_GET_CONTEXT (ctx, 0);
+
+ if (!ctx->features_cached)
+ _cogl_features_init ();
+
+ return ctx->feature_flags;
+}
+
+gboolean
+cogl_features_available (CoglFeatureFlags features)
+{
+ _COGL_GET_CONTEXT (ctx, 0);
+
+ if (!ctx->features_cached)
+ _cogl_features_init ();
+
+ return (ctx->feature_flags & features) == features;
}
void
@@ -654,39 +560,6 @@ cogl_fog_set (const ClutterColor *fog_color,
glFogx (GL_FOG_END, (GLfixed) z_far);
}
-/* Offscreen - TODO: possible support from FBO's/PBuffers
- * See;
- * http://www.khronos.org/message_boards/viewtopic.php?t=589
- * http://www.gamedev.net/community/forums/topic.asp?topic_id=369739
- *
- * Likely requires EGL 1.3 for eglBindTexImage
-*/
-COGLuint
-cogl_offscreen_create (COGLuint target_texture)
-{
- return 0;
-}
-
-void
-cogl_offscreen_destroy (COGLuint offscreen_handle)
-{
-}
-
-void
-cogl_offscreen_redirect_start (COGLuint offscreen_handle,
- gint width,
- gint height)
-{
-}
-
-void
-cogl_offscreen_redirect_end (COGLuint offscreen_handle,
- gint width,
- gint height)
-{
-}
-
-
/* Shaders, no support on regular OpenGL 1.1 */
COGLhandle