From eff57a1cb0543ef4e172885352db5ba0b14cca2a Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 25 Apr 2008 13:37:36 +0000 Subject: [PATCH] Merged clutter-ivan branch into trunk. svn merge \ https://svn.o-hand.com/repos/clutter/trunk/clutter@2509 \ https://svn.o-hand.com/repos/clutter/branches/clutter-ivan@HEAD --- Makefile.am | 2 +- TODO | 25 + cogl.h | 290 -- cogl.h.in | 874 +++++ common/Makefile.am | 23 + common/cogl-bitmap-fallback.c | 369 ++ common/cogl-bitmap-pixbuf.c | 197 + common/cogl-bitmap.c | 150 + common/cogl-bitmap.h | 95 + common/cogl-util.c | 51 + common/cogl-util.h | 32 + common/stb_image.c | 3772 ++++++++++++++++++++ doc/reference/cogl/Makefile.am | 93 + doc/reference/cogl/cogl-docs.sgml | 57 + doc/reference/cogl/cogl-sections.txt | 148 + gl/Makefile.am | 37 +- gl/cogl-context.c | 114 + gl/cogl-context.h | 95 + gl/cogl-fbo.c | 390 ++ gl/cogl-fbo.h | 38 + gl/cogl-internal.h | 61 + gl/cogl-primitives.c | 1092 ++++++ gl/cogl-primitives.h | 61 + gl/cogl-texture.c | 2303 ++++++++++++ gl/cogl-texture.h | 64 + gl/cogl.c | 808 ++--- gles/Makefile.am | 37 +- gles/cogl-context.c | 96 + gles/cogl-context.h | 78 + gles/{cogl-defines.h => cogl-defines.h.in} | 12 - gles/cogl-fbo.c | 188 + gles/cogl-fbo.h | 38 + gles/cogl-internal.h | 63 + gles/cogl-primitives.c | 1112 ++++++ gles/cogl-primitives.h | 54 + gles/cogl-texture.c | 2185 ++++++++++++ gles/cogl-texture.h | 63 + gles/cogl-util.c | 51 + gles/cogl-util.h | 32 + gles/cogl.c | 467 +-- 40 files changed, 14675 insertions(+), 1042 deletions(-) create mode 100644 TODO delete mode 100644 cogl.h create mode 100644 cogl.h.in create mode 100644 common/Makefile.am create mode 100644 common/cogl-bitmap-fallback.c create mode 100644 common/cogl-bitmap-pixbuf.c create mode 100644 common/cogl-bitmap.c create mode 100644 common/cogl-bitmap.h create mode 100644 common/cogl-util.c create mode 100644 common/cogl-util.h create mode 100644 common/stb_image.c create mode 100644 doc/reference/cogl/Makefile.am create mode 100644 doc/reference/cogl/cogl-docs.sgml create mode 100644 doc/reference/cogl/cogl-sections.txt create mode 100644 gl/cogl-context.c create mode 100644 gl/cogl-context.h create mode 100644 gl/cogl-fbo.c create mode 100644 gl/cogl-fbo.h create mode 100644 gl/cogl-internal.h create mode 100644 gl/cogl-primitives.c create mode 100644 gl/cogl-primitives.h create mode 100644 gl/cogl-texture.c create mode 100644 gl/cogl-texture.h create mode 100644 gles/cogl-context.c create mode 100644 gles/cogl-context.h rename gles/{cogl-defines.h => cogl-defines.h.in} (98%) create mode 100644 gles/cogl-fbo.c create mode 100644 gles/cogl-fbo.h create mode 100644 gles/cogl-internal.h create mode 100644 gles/cogl-primitives.c create mode 100644 gles/cogl-primitives.h create mode 100644 gles/cogl-texture.c create mode 100644 gles/cogl-texture.h create mode 100644 gles/cogl-util.c create mode 100644 gles/cogl-util.h diff --git a/Makefile.am b/Makefile.am index f09f63723..9bb6b772c 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 000000000..e1e5d316c --- /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 beb2955d7..000000000 --- a/cogl.h +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Clutter COGL - * - * A basic GL/GLES Abstraction/Utility Layer - * - * Authored By Matthew Allum - * - * 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 -#include -#include -#include -#include - -#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 000000000..47b6b58f3 --- /dev/null +++ b/cogl.h.in @@ -0,0 +1,874 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 +#include +#include +#include +#include + +#include + +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 #ClutterFixeds 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 #ClutterFixeds 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 #ClutterFixeds 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 000000000..b6dca2df2 --- /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 000000000..21a558460 --- /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 + * + * 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 + +/* 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 000000000..923788607 --- /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 + * + * 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 + +#ifdef USE_GDKPIXBUF +#include +#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 000000000..39341e0b0 --- /dev/null +++ b/common/cogl-bitmap.c @@ -0,0 +1,150 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 + +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; linerowstride; + dstdata += dst->rowstride; + } +} diff --git a/common/cogl-bitmap.h b/common/cogl-bitmap.h new file mode 100644 index 000000000..ca9f8a9c3 --- /dev/null +++ b/common/cogl-bitmap.h @@ -0,0 +1,95 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 + +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 000000000..b024e763e --- /dev/null +++ b/common/cogl-util.c @@ -0,0 +1,51 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..759da8cc7 --- /dev/null +++ b/common/cogl-util.h @@ -0,0 +1,32 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..bd3e63b35 --- /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 +#endif + +#ifndef STBI_NO_HDR +#include // ldexp +#include // 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 +#endif +#include +#include +#include +#include + +#if STBI_SIMD +#include +#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 000000000..39daa63dc --- /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 000000000..bac6e42b0 --- /dev/null +++ b/doc/reference/cogl/cogl-docs.sgml @@ -0,0 +1,57 @@ + + + + + COGL Reference Manual + + + + + 2008 + OpenedHand LTD + + + + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free + Documentation License, 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 GNU Free + Documentation License from the Free Software + Foundation by visiting their Web site or by writing + to: + +
+ The Free Software Foundation, Inc., + 59 Temple Place - Suite 330, + Boston, MA 02111-1307 +, + USA +
+
+
+ +
+ + + COGL - GL Abstraction API + +
+ About COGL + + FIXME + +
+ + + + + + + +
+
diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt new file mode 100644 index 000000000..33d49c9b3 --- /dev/null +++ b/doc/reference/cogl/cogl-sections.txt @@ -0,0 +1,148 @@ +
+cogl +General API +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 + +cogl_perspective +cogl_setup_viewport +cogl_get_modelview_matrix +cogl_get_projection_matrix +cogl_get_viewport + +cogl_push_matrix +cogl_pop_matrix +cogl_scale +cogl_translatex +cogl_translate +cogl_rotatex +cogl_rotate + +cogl_clip_set +cogl_clip_unset + +cogl_enable_depth_test +cogl_alpha_func +cogl_fog_set +
+ +
+cogl-util +Utility API +cogl_create_context +cogl_destroy_context +COGL_INVALID_HANDLE +CoglHandle +CoglFuncPtr + +CoglFeatureFlags +cogl_get_features +cogl_features_available + +cogl_get_proc_address +cogl_check_extension +cogl_get_bitmasks +cogl_paint_init + +cogl_util_next_p2 +
+ +
+cogl-primitives +Primitives +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 +
+ +
+cogl-texture +Textures +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 +
+ +
+cogl-shaders +Shaders and Programmable Pipeline +cogl_create_shader +cogl_shader_destroy +cogl_shader_source +cogl_shader_compile +cogl_shader_get_info_log +cogl_shader_get_parameteriv + +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 +
+ +
+cogl-offscreen +Offscreen Buffers +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 +
diff --git a/gl/Makefile.am b/gl/Makefile.am index 98949b261..17e5ed465 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 000000000..936d7e9bf --- /dev/null +++ b/gl/cogl-context.c @@ -0,0 +1,114 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 + +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 000000000..4e952cc5f --- /dev/null +++ b/gl/cogl-context.h @@ -0,0 +1,95 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..f4fb968d5 --- /dev/null +++ b/gl/cogl-fbo.c @@ -0,0 +1,390 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..f2dd5298f --- /dev/null +++ b/gl/cogl-fbo.h @@ -0,0 +1,38 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..1f6b8639e --- /dev/null +++ b/gl/cogl-internal.h @@ -0,0 +1,61 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..352ad46d3 --- /dev/null +++ b/gl/cogl-primitives.c @@ -0,0 +1,1092 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 +#include + +#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 000000000..f43ab6a47 --- /dev/null +++ b/gl/cogl-primitives.h @@ -0,0 +1,61 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..5e4d6ee54 --- /dev/null +++ b/gl/cogl-texture.c @@ -0,0 +1,2303 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 +#include + +/* +#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; islice_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 000000000..d4104804b --- /dev/null +++ b/gl/cogl-texture.h @@ -0,0 +1,64 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 5e752cd84..3e8fc350b 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; +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-context.h" -/* 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; +/* 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,197 +446,63 @@ 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( glClearStencil (0.0f) ); - GE( glClear (GL_STENCIL_BUFFER_BIT) ); + 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( glStencilFunc (GL_NEVER, 0x1, 0x1) ); - GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); + GE( glClearStencil (0.0f) ); + GE( glClear (GL_STENCIL_BUFFER_BIT) ); - GE( glColor3f (1.0f, 1.0f, 1.0f) ); + GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); + GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); - 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( glColor3f (1.0f, 1.0f, 1.0f) ); - GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); - GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + 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) ); + } } 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)) { - _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_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)) + { + 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 32dda4a67..c629e1ee5 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 000000000..0027e96df --- /dev/null +++ b/gles/cogl-context.c @@ -0,0 +1,96 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 +#include + +#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 000000000..c5117a9f5 --- /dev/null +++ b/gles/cogl-context.h @@ -0,0 +1,78 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 similarity index 98% rename from gles/cogl-defines.h rename to gles/cogl-defines.h.in index 83f2ae15d..b304680f9 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 000000000..e8f186b31 --- /dev/null +++ b/gles/cogl-fbo.c @@ -0,0 +1,188 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..f2dd5298f --- /dev/null +++ b/gles/cogl-fbo.h @@ -0,0 +1,38 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..69baa4a9a --- /dev/null +++ b/gles/cogl-internal.h @@ -0,0 +1,63 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..1bba58f55 --- /dev/null +++ b/gles/cogl-primitives.c @@ -0,0 +1,1112 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 +#include + +#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 000000000..4c96f409b --- /dev/null +++ b/gles/cogl-primitives.h @@ -0,0 +1,54 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..2eae2c9b7 --- /dev/null +++ b/gles/cogl-texture.c @@ -0,0 +1,2185 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 +#include +/* +#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; islice_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 000000000..e2401be07 --- /dev/null +++ b/gles/cogl-texture.h @@ -0,0 +1,63 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..b024e763e --- /dev/null +++ b/gles/cogl-util.c @@ -0,0 +1,51 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 000000000..759da8cc7 --- /dev/null +++ b/gles/cogl-util.h @@ -0,0 +1,32 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * 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 495702aee..e43908893 100644 --- a/gles/cogl.c +++ b/gles/cogl.c @@ -32,16 +32,12 @@ #include #include -#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 +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-context.h" -static gulong __enable_flags = 0; - -#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)) ); } +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)) - { - GE( glEnable (GL_BLEND) ); - GE( glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ); - } - __enable_flags |= CGL_ENABLE_BLEND; - } - else if (__enable_flags & CGL_ENABLE_BLEND) - { - GE( glDisable (GL_BLEND) ); - __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); - if (flags & CGL_ENABLE_TEXTURE_2D) - { - if (!(__enable_flags & CGL_ENABLE_TEXTURE_2D)) - GE( glEnable (GL_TEXTURE_2D) ); - __enable_flags |= CGL_ENABLE_TEXTURE_2D; - } - else if (__enable_flags & CGL_ENABLE_TEXTURE_2D) - { - GE( glDisable (GL_TEXTURE_2D) ); - __enable_flags &= ~CGL_ENABLE_TEXTURE_2D; - } + cogl_toggle_client_flag (ctx, flags, + COGL_ENABLE_COLOR_ARRAY, + GL_COLOR_ARRAY); +} -#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) - { - glDisable (GL_TEXTURE_RECTANGLE_); - __enable_flags &= ~CGL_ENABLE_TEXTURE_RECT; - } -#endif +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( glClearStencil (0) ); - GE( glClear (GL_STENCIL_BUFFER_BIT) ); + 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( glStencilFunc (GL_NEVER, 0x1, 0x1) ); - GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); + GE( glClearStencil (0) ); + GE( glClear (GL_STENCIL_BUFFER_BIT) ); - GE( glColor4x (CFX_ONE, CFX_ONE, CFX_ONE, CFX_ONE ) ); + GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); + GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); - cogl_rectangle_internal (x_offset, y_offset, width, height); + GE( glColor4x (CFX_ONE, CFX_ONE, CFX_ONE, CFX_ONE ) ); - GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); - GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + cogl_fast_fill_rectanglex (x_offset, y_offset, width, height); + + GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + } } void cogl_clip_unset (void) { - GE( glDisable (GL_STENCIL_TEST) ); + 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) ); + } } -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 */ -} - - 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