diff options
40 files changed, 14671 insertions, 1038 deletions
diff --git a/Makefile.am b/Makefile.am index f09f6372..9bb6b772 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = $(CLUTTER_COGL) +SUBDIRS = common $(CLUTTER_COGL) EXTRA_DIST = cogl.h @@ -0,0 +1,25 @@ +============================ +Cogl overhaul related tasks: +============================ + +MISC + +- implemenent a 1 to 1 mapping of COGL_FEATURE flags + into CLUTTER_FEATURE flags before combining them + into final clutter flags value (clutter-feature.c) + +TEXTURE + +- cogl_texture_get_data, cogl_texture_set_region in GLES + +- YUV texture format support (multitexturing + shader) + +FBO + +- add stencil, depth and other renderbuffers to fbos + +- cogl_offscreen_new_multisample + +- test cogl_offscreen_blit + +- add "filter" argument to cogl_offscreen_blit diff --git a/cogl.h b/cogl.h deleted file mode 100644 index beb2955d..00000000 --- a/cogl.h +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Clutter COGL - * - * A basic GL/GLES Abstraction/Utility Layer - * - * Authored By Matthew Allum <mallum@openedhand.com> - * - * Copyright (C) 2007 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* - * COGL - * ==== - * - * 'cogl' is a very simple abstraction layer which wraps GL and GLES. - * - * - * !!!! DO NOT USE THIS API YET OUTSIDE OF CLUTTER CORE !!!! - * THE API WILL FLUCTUATE WILDLY - * - * TODO: - * - Use ClutterReal for fixed/float params. - * - Add Perspective/viewport setup - * - Add Features.. - */ - -#ifndef __COGL_H__ -#define __COGL_H__ - -#include <glib.h> -#include <clutter/clutter-color.h> -#include <clutter/clutter-feature.h> -#include <clutter/clutter-fixed.h> -#include <clutter/clutter-types.h> - -#include "cogl-defines.h" - -G_BEGIN_DECLS - -#define CGL_ENABLE_BLEND (1<<1) -#define CGL_ENABLE_TEXTURE_2D (1<<2) -#define CGL_ENABLE_ALPHA_TEST (1<<3) -#define CGL_ENABLE_TEXTURE_RECT (1<<4) - -typedef void (*CoglFuncPtr) (void); - -CoglFuncPtr -cogl_get_proc_address (const gchar* name); - -gboolean -cogl_check_extension (const gchar *name, const gchar *ext); - -void -cogl_perspective (ClutterFixed fovy, - ClutterFixed aspect, - ClutterFixed zNear, - ClutterFixed zFar); - -void -cogl_setup_viewport (guint width, - guint height, - ClutterFixed fovy, - ClutterFixed aspect, - ClutterFixed z_near, - ClutterFixed z_far); - -void -cogl_paint_init (const ClutterColor *color); - -void -cogl_push_matrix (void); - -void -cogl_pop_matrix (void); - -void -cogl_scale (ClutterFixed x, ClutterFixed z); - -void -cogl_translatex (ClutterFixed x, ClutterFixed y, ClutterFixed z); - -void -cogl_translate (gint x, gint y, gint z); - -void -cogl_rotatex (ClutterFixed angle, gint x, gint y, gint z); - -void -cogl_rotate (gint angle, gint x, gint y, gint z); - -void -cogl_color (const ClutterColor *color); - -void -cogl_clip_set (ClutterFixed x_offset, - ClutterFixed y_offset, - ClutterFixed width, - ClutterFixed height); - -void -cogl_clip_unset (void); - -void -cogl_enable (gulong flags); - -void -cogl_enable_depth_test (gboolean setting); - -gboolean -cogl_texture_can_size (COGLenum target, - COGLenum pixel_format, - COGLenum pixel_type, - int width, - int height); - -void -cogl_texture_quad (gint x1, - gint x2, - gint y1, - gint y2, - ClutterFixed tx1, - ClutterFixed ty1, - ClutterFixed tx2, - ClutterFixed ty2); - -void -cogl_textures_create (guint num, COGLuint *textures); - -void -cogl_textures_destroy (guint num, const COGLuint *textures); - -void -cogl_texture_bind (COGLenum target, COGLuint texture); - -void -cogl_texture_set_alignment (COGLenum target, - guint alignment, - guint row_length); - -void -cogl_texture_set_filters (COGLenum target, - COGLenum min_filter, - COGLenum max_filter); - -void -cogl_texture_set_wrap (COGLenum target, - COGLenum wrap_s, - COGLenum wrap_t); - -void -cogl_texture_image_2d (COGLenum target, - COGLint internal_format, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels); - -void -cogl_texture_sub_image_2d (COGLenum target, - gint xoff, - gint yoff, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels); - -void -cogl_rectangle (gint x, gint y, guint width, guint height); - -void -cogl_trapezoid (gint y1, - gint x11, - gint x21, - gint y2, - gint x12, - gint x22); -void -cogl_alpha_func (COGLenum func, - ClutterFixed ref); - -ClutterFeatureFlags -cogl_get_features (); - -void -cogl_get_modelview_matrix (ClutterFixed m[16]); - -void -cogl_get_projection_matrix (ClutterFixed m[16]); - -void -cogl_get_viewport (ClutterFixed v[4]); - -void -cogl_get_bitmasks (gint *red, gint *green, gint *blue, gint *alpha); - -void -cogl_fog_set (const ClutterColor *fog_color, - ClutterFixed density, - ClutterFixed z_near, - ClutterFixed z_far); - - -COGLhandle -cogl_create_shader (COGLenum shaderType); - -void -cogl_shader_destroy (COGLhandle handle); - - -void -cogl_shader_source (COGLhandle shader, - const gchar *source); -void -cogl_shader_compile (COGLhandle shader_handle); - -void -cogl_shader_get_info_log (COGLhandle handle, - guint size, - gchar *buffer); - -void -cogl_shader_get_parameteriv (COGLhandle handle, - COGLenum pname, - COGLint *dest); - - -COGLhandle -cogl_create_program (void); - -void -cogl_program_destroy (COGLhandle handle); - -void -cogl_program_attach_shader (COGLhandle program_handle, - COGLhandle shader_handle); - -/* 0 to use none */ -void -cogl_program_link (COGLhandle program_handle); - -void -cogl_program_use (COGLhandle program_handle); - -COGLint -cogl_program_get_uniform_location (COGLhandle program_int, - const gchar *uniform_name); - - -void -cogl_program_uniform_1f (COGLint uniform_no, - gfloat value); - -/* Offscreen - FBO support */ - -COGLuint -cogl_offscreen_create (COGLuint target_texture); - -void -cogl_offscreen_destroy (COGLuint offscreen_handle); - -void -cogl_offscreen_redirect_start (COGLuint offscreen_handle, - gint width, - gint height); - -void -cogl_offscreen_redirect_end (COGLuint offscreen_handle, - gint width, - gint height); - -G_END_DECLS - -#endif /* __COGL_H__ */ diff --git a/cogl.h.in b/cogl.h.in new file mode 100644 index 00000000..47b6b58f --- /dev/null +++ b/cogl.h.in @@ -0,0 +1,874 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * COGL + * ==== + * + * 'cogl' is a very simple abstraction layer which wraps GL and GLES. + * + * + * !!!! DO NOT USE THIS API YET OUTSIDE OF CLUTTER CORE !!!! + * THE API WILL FLUCTUATE WILDLY + * + * TODO: + * - Use ClutterReal for fixed/float params. + * - Add Perspective/viewport setup + * - Add Features.. + */ + +#ifndef __COGL_H__ +#define __COGL_H__ + +#include <glib.h> +#include <clutter/clutter-color.h> +#include <clutter/clutter-feature.h> +#include <clutter/clutter-fixed.h> +#include <clutter/clutter-types.h> + +#include <cogl/cogl-defines-@CLUTTER_COGL@.h> + +G_BEGIN_DECLS + +/* Enum declarations */ + +#define COGL_PIXEL_FORMAT_24 2 +#define COGL_PIXEL_FORMAT_32 3 +#define COGL_A_BIT (1 << 4) +#define COGL_BGR_BIT (1 << 5) +#define COGL_AFIRST_BIT (1 << 6) +#define COGL_PREMULT_BIT (1 << 7) +#define COGL_UNORDERED_MASK 0x0F +#define COGL_UNPREMULT_MASK 0x7F + +/** + * CoglPixelFormat: + * @COGL_PIXEL_FORMAT_ANY: + * @COGL_PIXEL_FORMAT_A_8: + * @COGL_PIXEL_FORMAT_RGB_888: + * @COGL_PIXEL_FORMAT_BGR_888: + * @COGL_PIXEL_FORMAT_RGBA_8888: + * @COGL_PIXEL_FORMAT_BGRA_8888: + * @COGL_PIXEL_FORMAT_ARGB_8888: + * @COGL_PIXEL_FORMAT_ABGR_8888: + * @COGL_PIXEL_FORMAT_RGBA_8888_PRE: + * @COGL_PIXEL_FORMAT_BGRA_8888_PRE: + * @COGL_PIXEL_FORMAT_ARGB_8888_PRE: + * @COGL_PIXEL_FORMAT_ABGR_8888_PRE: + * @COGL_PIXEL_FORMAT_RGB_565: + * @COGL_PIXEL_FORMAT_RGBA_4444: + * @COGL_PIXEL_FORMAT_RGBA_5551: + * @COGL_PIXEL_FORMAT_RGBA_4444_PRE: + * @COGL_PIXEL_FORMAT_RGBA_5551_PRE: + * @COGL_PIXEL_FORMAT_YUV: + * @COGL_PIXEL_FORMAT_G_8: + * + * Pixel formats used by COGL. + */ +typedef enum +{ + COGL_PIXEL_FORMAT_ANY = 0, + COGL_PIXEL_FORMAT_A_8 = 1 | COGL_A_BIT, + + COGL_PIXEL_FORMAT_RGB_888 = COGL_PIXEL_FORMAT_24, + COGL_PIXEL_FORMAT_BGR_888 = (COGL_PIXEL_FORMAT_24 | COGL_BGR_BIT), + + COGL_PIXEL_FORMAT_RGBA_8888 = COGL_PIXEL_FORMAT_32 | COGL_A_BIT, + COGL_PIXEL_FORMAT_BGRA_8888 = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_BGR_BIT), + COGL_PIXEL_FORMAT_ARGB_8888 = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_AFIRST_BIT), + COGL_PIXEL_FORMAT_ABGR_8888 = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT), + + COGL_PIXEL_FORMAT_RGBA_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT), + COGL_PIXEL_FORMAT_BGRA_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_BGR_BIT), + COGL_PIXEL_FORMAT_ARGB_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_AFIRST_BIT), + COGL_PIXEL_FORMAT_ABGR_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT), + + COGL_PIXEL_FORMAT_RGB_565 = 4, + COGL_PIXEL_FORMAT_RGBA_4444 = 5 | COGL_A_BIT, + COGL_PIXEL_FORMAT_RGBA_5551 = 6 | COGL_A_BIT, + + COGL_PIXEL_FORMAT_RGBA_4444_PRE = (COGL_PIXEL_FORMAT_RGBA_4444 | COGL_A_BIT | COGL_PREMULT_BIT), + COGL_PIXEL_FORMAT_RGBA_5551_PRE = (COGL_PIXEL_FORMAT_RGBA_5551 | COGL_A_BIT | COGL_PREMULT_BIT), + + COGL_PIXEL_FORMAT_YUV = 7, + + COGL_PIXEL_FORMAT_G_8 = 8 + +} CoglPixelFormat; + +/** + * CoglFeatureFlags: + * @COGL_FEATURE_TEXTURE_RECTANGLE: + * @COGL_FEATURE_TEXTURE_NPOT: + * @COGL_FEATURE_TEXTURE_YUV: + * @COGL_FEATURE_TEXTURE_READ_PIXELS: + * @COGL_FEATURE_SHADERS_GLSL: + * @COGL_FEATURE_OFFSCREEN: + * @COGL_FEATURE_OFFSCREEN_MULTISAMPLE: + * @COGL_FEATURE_OFFSCREEN_BLIT: + * @COGL_FEATURE_FOUR_CLIP_PLANES: + * @COGL_FEATURE_STENCIL_BUFFER: + * + * Flags for the supported features. + */ +typedef enum +{ + COGL_FEATURE_TEXTURE_RECTANGLE = (1 << 1), + COGL_FEATURE_TEXTURE_NPOT = (1 << 2), + COGL_FEATURE_TEXTURE_YUV = (1 << 3), + COGL_FEATURE_TEXTURE_READ_PIXELS = (1 << 4), + COGL_FEATURE_SHADERS_GLSL = (1 << 5), + COGL_FEATURE_OFFSCREEN = (1 << 6), + COGL_FEATURE_OFFSCREEN_MULTISAMPLE = (1 << 7), + COGL_FEATURE_OFFSCREEN_BLIT = (1 << 8), + COGL_FEATURE_FOUR_CLIP_PLANES = (1 << 9), + COGL_FEATURE_STENCIL_BUFFER = (1 << 10) + +} CoglFeatureFlags; + +/** + * CoglBufferTarget: + * @COGL_WINDOW_BUFFER: + * @COGL_MASK_BUFFER: + * @COGL_OFFSCREEN_BUFFER: + * + * FIXME + */ +typedef enum +{ + COGL_WINDOW_BUFFER = (1 << 1), + COGL_MASK_BUFFER = (1 << 2), + COGL_OFFSCREEN_BUFFER = (1 << 3) + +} CoglBufferTarget; + +/** + * CoglTextureVertex: + * @x: Model x-coordinate + * @y: Model y-coordinate + * @z: Model z-coordinate + * @tx: Texture x-coordinate + * @ty: Texture y-coordinate + * @color: The color to use at this vertex. This is ignored if + * @use_color is %FALSE when calling cogl_texture_polygon(). + * + * Used to specify vertex information when calling cogl_texture_polygon(). + */ +struct _CoglTextureVertex +{ + ClutterFixed x, y, z; + ClutterFixed tx, ty; + ClutterColor color; +}; + +typedef struct _CoglTextureVertex CoglTextureVertex; + +/* Context manipulation */ + +gboolean +cogl_create_context (void); + +void +cogl_destroy_context (void); + +/* Misc */ +#define COGL_INVALID_HANDLE NULL + +typedef gpointer CoglHandle; + +typedef void (* CoglFuncPtr) (void); + +/** + * cogl_get_features: + * + * Returns all of the features supported by COGL. + * + * Return value: A logical OR of all the supported COGL features. + * + * Since: 0.8 + */ +ClutterFeatureFlags +cogl_get_features (void); + +/** + * cogl_features_available: + * @features: A bitmask of features to check for + * + * Checks whether the given COGL features are available. Multiple + * features can be checked for by or-ing them together with the '|' + * operator. %TRUE is only returned if all of the requested features + * are available. + * + * Return value: %TRUE if the features are available, %FALSE otherwise. + */ +gboolean +cogl_features_available (CoglFeatureFlags features); + +/** + * cogl_get_proc_address: + * @name: the name of the function. + * + * Gets a pointer to a given GL or GL ES extension function. This acts + * as a wrapper around glXGetProcAddress() or whatever is the + * appropriate function for the current backend. + * + * Return value: a pointer to the requested function or %NULL if the + * function is not available. + */ +CoglFuncPtr +cogl_get_proc_address (const gchar* name); + +gboolean +cogl_check_extension (const gchar *name, + const gchar *ext); + +/** + * cogl_get_bitmasks: + * @red: Return location for the number of red bits or %NULL + * @green: Return location for the number of green bits or %NULL + * @blue: Return location for the number of blue bits or %NULL + * @alpha: Return location for the number of alpha bits or %NULL + * + * Gets the number of bitplanes used for each of the color components + * in the color buffer. Pass %NULL for any of the arguments if the + * value is not required. + */ +void +cogl_get_bitmasks (gint *red, + gint *green, + gint *blue, + gint *alpha); + +void +cogl_perspective (ClutterFixed fovy, + ClutterFixed aspect, + ClutterFixed zNear, + ClutterFixed zFar); + +/** + * cogl_setup_viewport: + * @width: Width of the viewport + * @height: Height of the viewport + * @fovy: Field of view angle in degrees + * @aspect: Aspect ratio to determine the field of view along the x-axis + * @z_near: Nearest visible point along the z-axis + * @z_far: Furthest visible point along the z-axis + * + * Replaces the current viewport and projection matrix with the given + * values. The viewport is placed at the top left corner of the window + * with the given width and height. The projection matrix is replaced + * with one that has a viewing angle of @fovy along the y-axis and a + * view scaled according to @aspect along the x-axis. The view is + * clipped according to @z_near and @z_far on the z-axis. + */ +void +cogl_setup_viewport (guint width, + guint height, + ClutterFixed fovy, + ClutterFixed aspect, + ClutterFixed z_near, + ClutterFixed z_far); + +/** + * cogl_push_matrix: + * + * Store the current model-view matrix on the matrix stack. The matrix + * can later be restored with cogl_pop_matrix(). + */ +void +cogl_push_matrix (void); + +/** + * cogl_pop_matrix: + * + * Restore the current model-view matrix from the matrix stack. + */ +void +cogl_pop_matrix (void); + +/** + * cogl_scale: + * @x: Amount to scale along the x-axis + * @y: Amount to scale along the y-axis + * + * Multiplies the current model-view matrix by one that scales the x + * and y axes by the given values. + */ +void +cogl_scale (ClutterFixed x, ClutterFixed y); + +/** + * cogl_translatex: + * @x: Distance to translate along the x-axis + * @y: Distance to translate along the y-axis + * @z: Distance to translate along the z-axis + * + * Multiplies the current model-view matrix by one that translates the + * model along all three axes according to the given values. + */ +void +cogl_translatex (ClutterFixed x, ClutterFixed y, ClutterFixed z); + +/** + * cogl_translate: + * @x: Distance to translate along the x-axis + * @y: Distance to translate along the y-axis + * @z: Distance to translate along the z-axis + * + * Integer version of cogl_translatex(). Multiplies the current + * model-view matrix by one that translates the model along all three + * axes according to the given values. + */ +void +cogl_translate (gint x, gint y, gint z); + +/** + * cogl_rotatex: + * @angle: Angle in degrees to rotate. + * @x: X-component of vertex to rotate around. + * @y: Y-component of vertex to rotate around. + * @z: Z-component of vertex to rotate around. + * + * Multiplies the current model-view matrix by one that rotates the + * model around the vertex specified by @x, @y and @z. The rotation + * follows the right-hand thumb rule so for example rotating by 10 + * degrees about the vertex (0, 0, 1) causes a small counter-clockwise + * rotation. + */ +void +cogl_rotatex (ClutterFixed angle, gint x, gint y, gint z); + +/** + * cogl_rotate: + * @angle: Angle in degrees to rotate. + * @x: X-component of vertex to rotate around. + * @y: Y-component of vertex to rotate around. + * @z: Z-component of vertex to rotate around. + * + * Integer version of cogl_rotatex(). Multiplies the current + * model-view matrix by one that rotates the model around the vertex + * specified by @x, @y and @z. + */ +void +cogl_rotate (gint angle, gint x, gint y, gint z); + +/** + * cogl_get_modelview_matrix: + * @m: pointer to a 4x4 array of #ClutterFixed<!-- -->s to receive the matrix + * + * Stores the current model-view matrix in @m. The matrix is in + * column-major order. + */ +void +cogl_get_modelview_matrix (ClutterFixed m[16]); + +/** + * cogl_get_projection_matrix: + * @m: pointer to a 4x4 array of #ClutterFixed<!-- -->s to receive the matrix + * + * Stores the current projection matrix in @m. The matrix is in + * column-major order. + */ +void +cogl_get_projection_matrix (ClutterFixed m[16]); + +/** + * cogl_get_viewport: + * @v: pointer to a 4 element array of #ClutterFixed<!-- -->s to + * receive the viewport dimensions. + * + * Stores the current viewport in @v. @v[0] and @v[1] get the x and y + * position of the viewport and @v[2] and @v[3] get the width and + * height. + */ +void +cogl_get_viewport (ClutterFixed v[4]); + +/** + * cogl_clip_set: + * @x_offset: left edge of the clip rectangle + * @y_offset: top edge of the clip rectangle + * @width: width of the clip rectangle + * @height: height of the clip rectangle + * + * Specifies a rectangular clipping area for all subsequent drawing + * operations. Any drawing commands that extend outside the rectangle + * will be clipped so that only the portion inside the rectangle will + * be displayed. The rectangle dimensions are transformed by the + * current model-view matrix. + */ +void +cogl_clip_set (ClutterFixed x_offset, + ClutterFixed y_offset, + ClutterFixed width, + ClutterFixed height); + +/** + * cogl_clip_unset: + * + * Removes the current clipping rectangle so that all drawing + * operations extend to full size of the viewport again. + */ +void +cogl_clip_unset (void); + +/** + * cogl_enable_depth_test: + * @setting: %TRUE to enable depth testing or %FALSE to disable. + * + * Sets whether depth testing is enabled. If it is disabled then the + * order that actors are layered on the screen depends solely on the + * order specified using clutter_actor_raise() and + * clutter_actor_lower(), otherwise it will also take into account the + * actor's depth. Depth testing is disabled by default. + */ +void +cogl_enable_depth_test (gboolean setting); + +void +cogl_alpha_func (COGLenum func, + ClutterFixed ref); + +/** + * cogl_fog_set: + * @fog_color: The color of the fog + * @density: Ignored + * @z_near: Position along z-axis where no fogging should be applied + * @z_far: Position along z-axes where full fogging should be applied + * + * Enables fogging. Fogging causes vertices that are further away from + * the eye to be rendered with a different color. The color is + * linearly interpolated so that vertices at @z_near are drawn fully + * with their original color and vertices at @z_far are drawn fully + * with @fog_color. Fogging will remain enabled until the next call to + * cogl_paint_init(). + */ +void +cogl_fog_set (const ClutterColor *fog_color, + ClutterFixed density, + ClutterFixed z_near, + ClutterFixed z_far); + +/** + * cogl_paint_init: + * @color: Background color to clear to + * + * Clears the color buffer to @color. The depth buffer and stencil + * buffers are also cleared and fogging and lighting are disabled. + */ +void +cogl_paint_init (const ClutterColor *color); + +/* Textures api */ + +CoglHandle +cogl_texture_new_with_size (guint width, + guint height, + gint max_waste, + CoglPixelFormat internal_format); + +CoglHandle +cogl_texture_new_from_file (const gchar *filename, + gint max_waste, + CoglPixelFormat internal_format, + GError **error); + +CoglHandle +cogl_texture_new_from_data (guint width, + guint height, + gint max_waste, + CoglPixelFormat format, + CoglPixelFormat internal_format, + guint rowstride, + const guchar *data); + +CoglHandle +cogl_texture_new_from_foreign (GLuint gl_handle, + GLenum gl_target, + GLuint width, + GLuint height, + GLuint x_pot_waste, + GLuint y_pot_waste, + CoglPixelFormat format); + +/** + * cogl_is_texture: + * @handle: A CoglHandle + * + * Gets whether the given handle references an existing texture object. + * + * Return value: %TRUE if the handle references a texture, + * %FALSE otherwise + */ +gboolean +cogl_is_texture (CoglHandle handle); + +guint +cogl_texture_get_width (CoglHandle handle); + +guint +cogl_texture_get_height (CoglHandle handle); + +CoglPixelFormat +cogl_texture_get_format (CoglHandle handle); + +guint +cogl_texture_get_rowstride (CoglHandle handle); + +gint +cogl_texture_get_max_waste (CoglHandle handle); + +COGLenum +cogl_texture_get_min_filter (CoglHandle handle); + +COGLenum +cogl_texture_get_mag_filter (CoglHandle handle); + +gboolean +cogl_texture_is_sliced (CoglHandle handle); + +gboolean +cogl_texture_get_gl_texture (CoglHandle handle, + GLuint *out_gl_handle, + GLenum *out_gl_target); + +gint +cogl_texture_get_data (CoglHandle handle, + CoglPixelFormat format, + guint rowstride, + guchar *data); + +void +cogl_texture_set_filters (CoglHandle handle, + COGLenum min_filter, + COGLenum mag_filter); + +gboolean +cogl_texture_set_region (CoglHandle handle, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + guint dst_width, + guint dst_height, + gint width, + gint height, + CoglPixelFormat format, + guint rowstride, + const guchar *data); + +CoglHandle +cogl_texture_ref (CoglHandle handle); + +void +cogl_texture_unref (CoglHandle handle); + +void +cogl_texture_rectangle (CoglHandle handle, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2); + +/** + * cogl_texture_polygon: + * @handle: A CoglHandle for a texture + * @n_vertices: The length of the vertices array + * @vertices: An array of #CoglTextureVertex structs + * @use_color: %TRUE if the color member of #CoglTextureVertex should be used + * + * Draws a polygon from a texture with the given model and texture + * coordinates. This can be used to draw arbitrary shapes textured + * with a COGL texture. If @use_color is %TRUE then the current COGL + * color will be changed for each vertex using the value specified in + * the color member of #CoglTextureVertex. This can be used for + * example to make the texture fade out by setting the alpha value of + * the color. + * + * All of the texture coordinates must be in the range [0,1] and + * repeating the texture is not supported. + * + * Because of the way this function is implemented it will currently + * only work if either the texture is not sliced or the backend is not + * OpenGL ES and the minifying and magnifying functions are both set + * to CGL_NEAREST. + */ +void +cogl_texture_polygon (CoglHandle handle, + guint n_vertices, + CoglTextureVertex *vertices, + gboolean use_color); + +/* Primitives API */ + +void +cogl_color (const ClutterColor *color); + +void +cogl_fast_fill_rectangle (gint x, + gint y, + guint width, + guint height); + +void +cogl_fast_fill_rectanglex (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height); + +void +cogl_fast_fill_trapezoid (gint y1, + gint x11, + gint x21, + gint y2, + gint x12, + gint x22); + +void +cogl_fast_fill_trapezoidx (ClutterFixed y1, + ClutterFixed x11, + ClutterFixed x21, + ClutterFixed y2, + ClutterFixed x12, + ClutterFixed x22); + +void +cogl_fill (); + +void +cogl_stroke (); + +void +cogl_path_move_to (ClutterFixed x, + ClutterFixed y); + +void +cogl_path_move_to_rel (ClutterFixed x, + ClutterFixed y); + +void +cogl_path_line_to (ClutterFixed x, + ClutterFixed y); + +void +cogl_path_line_to_rel (ClutterFixed x, + ClutterFixed y); + +void +cogl_path_h_line_to (ClutterFixed x); + +void +cogl_path_v_line_to (ClutterFixed y); + +void +cogl_path_h_line_to_rel (ClutterFixed x); + +void +cogl_path_v_line_to_rel (ClutterFixed y); + +void +cogl_path_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step); + +void +cogl_path_arc_rel (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step); + +void +cogl_path_bezier2_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2); + +void +cogl_path_bezier2_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2); + +void +cogl_path_bezier3_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3); + +void +cogl_path_bezier3_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3); + +void +cogl_path_close (); + +void +cogl_line (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2); + +void +cogl_polyline (ClutterFixed *coords, + gint num_points); + +void +cogl_polygon (ClutterFixed *coords, + gint num_points); + +void +cogl_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height); + +void +cogl_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step); + +void +cogl_ellipse (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_step); + +void +cogl_round_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height, + ClutterFixed radius, + ClutterAngle arc_step); + + +COGLhandle +cogl_create_shader (COGLenum shaderType); + +void +cogl_shader_destroy (COGLhandle handle); + + +void +cogl_shader_source (COGLhandle shader, + const gchar *source); +void +cogl_shader_compile (COGLhandle shader_handle); + +void +cogl_shader_get_info_log (COGLhandle handle, + guint size, + gchar *buffer); + +void +cogl_shader_get_parameteriv (COGLhandle handle, + COGLenum pname, + COGLint *dest); + + +COGLhandle +cogl_create_program (void); + +void +cogl_program_destroy (COGLhandle handle); + +void +cogl_program_attach_shader (COGLhandle program_handle, + COGLhandle shader_handle); + +/* 0 to use none */ +void +cogl_program_link (COGLhandle program_handle); + +void +cogl_program_use (COGLhandle program_handle); + +COGLint +cogl_program_get_uniform_location (COGLhandle program_int, + const gchar *uniform_name); + + +void +cogl_program_uniform_1f (COGLint uniform_no, + gfloat value); + +/* Offscreen api */ + +CoglHandle +cogl_offscreen_new_to_texture (CoglHandle texhandle); + +CoglHandle +cogl_offscreen_new_multisample (); + +CoglHandle +cogl_offscreen_ref (CoglHandle handle); + +void +cogl_offscreen_unref (CoglHandle handle); + +void +cogl_offscreen_blit (CoglHandle src_buffer, + CoglHandle dst_buffer); + +void +cogl_offscreen_blit_region (CoglHandle src_buffer, + CoglHandle dst_buffer, + int src_x, + int src_y, + int src_w, + int src_h, + int dst_x, + int dst_y, + int dst_w, + int dst_h); + +void +cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen); + +G_END_DECLS + +#endif /* __COGL_H__ */ diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 00000000..b6dca2df --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,23 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/clutter \ + -I$(top_srcdir)/clutter/cogl \ + -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \ + -I$(top_builddir)/clutter \ + -I$(top_builddir)/clutter/cogl \ + $(CLUTTER_CFLAGS) \ + $(CLUTTER_DEBUG_CFLAGS) \ + $(GCC_FLAGS) + +LDADD = $(CLUTTER_LIBS) + +noinst_LTLIBRARIES = libclutter-cogl-common.la +EXTRA_DIST = stb_image.c + +libclutter_cogl_common_la_SOURCES = \ + cogl-util.h \ + cogl-bitmap.h \ + cogl-util.c \ + cogl-bitmap.c \ + cogl-bitmap-fallback.c \ + cogl-bitmap-pixbuf.c diff --git a/common/cogl-bitmap-fallback.c b/common/cogl-bitmap-fallback.c new file mode 100644 index 00000000..21a55846 --- /dev/null +++ b/common/cogl-bitmap-fallback.c @@ -0,0 +1,369 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-bitmap.h" + +#include <string.h> + +/* TO rgba */ + +inline static void +_cogl_g_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[0]; + dst[1] = src[0]; + dst[2] = src[0]; + dst[3] = 255; +} + +inline static void +_cogl_rgb_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = 255; +} + +inline static void +_cogl_bgr_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = 255; +} + +inline static void +_cogl_bgra_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = src[3]; +} + +inline static void +_cogl_argb_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[1]; + dst[1] = src[2]; + dst[2] = src[3]; + dst[3] = src[0]; +} + +inline static void +_cogl_abgr_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +} + +inline static void +_cogl_rgba_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; +} + +/* FROM rgba */ + +inline static void +_cogl_rgba_to_g (const guchar *src, guchar *dst) +{ + dst[0] = (src[0] + src[1] + src[2]) / 3; +} + +inline static void +_cogl_rgba_to_rgb (const guchar *src, guchar *dst) +{ + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; +} + +inline static void +_cogl_rgba_to_bgr (const guchar *src, guchar *dst) +{ + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; +} + +inline static void +_cogl_rgba_to_bgra (const guchar *src, guchar *dst) +{ + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = src[3]; +} + +inline static void +_cogl_rgba_to_argb (const guchar *src, guchar *dst) +{ + dst[0] = src[3]; + dst[1] = src[0]; + dst[2] = src[1]; + dst[3] = src[2]; +} + +inline static void +_cogl_rgba_to_abgr (const guchar *src, guchar *dst) +{ + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +} + +/* (Un)Premultiplication */ + +inline static void +_cogl_unpremult_alpha_0 (const guchar *src, guchar *dst) +{ + dst[0] = 0; + dst[1] = 0; + dst[2] = 0; + dst[3] = 0; +} + +inline static void +_cogl_unpremult_alpha_last (const guchar *src, guchar *dst) +{ + guchar alpha = src[3]; + + dst[0] = (((src[0] >> 16) & 0xff) * 255 ) / alpha; + dst[1] = (((src[1] >> 8) & 0xff) * 255 ) / alpha; + dst[2] = (((src[2] >> 0) & 0xff) * 255 ) / alpha; + dst[3] = alpha; +} + +inline static void +_cogl_unpremult_alpha_first (const guchar *src, guchar *dst) +{ + guchar alpha = src[0]; + + dst[0] = alpha; + dst[1] = (((src[1] >> 16) & 0xff) * 255 ) / alpha; + dst[2] = (((src[2] >> 8) & 0xff) * 255 ) / alpha; + dst[3] = (((src[3] >> 0) & 0xff) * 255 ) / alpha; +} + +gboolean +_cogl_bitmap_fallback_can_convert (CoglPixelFormat src, CoglPixelFormat dst) +{ + if (src == dst) + return FALSE; + + switch (src & COGL_UNORDERED_MASK) + { + case COGL_PIXEL_FORMAT_G_8: + case COGL_PIXEL_FORMAT_24: + case COGL_PIXEL_FORMAT_32: + + if ((dst & COGL_UNORDERED_MASK) != COGL_PIXEL_FORMAT_24 && + (dst & COGL_UNORDERED_MASK) != COGL_PIXEL_FORMAT_32 && + (dst & COGL_UNORDERED_MASK) != COGL_PIXEL_FORMAT_G_8) + return FALSE; + break; + + default: + return FALSE; + } + + return TRUE; +} + +gboolean +_cogl_bitmap_fallback_can_unpremult (CoglPixelFormat format) +{ + return ((format & COGL_UNORDERED_MASK) == COGL_PIXEL_FORMAT_32); +} + +gboolean +_cogl_bitmap_fallback_convert (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format) +{ + guchar *src; + guchar *dst; + gint src_bpp; + gint dst_bpp; + gint x,y; + guchar temp_rgba[4] = {0,0,0,0}; + + /* Make sure conversion supported */ + if (!_cogl_bitmap_fallback_can_convert (bmp->format, dst_format)) + return FALSE; + + src_bpp = _cogl_get_format_bpp (bmp->format); + dst_bpp = _cogl_get_format_bpp (dst_format); + + /* Initialize destination bitmap */ + *dst_bmp = *bmp; + dst_bmp->rowstride = sizeof(guchar) * dst_bpp * dst_bmp->width; + dst_bmp->format = ((bmp->format & COGL_PREMULT_BIT) | + (dst_format & COGL_UNPREMULT_MASK)); + + /* Allocate a new buffer to hold converted data */ + dst_bmp->data = g_malloc (sizeof(guchar) + * dst_bmp->height + * dst_bmp->rowstride); + + /* FIXME: Optimize */ + for (y = 0; y < bmp->height; y++) + { + src = (guchar*)bmp->data + y * bmp->rowstride; + dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride; + + for (x = 0; x < bmp->width; x++) + { + /* FIXME: Would be nice to at least remove this inner + * branching, but not sure it can be done without + * rewriting of the whole loop */ + switch (bmp->format & COGL_UNPREMULT_MASK) + { + case COGL_PIXEL_FORMAT_G_8: + _cogl_g_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_RGB_888: + _cogl_rgb_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_BGR_888: + _cogl_bgr_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_RGBA_8888: + _cogl_rgba_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_BGRA_8888: + _cogl_bgra_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_ARGB_8888: + _cogl_argb_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_ABGR_8888: + _cogl_abgr_to_rgba (src, temp_rgba); break; + default: + break; + } + + switch (dst_format & COGL_UNPREMULT_MASK) + { + case COGL_PIXEL_FORMAT_G_8: + _cogl_rgba_to_g (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_RGB_888: + _cogl_rgba_to_rgb (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_BGR_888: + _cogl_rgba_to_bgr (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_RGBA_8888: + _cogl_rgba_to_rgba (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_BGRA_8888: + _cogl_rgba_to_bgra (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_ARGB_8888: + _cogl_rgba_to_argb (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_ABGR_8888: + _cogl_rgba_to_abgr (temp_rgba, dst); break; + default: + break; + } + + src += src_bpp; + dst += dst_bpp; + } + } + + return TRUE; +} + +gboolean +_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp) +{ + guchar *src; + guchar *dst; + gint bpp; + gint x,y; + + /* Make sure format supported for un-premultiplication */ + if (!_cogl_bitmap_fallback_can_unpremult (bmp->format)) + return FALSE; + + bpp = _cogl_get_format_bpp (bmp->format); + + /* Initialize destination bitmap */ + *dst_bmp = *bmp; + dst_bmp->format = (bmp->format & COGL_UNPREMULT_MASK); + + /* Allocate a new buffer to hold converted data */ + dst_bmp->data = g_malloc (sizeof(guchar) + * dst_bmp->height + * dst_bmp->rowstride); + + /* FIXME: Optimize */ + for (y = 0; y < bmp->height; y++) + { + src = (guchar*)bmp->data + y * bmp->rowstride; + dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride; + + for (x = 0; x < bmp->width; x++) + { + /* FIXME: Would be nice to at least remove this inner + * branching, but not sure it can be done without + * rewriting of the whole loop */ + if (bmp->format & COGL_AFIRST_BIT) + { + if (src[0] == 0) + _cogl_unpremult_alpha_0 (src, dst); + else + _cogl_unpremult_alpha_first (src, dst); + } + else + { + if (src[3] == 0) + _cogl_unpremult_alpha_0 (src, dst); + else + _cogl_unpremult_alpha_last (src, dst); + } + + src += bpp; + dst += bpp; + } + } + + return TRUE; +} + +gboolean +_cogl_bitmap_fallback_from_file (CoglBitmap *bmp, + const gchar *filename) +{ + /* FIXME: use jpeglib, libpng, etc. manually maybe */ + return FALSE; +} diff --git a/common/cogl-bitmap-pixbuf.c b/common/cogl-bitmap-pixbuf.c new file mode 100644 index 00000000..92378860 --- /dev/null +++ b/common/cogl-bitmap-pixbuf.c @@ -0,0 +1,197 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-bitmap.h" + +#include <string.h> + +#ifdef USE_GDKPIXBUF +#include <gdk-pixbuf/gdk-pixbuf.h> +#endif + +gboolean +_cogl_bitmap_can_convert (CoglPixelFormat src, CoglPixelFormat dst) +{ + return FALSE; +} + +gboolean +_cogl_bitmap_can_unpremult (CoglPixelFormat format) +{ + return FALSE; +} + +gboolean +_cogl_bitmap_convert (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format) +{ + return FALSE; +} + +gboolean +_cogl_bitmap_unpremult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp) +{ + return FALSE; +} + +#ifdef USE_GDKPIXBUF + +gboolean +_cogl_bitmap_from_file (CoglBitmap *bmp, + const gchar *filename, + GError **error) +{ + GdkPixbuf *pixbuf; + gboolean has_alpha; + GdkColorspace color_space; + CoglPixelFormat pixel_format; + gint width; + gint height; + gint rowstride; + gint bits_per_sample; + gint n_channels; + gint last_row_size; + guchar *pixels; + guchar *out_data; + guchar *out; + gint r; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (bmp == NULL) return FALSE; + + /* Load from file using GdkPixbuf */ + pixbuf = gdk_pixbuf_new_from_file (filename, error); + if (pixbuf == NULL) return FALSE; + + /* Get pixbuf properties */ + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + color_space = gdk_pixbuf_get_colorspace (pixbuf); + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + bits_per_sample = gdk_pixbuf_get_bits_per_sample (pixbuf); + n_channels = gdk_pixbuf_get_n_channels (pixbuf); + + /* The docs say this is the right way */ + last_row_size = width * ((n_channels * bits_per_sample + 7) / 8); + + /* According to current docs this should be true and so + * the translation to cogl pixel format below valid */ + g_assert (bits_per_sample == 8); + + if (has_alpha) + g_assert (n_channels == 4); + else + g_assert (n_channels == 3); + + /* Translate to cogl pixel format */ + switch (color_space) + { + case GDK_COLORSPACE_RGB: + /* The only format supported by GdkPixbuf so far */ + pixel_format = has_alpha ? + COGL_PIXEL_FORMAT_RGBA_8888 : + COGL_PIXEL_FORMAT_RGB_888; + break; + + default: + /* Ouch, spec changed! */ + g_object_unref (pixbuf); + return FALSE; + } + + /* FIXME: Any way to destroy pixbuf but retain pixel data? */ + + pixels = gdk_pixbuf_get_pixels (pixbuf); + out_data = (guchar*) g_malloc ((height - 1) * rowstride + last_row_size); + out = out_data; + + /* Copy up to last row */ + for (r = 0; r < height-1; ++r) + { + memcpy (out, pixels, rowstride); + pixels += rowstride; + out += rowstride; + } + + /* Copy last row */ + memcpy (out, pixels, last_row_size); + + /* Destroy GdkPixbuf object */ + g_object_unref (pixbuf); + + /* Store bitmap info */ + bmp->data = out_data; + bmp->format = pixel_format; + bmp->width = width; + bmp->height = height; + bmp->rowstride = rowstride; + + return TRUE; +} + +#else + +#include "stb_image.c" + +gboolean +_cogl_bitmap_from_file (CoglBitmap *bmp, + const gchar *filename, + GError **error) +{ + gint stb_pixel_format; + gint width; + gint height; + guchar *pixels; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (bmp == NULL) return FALSE; + + /* Load from file using stb */ + pixels = stbi_load (filename, &width, &height, &stb_pixel_format, STBI_rgb_alpha); + if (pixels == NULL) return FALSE; + + /* Store bitmap info */ + bmp->data = pixels; + bmp->format = COGL_PIXEL_FORMAT_RGBA_8888; + bmp->width = width; + bmp->height = height; + bmp->rowstride = width * 4; + + g_print ("we successfully used stb_image to load %s\n", filename); + + return TRUE; +} +#endif diff --git a/common/cogl-bitmap.c b/common/cogl-bitmap.c new file mode 100644 index 00000000..39341e0b --- /dev/null +++ b/common/cogl-bitmap.c @@ -0,0 +1,150 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-bitmap.h" + +#include <string.h> + +gint +_cogl_get_format_bpp (CoglPixelFormat format) +{ + gint bpp_lut[] = { + 0, /* invalid */ + 1, /* A_8 */ + 3, /* 888 */ + 4, /* 8888 */ + 2, /* 565 */ + 2, /* 4444 */ + 2, /* 5551 */ + 2, /* YUV */ + 1 /* G_8 */ + }; + + return bpp_lut [format & COGL_UNORDERED_MASK]; +} + +gboolean +_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format) +{ + CoglBitmap tmp_bmp = *bmp; + CoglBitmap new_bmp = *bmp; + gboolean new_bmp_owner = FALSE; + + /* Is base format different (not considering premult status)? */ + if ((bmp->format & COGL_UNPREMULT_MASK) != + (dst_format & COGL_UNPREMULT_MASK)) + { + /* Try converting using imaging library */ + if (!_cogl_bitmap_convert (&new_bmp, &tmp_bmp, dst_format)) + { + /* ... or try fallback */ + if (!_cogl_bitmap_fallback_convert (&new_bmp, &tmp_bmp, dst_format)) + return FALSE; + } + + /* Update bitmap with new data */ + new_bmp = tmp_bmp; + new_bmp_owner = TRUE; + } + + /* Do we need to unpremultiply */ + if ((bmp->format & COGL_PREMULT_BIT) == 0 && + (dst_format & COGL_PREMULT_BIT) > 0) + { + /* Try unpremultiplying using imaging library */ + if (!_cogl_bitmap_unpremult (&new_bmp, &tmp_bmp)) + { + /* ... or try fallback */ + if (!_cogl_bitmap_fallback_unpremult (&new_bmp, &tmp_bmp)) + { + if (new_bmp_owner) + g_free (new_bmp.data); + + return FALSE; + } + } + + /* Update bitmap with new data */ + if (new_bmp_owner) + g_free (new_bmp.data); + + new_bmp = tmp_bmp; + new_bmp_owner = TRUE; + } + + /* Do we need to premultiply */ + if ((bmp->format & COGL_PREMULT_BIT) > 0 && + (dst_format & COGL_PREMULT_BIT) == 0) + { + /* FIXME: implement premultiplication */ + if (new_bmp_owner) + g_free (new_bmp.data); + + return FALSE; + } + + /* Output new bitmap info */ + *dst_bmp = new_bmp; + + return TRUE; +} + +void +_cogl_bitmap_copy_subregion (CoglBitmap *src, + CoglBitmap *dst, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + gint width, + gint height) +{ + guchar *srcdata; + guchar *dstdata; + gint bpp; + gint line; + + /* Intended only for fast copies when format is equal! */ + g_assert (src->format == dst->format); + bpp = _cogl_get_format_bpp (src->format); + + srcdata = src->data + src_y * src->rowstride + src_x * bpp; + dstdata = dst->data + dst_y * dst->rowstride + dst_x * bpp; + + for (line=0; line<height; ++line) + { + memcpy (dstdata, srcdata, width * bpp); + srcdata += src->rowstride; + dstdata += dst->rowstride; + } +} diff --git a/common/cogl-bitmap.h b/common/cogl-bitmap.h new file mode 100644 index 00000000..ca9f8a9c --- /dev/null +++ b/common/cogl-bitmap.h @@ -0,0 +1,95 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_BITMAP_H +#define __COGL_BITMAP_H + +#include <glib.h> + +typedef struct _CoglBitmap CoglBitmap; + +struct _CoglBitmap +{ + guchar *data; + CoglPixelFormat format; + gint width; + gint height; + gint rowstride; +}; + +gboolean +_cogl_bitmap_can_convert (CoglPixelFormat src, CoglPixelFormat dst); + +gboolean +_cogl_bitmap_fallback_can_convert (CoglPixelFormat src, CoglPixelFormat dst); + +gboolean +_cogl_bitmap_can_unpremult (CoglPixelFormat format); + +gboolean +_cogl_bitmap_fallback_can_unpremult (CoglPixelFormat format); + +gboolean +_cogl_bitmap_convert (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format); +gboolean +_cogl_bitmap_fallback_convert (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format); + +gboolean +_cogl_bitmap_unpremult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp); + +gboolean +_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp); + +gboolean +_cogl_bitmap_from_file (CoglBitmap *bmp, + const gchar *filename, + GError **error); + +gboolean +_cogl_bitmap_fallback_from_file (CoglBitmap *bmp, + const gchar *filename); + +gboolean +_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format); + +void +_cogl_bitmap_copy_subregion (CoglBitmap *src, + CoglBitmap *dst, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + gint width, + gint height); + +#endif /* __COGL_BITMAP_H */ diff --git a/common/cogl-util.c b/common/cogl-util.c new file mode 100644 index 00000000..b024e763 --- /dev/null +++ b/common/cogl-util.c @@ -0,0 +1,51 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" + +/** + * cogl_util_next_p2: + * @a: Value to get the next power + * + * Calculates the next power greater than @a. + * + * Return value: The next power after @a. + */ +int +cogl_util_next_p2 (int a) +{ + int rval=1; + + while(rval < a) + rval <<= 1; + + return rval; +} diff --git a/common/cogl-util.h b/common/cogl-util.h new file mode 100644 index 00000000..759da8cc --- /dev/null +++ b/common/cogl-util.h @@ -0,0 +1,32 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_UTIL_H +#define __COGL_UTIL_H + +int +cogl_util_next_p2 (int a); + +#endif /* __COGL_UTIL_H */ diff --git a/common/stb_image.c b/common/stb_image.c new file mode 100644 index 00000000..bd3e63b3 --- /dev/null +++ b/common/stb_image.c @@ -0,0 +1,3772 @@ +/* stbi-1.12 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
+ when you control the images you're loading
+
+ QUICK NOTES:
+ Primarily of interest to game developers and other people who can
+ avoid problematic images and only need the trivial interface
+
+ JPEG baseline (no JPEG progressive, no oddball channel decimations)
+ PNG non-interlaced
+ BMP non-1bpp, non-RLE
+ TGA (not sure what subset, if a subset)
+ PSD (composited view only, no extra channels)
+ HDR (radiance rgbE format)
+ writes BMP,TGA (define STBI_NO_WRITE to remove code)
+ decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code)
+ supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD)
+
+ TODO:
+ stbi_info_*
+
+ history:
+ 1.12 const qualifiers in the API
+ 1.11 Support installable IDCT, colorspace conversion routines
+ 1.10 Fixes for 64-bit (don't use "unsigned long")
+ optimized upsampling by Fabian "ryg" Giesen
+ 1.09 Fix format-conversion for PSD code (bad global variables!)
+ 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
+ 1.07 attempt to fix C++ warning/errors again
+ 1.06 attempt to fix C++ warning/errors again
+ 1.05 fix TGA loading to return correct *comp and use good luminance calc
+ 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
+ 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
+ 1.02 support for (subset of) HDR files, float interface for preferred access to them
+ 1.01 fix bug: possible bug in handling right-side up bmps... not sure
+ fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all
+ 1.00 interface to zlib that skips zlib header
+ 0.99 correct handling of alpha in palette
+ 0.98 TGA loader by lonesock; dynamically add loaders (untested)
+ 0.97 jpeg errors on too large a file; also catch another malloc failure
+ 0.96 fix detection of invalid v value - particleman@mollyrocket forum
+ 0.95 during header scan, seek to markers in case of padding
+ 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
+ 0.93 handle jpegtran output; verbose errors
+ 0.92 read 4,8,16,24,32-bit BMP files of several formats
+ 0.91 output 24-bit Windows 3.0 BMP files
+ 0.90 fix a few more warnings; bump version number to approach 1.0
+ 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
+ 0.60 fix compiling as c++
+ 0.59 fix warnings: merge Dave Moore's -Wall fixes
+ 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
+ 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less
+ than 16 available
+ 0.56 fix bug: zlib uncompressed mode len vs. nlen
+ 0.55 fix bug: restart_interval not initialized to 0
+ 0.54 allow NULL for 'int *comp'
+ 0.53 fix bug in png 3->4; speedup png decoding
+ 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
+ 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
+ on 'test' only check type, not whether we support this variant
+*/
+
+
+//// begin header file ////////////////////////////////////////////////////
+//
+// Limitations:
+// - no progressive/interlaced support (jpeg, png)
+// - 8-bit samples only (jpeg, png)
+// - not threadsafe
+// - channel subsampling of at most 2 in each dimension (jpeg)
+// - no delayed line count (jpeg) -- IJG doesn't support either
+//
+// Basic usage (see HDR discussion below):
+// int x,y,n;
+// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
+// // ... process data if not NULL ...
+// // ... x = width, y = height, n = # 8-bit components per pixel ...
+// // ... replace '0' with '1'..'4' to force that many components per pixel
+// stbi_image_free(data)
+//
+// Standard parameters:
+// int *x -- outputs image width in pixels
+// int *y -- outputs image height in pixels
+// int *comp -- outputs # of image components in image file
+// int req_comp -- if non-zero, # of image components requested in result
+//
+// The return value from an image loader is an 'unsigned char *' which points
+// to the pixel data. The pixel data consists of *y scanlines of *x pixels,
+// with each pixel consisting of N interleaved 8-bit components; the first
+// pixel pointed to is top-left-most in the image. There is no padding between
+// image scanlines or between pixels, regardless of format. The number of
+// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise.
+// If req_comp is non-zero, *comp has the number of components that _would_
+// have been output otherwise. E.g. if you set req_comp to 4, you will always
+// get RGBA output, but you can check *comp to easily see if it's opaque.
+//
+// An output image with N components has the following components interleaved
+// in this order in each pixel:
+//
+// N=#comp components
+// 1 grey
+// 2 grey, alpha
+// 3 red, green, blue
+// 4 red, green, blue, alpha
+//
+// If image loading fails for any reason, the return value will be NULL,
+// and *x, *y, *comp will be unchanged. The function stbi_failure_reason()
+// can be queried for an extremely brief, end-user unfriendly explanation
+// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid
+// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
+// more user-friendly ones.
+//
+// Paletted PNG and BMP images are automatically depalettized.
+//
+//
+// ===========================================================================
+//
+// HDR image support (disable by defining STBI_NO_HDR)
+//
+// stb_image now supports loading HDR images in general, and currently
+// the Radiance .HDR file format, although the support is provided
+// generically. You can still load any file through the existing interface;
+// if you attempt to load an HDR file, it will be automatically remapped to
+// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
+// both of these constants can be reconfigured through this interface:
+//
+// stbi_hdr_to_ldr_gamma(2.2f);
+// stbi_hdr_to_ldr_scale(1.0f);
+//
+// (note, do not use _inverse_ constants; stbi_image will invert them
+// appropriately).
+//
+// Additionally, there is a new, parallel interface for loading files as
+// (linear) floats to preserve the full dynamic range:
+//
+// float *data = stbi_loadf(filename, &x, &y, &n, 0);
+//
+// If you load LDR images through this interface, those images will
+// be promoted to floating point values, run through the inverse of
+// constants corresponding to the above:
+//
+// stbi_ldr_to_hdr_scale(1.0f);
+// stbi_ldr_to_hdr_gamma(2.2f);
+//
+// Finally, given a filename (or an open file or memory block--see header
+// file for details) containing image data, you can query for the "most
+// appropriate" interface to use (that is, whether the image is HDR or
+// not), using:
+//
+// stbi_is_hdr(char *filename);
+
+
+#ifndef STBI_NO_STDIO
+#include <stdio.h>
+#endif
+
+#ifndef STBI_NO_HDR
+#include <math.h> // ldexp
+#include <string.h> // strcmp
+#endif
+
+enum
+{
+ STBI_default = 0, // only used for req_comp
+
+ STBI_grey = 1,
+ STBI_grey_alpha = 2,
+ STBI_rgb = 3,
+ STBI_rgb_alpha = 4,
+};
+
+typedef unsigned char stbi_uc;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// WRITING API
+
+#if !defined(STBI_NO_WRITE) && !defined(STBI_NO_STDIO)
+// write a BMP/TGA file given tightly packed 'comp' channels (no padding, nor bmp-stride-padding)
+// (you must include the appropriate extension in the filename).
+// returns TRUE on success, FALSE if couldn't open file, error writing file
+extern int stbi_write_bmp (char const *filename, int x, int y, int comp, void *data);
+extern int stbi_write_tga (char const *filename, int x, int y, int comp, void *data);
+#endif
+
+// PRIMARY API - works on images of any type
+
+// load image by filename, open file, or memory buffer
+#ifndef STBI_NO_STDIO
+extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+// for stbi_load_from_file, file pointer is left pointing immediately after image
+
+#ifndef STBI_NO_HDR
+#ifndef STBI_NO_STDIO
+extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+
+extern void stbi_hdr_to_ldr_gamma(float gamma);
+extern void stbi_hdr_to_ldr_scale(float scale);
+
+extern void stbi_ldr_to_hdr_gamma(float gamma);
+extern void stbi_ldr_to_hdr_scale(float scale);
+
+#endif // STBI_NO_HDR
+
+// get a VERY brief reason for failure
+extern char *stbi_failure_reason (void);
+
+// free the loaded image -- this is just free()
+extern void stbi_image_free (void *retval_from_stbi_load);
+
+// get image dimensions & components without fully decoding
+extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
+#ifndef STBI_NO_STDIO
+extern int stbi_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_is_hdr (char const *filename);
+extern int stbi_is_hdr_from_file(FILE *f);
+#endif
+
+// ZLIB client - used by PNG, available for other purposes
+
+extern char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen);
+extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);
+extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
+
+extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
+extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
+
+// TYPE-SPECIFIC ACCESS
+
+// is it a jpeg?
+extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len);
+extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+#ifndef STBI_NO_STDIO
+extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern int stbi_jpeg_test_file (FILE *f);
+extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+
+extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+
+extern int stbi_jpeg_dc_only; // only decode DC component
+
+// is it a png?
+extern int stbi_png_test_memory (stbi_uc const *buffer, int len);
+extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+#ifndef STBI_NO_STDIO
+extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern int stbi_png_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_png_test_file (FILE *f);
+extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+
+// is it a bmp?
+extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len);
+
+extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_bmp_test_file (FILE *f);
+extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// is it a tga?
+extern int stbi_tga_test_memory (stbi_uc const *buffer, int len);
+
+extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_tga_test_file (FILE *f);
+extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// is it a psd?
+extern int stbi_psd_test_memory (stbi_uc const *buffer, int len);
+
+extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_psd_test_file (FILE *f);
+extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// is it an hdr?
+extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len);
+
+extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_hdr_test_file (FILE *f);
+extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// define new loaders
+typedef struct
+{
+ int (*test_memory)(stbi_uc const *buffer, int len);
+ stbi_uc * (*load_from_memory)(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+ #ifndef STBI_NO_STDIO
+ int (*test_file)(FILE *f);
+ stbi_uc * (*load_from_file)(FILE *f, int *x, int *y, int *comp, int req_comp);
+ #endif
+} stbi_loader;
+
+// register a loader by filling out the above structure (you must defined ALL functions)
+// returns 1 if added or already added, 0 if not added (too many loaders)
+extern int stbi_register_loader(stbi_loader *loader);
+
+// define faster low-level operations (typically SIMD support)
+#if STBI_SIMD
+typedef void (*stbi_idct_8x8)(uint8 *out, int out_stride, short data[64], unsigned short *dequantize);
+// compute an integer IDCT on "input"
+// input[x] = data[x] * dequantize[x]
+// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride'
+// CLAMP results to 0..255
+typedef void (*stbi_YCbCr_to_RGB_run)(uint8 *output, uint8 const *y, uint8 const *cb, uint8 const *cr, int count, int step);
+// compute a conversion from YCbCr to RGB
+// 'count' pixels
+// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B
+// y: Y input channel
+// cb: Cb input channel; scale/biased to be 0..255
+// cr: Cr input channel; scale/biased to be 0..255
+
+extern void stbi_install_idct(stbi_idct_8x8 func);
+extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func);
+#endif // STBI_SIMD
+
+#ifdef __cplusplus
+}
+#endif
+
+//
+//
+//// end header file /////////////////////////////////////////////////////
+
+#ifndef STBI_NO_STDIO
+#include <stdio.h>
+#endif
+#include <stdlib.h>
+#include <memory.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#if STBI_SIMD
+#include <emmintrin.h>
+#endif
+
+#ifndef _MSC_VER
+#define __forceinline
+#endif
+
+
+// implementation:
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef signed short int16;
+typedef unsigned int uint32;
+typedef signed int int32;
+typedef unsigned int uint;
+
+// should produce compiler error if size is wrong
+typedef unsigned char validate_uint32[sizeof(uint32)==4];
+
+#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE)
+#define STBI_NO_WRITE
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Generic API that works on all image types
+//
+
+static char *failure_reason;
+
+char *stbi_failure_reason(void)
+{
+ return failure_reason;
+}
+
+static int e(char *str)
+{
+ failure_reason = str;
+ return 0;
+}
+
+#ifdef STBI_NO_FAILURE_STRINGS
+ #define e(x,y) 0
+#elif defined(STBI_FAILURE_USERMSG)
+ #define e(x,y) e(y)
+#else
+ #define e(x,y) e(x)
+#endif
+
+#define epf(x,y) ((float *) (e(x,y)?NULL:NULL))
+#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL))
+
+void stbi_image_free(void *retval_from_stbi_load)
+{
+ free(retval_from_stbi_load);
+}
+
+#define MAX_LOADERS 32
+stbi_loader *loaders[MAX_LOADERS];
+static int max_loaders = 0;
+
+int stbi_register_loader(stbi_loader *loader)
+{
+ int i;
+ for (i=0; i < MAX_LOADERS; ++i) {
+ // already present?
+ if (loaders[i] == loader)
+ return 1;
+ // end of the list?
+ if (loaders[i] == NULL) {
+ loaders[i] = loader;
+ max_loaders = i+1;
+ return 1;
+ }
+ }
+ // no room for it
+ return 0;
+}
+
+#ifndef STBI_NO_HDR
+static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
+static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp);
+#endif
+
+#ifndef STBI_NO_STDIO
+unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ FILE *f = fopen(filename, "rb");
+ unsigned char *result;
+ if (!f) return epuc("can't fopen", "Unable to open file");
+ result = stbi_load_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return result;
+}
+
+unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ int i;
+ if (stbi_jpeg_test_file(f))
+ return stbi_jpeg_load_from_file(f,x,y,comp,req_comp);
+ if (stbi_png_test_file(f))
+ return stbi_png_load_from_file(f,x,y,comp,req_comp);
+ if (stbi_bmp_test_file(f))
+ return stbi_bmp_load_from_file(f,x,y,comp,req_comp);
+ if (stbi_psd_test_file(f))
+ return stbi_psd_load_from_file(f,x,y,comp,req_comp);
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_file(f)) {
+ float *hdr = stbi_hdr_load_from_file(f, x,y,comp,req_comp);
+ return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
+ }
+ #endif
+ for (i=0; i < max_loaders; ++i)
+ if (loaders[i]->test_file(f))
+ return loaders[i]->load_from_file(f,x,y,comp,req_comp);
+ // test tga last because it's a crappy test!
+ if (stbi_tga_test_file(f))
+ return stbi_tga_load_from_file(f,x,y,comp,req_comp);
+ return epuc("unknown image type", "Image not of any known type, or corrupt");
+}
+#endif
+
+unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ int i;
+ if (stbi_jpeg_test_memory(buffer,len))
+ return stbi_jpeg_load_from_memory(buffer,len,x,y,comp,req_comp);
+ if (stbi_png_test_memory(buffer,len))
+ return stbi_png_load_from_memory(buffer,len,x,y,comp,req_comp);
+ if (stbi_bmp_test_memory(buffer,len))
+ return stbi_bmp_load_from_memory(buffer,len,x,y,comp,req_comp);
+ if (stbi_psd_test_memory(buffer,len))
+ return stbi_psd_load_from_memory(buffer,len,x,y,comp,req_comp);
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_memory(buffer, len)) {
+ float *hdr = stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp);
+ return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
+ }
+ #endif
+ for (i=0; i < max_loaders; ++i)
+ if (loaders[i]->test_memory(buffer,len))
+ return loaders[i]->load_from_memory(buffer,len,x,y,comp,req_comp);
+ // test tga last because it's a crappy test!
+ if (stbi_tga_test_memory(buffer,len))
+ return stbi_tga_load_from_memory(buffer,len,x,y,comp,req_comp);
+ return epuc("unknown image type", "Image not of any known type, or corrupt");
+}
+
+#ifndef STBI_NO_HDR
+
+#ifndef STBI_NO_STDIO
+float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ FILE *f = fopen(filename, "rb");
+ float *result;
+ if (!f) return epf("can't fopen", "Unable to open file");
+ result = stbi_loadf_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return result;
+}
+
+float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *data;
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_file(f))
+ return stbi_hdr_load_from_file(f,x,y,comp,req_comp);
+ #endif
+ data = stbi_load_from_file(f, x, y, comp, req_comp);
+ if (data)
+ return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
+ return epf("unknown image type", "Image not of any known type, or corrupt");
+}
+#endif
+
+float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_memory(buffer, len))
+ return stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp);
+ #endif
+ data = stbi_load_from_memory(buffer, len, x, y, comp, req_comp);
+ if (data)
+ return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
+ return epf("unknown image type", "Image not of any known type, or corrupt");
+}
+#endif
+
+// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is
+// defined, for API simplicity; if STBI_NO_HDR is defined, it always
+// reports false!
+
+extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
+{
+ #ifndef STBI_NO_HDR
+ return stbi_hdr_test_memory(buffer, len);
+ #else
+ return 0;
+ #endif
+}
+
+#ifndef STBI_NO_STDIO
+extern int stbi_is_hdr (char const *filename)
+{
+ FILE *f = fopen(filename, "rb");
+ int result=0;
+ if (f) {
+ result = stbi_is_hdr_from_file(f);
+ fclose(f);
+ }
+ return result;
+}
+
+extern int stbi_is_hdr_from_file(FILE *f)
+{
+ #ifndef STBI_NO_HDR
+ return stbi_hdr_test_file(f);
+ #else
+ return 0;
+ #endif
+}
+
+#endif
+
+// @TODO: get image dimensions & components without fully decoding
+#ifndef STBI_NO_STDIO
+extern int stbi_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+#ifndef STBI_NO_HDR
+static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f;
+static float l2h_gamma=2.2f, l2h_scale=1.0f;
+
+void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; }
+void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; }
+
+void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; }
+void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; }
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Common code used by all image loaders
+//
+
+// image width, height, # components
+static uint32 img_x, img_y;
+static int img_n, img_out_n;
+
+enum
+{
+ SCAN_load=0,
+ SCAN_type,
+ SCAN_header,
+};
+
+// An API for reading either from memory or file.
+#ifndef STBI_NO_STDIO
+static FILE *img_file;
+#endif
+static uint8 const *img_buffer, *img_buffer_end;
+
+#ifndef STBI_NO_STDIO
+static void start_file(FILE *f)
+{
+ img_file = f;
+}
+#endif
+
+static void start_mem(uint8 const *buffer, int len)
+{
+#ifndef STBI_NO_STDIO
+ img_file = NULL;
+#endif
+ img_buffer = buffer;
+ img_buffer_end = buffer+len;
+}
+
+static int get8(void)
+{
+#ifndef STBI_NO_STDIO
+ if (img_file) {
+ int c = fgetc(img_file);
+ return c == EOF ? 0 : c;
+ }
+#endif
+ if (img_buffer < img_buffer_end)
+ return *img_buffer++;
+ return 0;
+}
+
+static int at_eof(void)
+{
+#ifndef STBI_NO_STDIO
+ if (img_file)
+ return feof(img_file);
+#endif
+ return img_buffer >= img_buffer_end;
+}
+
+static uint8 get8u(void)
+{
+ return (uint8) get8();
+}
+
+static void skip(int n)
+{
+#ifndef STBI_NO_STDIO
+ if (img_file)
+ fseek(img_file, n, SEEK_CUR);
+ else
+#endif
+ img_buffer += n;
+}
+
+static int get16(void)
+{
+ int z = get8();
+ return (z << 8) + get8();
+}
+
+static uint32 get32(void)
+{
+ uint32 z = get16();
+ return (z << 16) + get16();
+}
+
+static int get16le(void)
+{
+ int z = get8();
+ return z + (get8() << 8);
+}
+
+static uint32 get32le(void)
+{
+ uint32 z = get16le();
+ return z + (get16le() << 16);
+}
+
+static void getn(stbi_uc *buffer, int n)
+{
+#ifndef STBI_NO_STDIO
+ if (img_file) {
+ fread(buffer, 1, n, img_file);
+ return;
+ }
+#endif
+ memcpy(buffer, img_buffer, n);
+ img_buffer += n;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// generic converter from built-in img_n to req_comp
+// individual types do this automatically as much as possible (e.g. jpeg
+// does all cases internally since it needs to colorspace convert anyway,
+// and it never has alpha, so very few cases ). png can automatically
+// interleave an alpha=255 channel, but falls back to this for other cases
+//
+// assume data buffer is malloced, so malloc a new one and free that one
+// only failure mode is malloc failing
+
+static uint8 compute_y(int r, int g, int b)
+{
+ return (uint8) (((r*77) + (g*150) + (29*b)) >> 8);
+}
+
+static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp)
+{
+ uint i,j;
+ unsigned char *good;
+
+ if (req_comp == img_n) return data;
+ assert(req_comp >= 1 && req_comp <= 4);
+
+ good = (unsigned char *) malloc(req_comp * img_x * img_y);
+ if (good == NULL) {
+ free(data);
+ return epuc("outofmem", "Out of memory");
+ }
+
+ for (j=0; j < img_y; ++j) {
+ unsigned char *src = data + j * img_x * img_n ;
+ unsigned char *dest = good + j * img_x * req_comp;
+
+ #define COMBO(a,b) ((a)*8+(b))
+ #define CASE(a,b) case COMBO(a,b): for(i=0; i < img_x; ++i, src += a, dest += b)
+
+ // convert source image with img_n components to one with req_comp components;
+ // avoid switch per pixel, so use switch per scanline and massive macros
+ switch(COMBO(img_n, req_comp)) {
+ CASE(1,2) dest[0]=src[0], dest[1]=255; break;
+ CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break;
+ CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break;
+ CASE(2,1) dest[0]=src[0]; break;
+ CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break;
+ CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break;
+ CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break;
+ CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break;
+ CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break;
+ CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break;
+ CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break;
+ CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break;
+ default: assert(0);
+ }
+ #undef CASE
+ }
+
+ free(data);
+ img_out_n = req_comp;
+ return good;
+}
+
+#ifndef STBI_NO_HDR
+static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp)
+{
+ int i,k,n;
+ float *output = (float *) malloc(x * y * comp * sizeof(float));
+ if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); }
+ // compute number of non-alpha components
+ if (comp & 1) n = comp; else n = comp-1;
+ for (i=0; i < x*y; ++i) {
+ for (k=0; k < n; ++k) {
+ output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale;
+ }
+ if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f;
+ }
+ free(data);
+ return output;
+}
+
+#define float2int(x) ((int) (x))
+static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp)
+{
+ int i,k,n;
+ stbi_uc *output = (stbi_uc *) malloc(x * y * comp);
+ if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); }
+ // compute number of non-alpha components
+ if (comp & 1) n = comp; else n = comp-1;
+ for (i=0; i < x*y; ++i) {
+ for (k=0; k < n; ++k) {
+ float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f;
+ if (z < 0) z = 0;
+ if (z > 255) z = 255;
+ output[i*comp + k] = float2int(z);
+ }
+ if (k < comp) {
+ float z = data[i*comp+k] * 255 + 0.5f;
+ if (z < 0) z = 0;
+ if (z > 255) z = 255;
+ output[i*comp + k] = float2int(z);
+ }
+ }
+ free(data);
+ return output;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation)
+//
+// simple implementation
+// - channel subsampling of at most 2 in each dimension
+// - doesn't support delayed output of y-dimension
+// - simple interface (only one output format: 8-bit interleaved RGB)
+// - doesn't try to recover corrupt jpegs
+// - doesn't allow partial loading, loading multiple at once
+// - still fast on x86 (copying globals into locals doesn't help x86)
+// - allocates lots of intermediate memory (full size of all components)
+// - non-interleaved case requires this anyway
+// - allows good upsampling (see next)
+// high-quality
+// - upsampled channels are bilinearly interpolated, even across blocks
+// - quality integer IDCT derived from IJG's 'slow'
+// performance
+// - fast huffman; reasonable integer IDCT
+// - uses a lot of intermediate memory, could cache poorly
+// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4
+// stb_jpeg: 1.34 seconds (MSVC6, default release build)
+// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro)
+// IJL11.dll: 1.08 seconds (compiled by intel)
+// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG)
+// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro)
+
+int stbi_jpeg_dc_only;
+
+// huffman decoding acceleration
+#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache
+
+typedef struct
+{
+ uint8 fast[1 << FAST_BITS];
+ // weirdly, repacking this into AoS is a 10% speed loss, instead of a win
+ uint16 code[256];
+ uint8 values[256];
+ uint8 size[257];
+ unsigned int maxcode[18];
+ int delta[17]; // old 'firstsymbol' - old 'firstcode'
+} huffman;
+
+static huffman huff_dc[4]; // baseline is 2 tables, extended is 4
+static huffman huff_ac[4];
+static uint8 dequant[4][64];
+#if STBI_SIMD
+static __declspec(align(16)) unsigned short dequant2[4][64];
+#endif
+
+static int build_huffman(huffman *h, int *count)
+{
+ int i,j,k=0,code;
+ // build size list for each symbol (from JPEG spec)
+ for (i=0; i < 16; ++i)
+ for (j=0; j < count[i]; ++j)
+ h->size[k++] = (uint8) (i+1);
+ h->size[k] = 0;
+
+ // compute actual symbols (from jpeg spec)
+ code = 0;
+ k = 0;
+ for(j=1; j <= 16; ++j) {
+ // compute delta to add to code to compute symbol id
+ h->delta[j] = k - code;
+ if (h->size[k] == j) {
+ while (h->size[k] == j)
+ h->code[k++] = (uint16) (code++);
+ if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG");
+ }
+ // compute largest code + 1 for this size, preshifted as needed later
+ h->maxcode[j] = code << (16-j);
+ code <<= 1;
+ }
+ h->maxcode[j] = 0xffffffff;
+
+ // build non-spec acceleration table; 255 is flag for not-accelerated
+ memset(h->fast, 255, 1 << FAST_BITS);
+ for (i=0; i < k; ++i) {
+ int s = h->size[i];
+ if (s <= FAST_BITS) {
+ int c = h->code[i] << (FAST_BITS-s);
+ int m = 1 << (FAST_BITS-s);
+ for (j=0; j < m; ++j) {
+ h->fast[c+j] = (uint8) i;
+ }
+ }
+ }
+ return 1;
+}
+
+// sizes for components, interleaved MCUs
+static int img_h_max, img_v_max;
+static int img_mcu_x, img_mcu_y;
+static int img_mcu_w, img_mcu_h;
+
+// definition of jpeg image component
+static struct
+{
+ int id;
+ int h,v;
+ int tq;
+ int hd,ha;
+ int dc_pred;
+
+ int x,y,w2,h2;
+ uint8 *data;
+ void *raw_data;
+ uint8 *linebuf;
+} img_comp[4];
+
+static uint32 code_buffer; // jpeg entropy-coded buffer
+static int code_bits; // number of valid bits
+static unsigned char marker; // marker seen while filling entropy buffer
+static int nomore; // flag if we saw a marker so must stop
+
+static void grow_buffer_unsafe(void)
+{
+ do {
+ int b = nomore ? 0 : get8();
+ if (b == 0xff) {
+ int c = get8();
+ if (c != 0) {
+ marker = (unsigned char) c;
+ nomore = 1;
+ return;
+ }
+ }
+ code_buffer = (code_buffer << 8) | b;
+ code_bits += 8;
+ } while (code_bits <= 24);
+}
+
+// (1 << n) - 1
+static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};
+
+// decode a jpeg huffman value from the bitstream
+__forceinline static int decode(huffman *h)
+{
+ unsigned int temp;
+ int c,k;
+
+ if (code_bits < 16) grow_buffer_unsafe();
+
+ // look at the top FAST_BITS and determine what symbol ID it is,
+ // if the code is <= FAST_BITS
+ c = (code_buffer >> (code_bits - FAST_BITS)) & ((1 << FAST_BITS)-1);
+ k = h->fast[c];
+ if (k < 255) {
+ if (h->size[k] > code_bits)
+ return -1;
+ code_bits -= h->size[k];
+ return h->values[k];
+ }
+
+ // naive test is to shift the code_buffer down so k bits are
+ // valid, then test against maxcode. To speed this up, we've
+ // preshifted maxcode left so that it has (16-k) 0s at the
+ // end; in other words, regardless of the number of bits, it
+ // wants to be compared against something shifted to have 16;
+ // that way we don't need to shift inside the loop.
+ if (code_bits < 16)
+ temp = (code_buffer << (16 - code_bits)) & 0xffff;
+ else
+ temp = (code_buffer >> (code_bits - 16)) & 0xffff;
+ for (k=FAST_BITS+1 ; ; ++k)
+ if (temp < h->maxcode[k])
+ break;
+ if (k == 17) {
+ // error! code not found
+ code_bits -= 16;
+ return -1;
+ }
+
+ if (k > code_bits)
+ return -1;
+
+ // convert the huffman code to the symbol id
+ c = ((code_buffer >> (code_bits - k)) & bmask[k]) + h->delta[k];
+ assert((((code_buffer) >> (code_bits - h->size[c])) & bmask[h->size[c]]) == h->code[c]);
+
+ // convert the id to a symbol
+ code_bits -= k;
+ return h->values[c];
+}
+
+// combined JPEG 'receive' and JPEG 'extend', since baseline
+// always extends everything it receives.
+__forceinline static int extend_receive(int n)
+{
+ unsigned int m = 1 << (n-1);
+ unsigned int k;
+ if (code_bits < n) grow_buffer_unsafe();
+ k = (code_buffer >> (code_bits - n)) & bmask[n];
+ code_bits -= n;
+ // the following test is probably a random branch that won't
+ // predict well. I tried to table accelerate it but failed.
+ // maybe it's compiling as a conditional move?
+ if (k < m)
+ return (-1 << n) + k + 1;
+ else
+ return k;
+}
+
+// given a value that's at position X in the zigzag stream,
+// where does it appear in the 8x8 matrix coded as row-major?
+static uint8 dezigzag[64+15] =
+{
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63,
+ // let corrupt input sample past end
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63
+};
+
+// decode one 64-entry block--
+static int decode_block(short data[64], huffman *hdc, huffman *hac, int b)
+{
+ int diff,dc,k;
+ int t = decode(hdc);
+ if (t < 0) return e("bad huffman code","Corrupt JPEG");
+
+ // 0 all the ac values now so we can do it 32-bits at a time
+ memset(data,0,64*sizeof(data[0]));
+
+ diff = t ? extend_receive(t) : 0;
+ dc = img_comp[b].dc_pred + diff;
+ img_comp[b].dc_pred = dc;
+ data[0] = (short) dc;
+
+ // decode AC components, see JPEG spec
+ k = 1;
+ do {
+ int r,s;
+ int rs = decode(hac);
+ if (rs < 0) return e("bad huffman code","Corrupt JPEG");
+ s = rs & 15;
+ r = rs >> 4;
+ if (s == 0) {
+ if (rs != 0xf0) break; // end block
+ k += 16;
+ } else {
+ k += r;
+ // decode into unzigzag'd location
+ data[dezigzag[k++]] = (short) extend_receive(s);
+ }
+ } while (k < 64);
+ return 1;
+}
+
+// take a -128..127 value and clamp it and convert to 0..255
+__forceinline static uint8 clamp(int x)
+{
+ x += 128;
+ // trick to use a single test to catch both cases
+ if ((unsigned int) x > 255) {
+ if (x < 0) return 0;
+ if (x > 255) return 255;
+ }
+ return (uint8) x;
+}
+
+#define f2f(x) (int) (((x) * 4096 + 0.5))
+#define fsh(x) ((x) << 12)
+
+// derived from jidctint -- DCT_ISLOW
+#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \
+ int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \
+ p2 = s2; \
+ p3 = s6; \
+ p1 = (p2+p3) * f2f(0.5411961f); \
+ t2 = p1 + p3*f2f(-1.847759065f); \
+ t3 = p1 + p2*f2f( 0.765366865f); \
+ p2 = s0; \
+ p3 = s4; \
+ t0 = fsh(p2+p3); \
+ t1 = fsh(p2-p3); \
+ x0 = t0+t3; \
+ x3 = t0-t3; \
+ x1 = t1+t2; \
+ x2 = t1-t2; \
+ t0 = s7; \
+ t1 = s5; \
+ t2 = s3; \
+ t3 = s1; \
+ p3 = t0+t2; \
+ p4 = t1+t3; \
+ p1 = t0+t3; \
+ p2 = t1+t2; \
+ p5 = (p3+p4)*f2f( 1.175875602f); \
+ t0 = t0*f2f( 0.298631336f); \
+ t1 = t1*f2f( 2.053119869f); \
+ t2 = t2*f2f( 3.072711026f); \
+ t3 = t3*f2f( 1.501321110f); \
+ p1 = p5 + p1*f2f(-0.899976223f); \
+ p2 = p5 + p2*f2f(-2.562915447f); \
+ p3 = p3*f2f(-1.961570560f); \
+ p4 = p4*f2f(-0.390180644f); \
+ t3 += p1+p4; \
+ t2 += p2+p3; \
+ t1 += p2+p4; \
+ t0 += p1+p3;
+
+#if !STBI_SIMD
+// .344 seconds on 3*anemones.jpg
+static void idct_block(uint8 *out, int out_stride, short data[64], uint8 *dequantize)
+{
+ int i,val[64],*v=val;
+ uint8 *o,*dq = dequantize;
+ short *d = data;
+
+ if (stbi_jpeg_dc_only) {
+ // ok, I don't really know why this is right, but it seems to be:
+ int z = 128 + ((d[0] * dq[0]) >> 3);
+ for (i=0; i < 8; ++i) {
+ out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = z;
+ out += out_stride;
+ }
+ return;
+ }
+
+ // columns
+ for (i=0; i < 8; ++i,++d,++dq, ++v) {
+ // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
+ if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
+ && d[40]==0 && d[48]==0 && d[56]==0) {
+ // no shortcut 0 seconds
+ // (1|2|3|4|5|6|7)==0 0 seconds
+ // all separate -0.047 seconds
+ // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
+ int dcterm = d[0] * dq[0] << 2;
+ v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
+ } else {
+ IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24],
+ d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56])
+ // constants scaled things up by 1<<12; let's bring them back
+ // down, but keep 2 extra bits of precision
+ x0 += 512; x1 += 512; x2 += 512; x3 += 512;
+ v[ 0] = (x0+t3) >> 10;
+ v[56] = (x0-t3) >> 10;
+ v[ 8] = (x1+t2) >> 10;
+ v[48] = (x1-t2) >> 10;
+ v[16] = (x2+t1) >> 10;
+ v[40] = (x2-t1) >> 10;
+ v[24] = (x3+t0) >> 10;
+ v[32] = (x3-t0) >> 10;
+ }
+ }
+
+ for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
+ // no fast case since the first 1D IDCT spread components out
+ IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
+ // constants scaled things up by 1<<12, plus we had 1<<2 from first
+ // loop, plus horizontal and vertical each scale by sqrt(8) so together
+ // we've got an extra 1<<3, so 1<<17 total we need to remove.
+ x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536;
+ o[0] = clamp((x0+t3) >> 17);
+ o[7] = clamp((x0-t3) >> 17);
+ o[1] = clamp((x1+t2) >> 17);
+ o[6] = clamp((x1-t2) >> 17);
+ o[2] = clamp((x2+t1) >> 17);
+ o[5] = clamp((x2-t1) >> 17);
+ o[3] = clamp((x3+t0) >> 17);
+ o[4] = clamp((x3-t0) >> 17);
+ }
+}
+#else
+static void idct_block(uint8 *out, int out_stride, short data[64], unsigned short *dequantize)
+{
+ int i,val[64],*v=val;
+ uint8 *o;
+ unsigned short *dq = dequantize;
+ short *d = data;
+
+ if (stbi_jpeg_dc_only) {
+ // ok, I don't really know why this is right, but it seems to be:
+ int z = 128 + ((d[0] * dq[0]) >> 3);
+ for (i=0; i < 8; ++i) {
+ out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = z;
+ out += out_stride;
+ }
+ return;
+ }
+
+ // columns
+ for (i=0; i < 8; ++i,++d,++dq, ++v) {
+ // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
+ if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
+ && d[40]==0 && d[48]==0 && d[56]==0) {
+ // no shortcut 0 seconds
+ // (1|2|3|4|5|6|7)==0 0 seconds
+ // all separate -0.047 seconds
+ // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
+ int dcterm = d[0] * dq[0] << 2;
+ v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
+ } else {
+ IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24],
+ d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56])
+ // constants scaled things up by 1<<12; let's bring them back
+ // down, but keep 2 extra bits of precision
+ x0 += 512; x1 += 512; x2 += 512; x3 += 512;
+ v[ 0] = (x0+t3) >> 10;
+ v[56] = (x0-t3) >> 10;
+ v[ 8] = (x1+t2) >> 10;
+ v[48] = (x1-t2) >> 10;
+ v[16] = (x2+t1) >> 10;
+ v[40] = (x2-t1) >> 10;
+ v[24] = (x3+t0) >> 10;
+ v[32] = (x3-t0) >> 10;
+ }
+ }
+
+ for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
+ // no fast case since the first 1D IDCT spread components out
+ IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
+ // constants scaled things up by 1<<12, plus we had 1<<2 from first
+ // loop, plus horizontal and vertical each scale by sqrt(8) so together
+ // we've got an extra 1<<3, so 1<<17 total we need to remove.
+ x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536;
+ o[0] = clamp((x0+t3) >> 17);
+ o[7] = clamp((x0-t3) >> 17);
+ o[1] = clamp((x1+t2) >> 17);
+ o[6] = clamp((x1-t2) >> 17);
+ o[2] = clamp((x2+t1) >> 17);
+ o[5] = clamp((x2-t1) >> 17);
+ o[3] = clamp((x3+t0) >> 17);
+ o[4] = clamp((x3-t0) >> 17);
+ }
+}
+static stbi_idct_8x8 stbi_idct_installed = idct_block;
+
+extern void stbi_install_idct(stbi_idct_8x8 func)
+{
+ stbi_idct_installed = func;
+}
+#endif
+
+#define MARKER_none 0xff
+// if there's a pending marker from the entropy stream, return that
+// otherwise, fetch from the stream and get a marker. if there's no
+// marker, return 0xff, which is never a valid marker value
+static uint8 get_marker(void)
+{
+ uint8 x;
+ if (marker != MARKER_none) { x = marker; marker = MARKER_none; return x; }
+ x = get8u();
+ if (x != 0xff) return MARKER_none;
+ while (x == 0xff)
+ x = get8u();
+ return x;
+}
+
+// in each scan, we'll have scan_n components, and the order
+// of the components is specified by order[]
+static int scan_n, order[4];
+static int restart_interval, todo;
+#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7)
+
+// after a restart interval, reset the entropy decoder and
+// the dc prediction
+static void reset(void)
+{
+ code_bits = 0;
+ code_buffer = 0;
+ nomore = 0;
+ img_comp[0].dc_pred = img_comp[1].dc_pred = img_comp[2].dc_pred = 0;
+ marker = MARKER_none;
+ todo = restart_interval ? restart_interval : 0x7fffffff;
+ // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,
+ // since we don't even allow 1<<30 pixels
+}
+
+static int parse_entropy_coded_data(void)
+{
+ reset();
+ if (scan_n == 1) {
+ int i,j;
+ #if STBI_SIMD
+ __declspec(align(16))
+ #endif
+ short data[64];
+ int n = order[0];
+ // non-interleaved data, we just need to process one block at a time,
+ // in trivial scanline order
+ // number of blocks to do just depends on how many actual "pixels" this
+ // component has, independent of interleaved MCU blocking and such
+ int w = (img_comp[n].x+7) >> 3;
+ int h = (img_comp[n].y+7) >> 3;
+ for (j=0; j < h; ++j) {
+ for (i=0; i < w; ++i) {
+ if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0;
+ #if STBI_SIMD
+ stbi_idct_installed(img_comp[n].data+img_comp[n].w2*j*8+i*8, img_comp[n].w2, data, dequant2[img_comp[n].tq]);
+ #else
+ idct_block(img_comp[n].data+img_comp[n].w2*j*8+i*8, img_comp[n].w2, data, dequant[img_comp[n].tq]);
+ #endif
+ // every data block is an MCU, so countdown the restart interval
+ if (--todo <= 0) {
+ if (code_bits < 24) grow_buffer_unsafe();
+ // if it's NOT a restart, then just bail, so we get corrupt data
+ // rather than no data
+ if (!RESTART(marker)) return 1;
+ reset();
+ }
+ }
+ }
+ } else { // interleaved!
+ int i,j,k,x,y;
+ short data[64];
+ for (j=0; j < img_mcu_y; ++j) {
+ for (i=0; i < img_mcu_x; ++i) {
+ // scan an interleaved mcu... process scan_n components in order
+ for (k=0; k < scan_n; ++k) {
+ int n = order[k];
+ // scan out an mcu's worth of this component; that's just determined
+ // by the basic H and V specified for the component
+ for (y=0; y < img_comp[n].v; ++y) {
+ for (x=0; x < img_comp[n].h; ++x) {
+ int x2 = (i*img_comp[n].h + x)*8;
+ int y2 = (j*img_comp[n].v + y)*8;
+ if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0;
+ #if STBI_SIMD
+ stbi_idct_installed(img_comp[n].data+img_comp[n].w2*y2+x2, img_comp[n].w2, data, dequant2[img_comp[n].tq]);
+ #else
+ idct_block(img_comp[n].data+img_comp[n].w2*y2+x2, img_comp[n].w2, data, dequant[img_comp[n].tq]);
+ #endif
+ }
+ }
+ }
+ // after all interleaved components, that's an interleaved MCU,
+ // so now count down the restart interval
+ if (--todo <= 0) {
+ if (code_bits < 24) grow_buffer_unsafe();
+ // if it's NOT a restart, then just bail, so we get corrupt data
+ // rather than no data
+ if (!RESTART(marker)) return 1;
+ reset();
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+static int process_marker(int m)
+{
+ int L;
+ switch (m) {
+ case MARKER_none: // no marker found
+ return e("expected marker","Corrupt JPEG");
+
+ case 0xC2: // SOF - progressive
+ return e("progressive jpeg","JPEG format not supported (progressive)");
+
+ case 0xDD: // DRI - specify restart interval
+ if (get16() != 4) return e("bad DRI len","Corrupt JPEG");
+ restart_interval = get16();
+ return 1;
+
+ case 0xDB: // DQT - define quantization table
+ L = get16()-2;
+ while (L > 0) {
+ int z = get8();
+ int p = z >> 4;
+ int t = z & 15,i;
+ if (p != 0) return e("bad DQT type","Corrupt JPEG");
+ if (t > 3) return e("bad DQT table","Corrupt JPEG");
+ for (i=0; i < 64; ++i)
+ dequant[t][dezigzag[i]] = get8u();
+ #if STBI_SIMD
+ for (i=0; i < 64; ++i)
+ dequant2[t][i] = dequant[t][i];
+ #endif
+ L -= 65;
+ }
+ return L==0;
+
+ case 0xC4: // DHT - define huffman table
+ L = get16()-2;
+ while (L > 0) {
+ uint8 *v;
+ int sizes[16],i,m=0;
+ int z = get8();
+ int tc = z >> 4;
+ int th = z & 15;
+ if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG");
+ for (i=0; i < 16; ++i) {
+ sizes[i] = get8();
+ m += sizes[i];
+ }
+ L -= 17;
+ if (tc == 0) {
+ if (!build_huffman(huff_dc+th, sizes)) return 0;
+ v = huff_dc[th].values;
+ } else {
+ if (!build_huffman(huff_ac+th, sizes)) return 0;
+ v = huff_ac[th].values;
+ }
+ for (i=0; i < m; ++i)
+ v[i] = get8u();
+ L -= m;
+ }
+ return L==0;
+ }
+ // check for comment block or APP blocks
+ if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {
+ skip(get16()-2);
+ return 1;
+ }
+ return 0;
+}
+
+// after we see SOS
+static int process_scan_header(void)
+{
+ int i;
+ int Ls = get16();
+ scan_n = get8();
+ if (scan_n < 1 || scan_n > 4 || scan_n > (int) img_n) return e("bad SOS component count","Corrupt JPEG");
+ if (Ls != 6+2*scan_n) return e("bad SOS len","Corrupt JPEG");
+ for (i=0; i < scan_n; ++i) {
+ int id = get8(), which;
+ int z = get8();
+ for (which = 0; which < img_n; ++which)
+ if (img_comp[which].id == id)
+ break;
+ if (which == img_n) return 0;
+ img_comp[which].hd = z >> 4; if (img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG");
+ img_comp[which].ha = z & 15; if (img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG");
+ order[i] = which;
+ }
+ if (get8() != 0) return e("bad SOS","Corrupt JPEG");
+ get8(); // should be 63, but might be 0
+ if (get8() != 0) return e("bad SOS","Corrupt JPEG");
+
+ return 1;
+}
+
+static int process_frame_header(int scan)
+{
+ int Lf,p,i,z, h_max=1,v_max=1;
+ Lf = get16(); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG
+ p = get8(); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline
+ img_y = get16(); if (img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG
+ img_x = get16(); if (img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires
+ img_n = get8();
+ if (img_n != 3 && img_n != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires
+
+ if (Lf != 8+3*img_n) return e("bad SOF len","Corrupt JPEG");
+
+ for (i=0; i < img_n; ++i) {
+ img_comp[i].id = get8();
+ if (img_comp[i].id != i+1) // JFIF requires
+ if (img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files!
+ return e("bad component ID","Corrupt JPEG");
+ z = get8();
+ img_comp[i].h = (z >> 4); if (!img_comp[i].h || img_comp[i].h > 4) return e("bad H","Corrupt JPEG");
+ img_comp[i].v = z & 15; if (!img_comp[i].v || img_comp[i].v > 4) return e("bad V","Corrupt JPEG");
+ img_comp[i].tq = get8(); if (img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG");
+ }
+
+ if (scan != SCAN_load) return 1;
+
+ if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode");
+
+ for (i=0; i < img_n; ++i) {
+ if (img_comp[i].h > h_max) h_max = img_comp[i].h;
+ if (img_comp[i].v > v_max) v_max = img_comp[i].v;
+ }
+
+ // compute interleaved mcu info
+ img_h_max = h_max;
+ img_v_max = v_max;
+ img_mcu_w = h_max * 8;
+ img_mcu_h = v_max * 8;
+ img_mcu_x = (img_x + img_mcu_w-1) / img_mcu_w;
+ img_mcu_y = (img_y + img_mcu_h-1) / img_mcu_h;
+
+ for (i=0; i < img_n; ++i) {
+ // number of effective pixels (e.g. for non-interleaved MCU)
+ img_comp[i].x = (img_x * img_comp[i].h + h_max-1) / h_max;
+ img_comp[i].y = (img_y * img_comp[i].v + v_max-1) / v_max;
+ // to simplify generation, we'll allocate enough memory to decode
+ // the bogus oversized data from using interleaved MCUs and their
+ // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't
+ // discard the extra data until colorspace conversion
+ img_comp[i].w2 = img_mcu_x * img_comp[i].h * 8;
+ img_comp[i].h2 = img_mcu_y * img_comp[i].v * 8;
+ img_comp[i].raw_data = malloc(img_comp[i].w2 * img_comp[i].h2+15);
+ if (img_comp[i].raw_data == NULL) {
+ for(--i; i >= 0; --i) {
+ free(img_comp[i].raw_data);
+ img_comp[i].data = NULL;
+ }
+ return e("outofmem", "Out of memory");
+ }
+ img_comp[i].data = (uint8*) (((int) img_comp[i].raw_data + 15) & ~15);
+ img_comp[i].linebuf = NULL;
+ }
+
+ return 1;
+}
+
+// use comparisons since in some cases we handle more than one case (e.g. SOF)
+#define DNL(x) ((x) == 0xdc)
+#define SOI(x) ((x) == 0xd8)
+#define EOI(x) ((x) == 0xd9)
+#define SOF(x) ((x) == 0xc0 || (x) == 0xc1)
+#define SOS(x) ((x) == 0xda)
+
+static int decode_jpeg_header(int scan)
+{
+ int m;
+ marker = MARKER_none; // initialize cached marker to empty
+ m = get_marker();
+ if (!SOI(m)) return e("no SOI","Corrupt JPEG");
+ if (scan == SCAN_type) return 1;
+ m = get_marker();
+ while (!SOF(m)) {
+ if (!process_marker(m)) return 0;
+ m = get_marker();
+ while (m == MARKER_none) {
+ // some files have extra padding after their blocks, so ok, we'll scan
+ if (at_eof()) return e("no SOF", "Corrupt JPEG");
+ m = get_marker();
+ }
+ }
+ if (!process_frame_header(scan)) return 0;
+ return 1;
+}
+
+static int decode_jpeg_image(void)
+{
+ int m;
+ restart_interval = 0;
+ if (!decode_jpeg_header(SCAN_load)) return 0;
+ m = get_marker();
+ while (!EOI(m)) {
+ if (SOS(m)) {
+ if (!process_scan_header()) return 0;
+ if (!parse_entropy_coded_data()) return 0;
+ } else {
+ if (!process_marker(m)) return 0;
+ }
+ m = get_marker();
+ }
+ return 1;
+}
+
+// static jfif-centered resampling (across block boundaries)
+
+typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1,
+ int w, int hs);
+
+#define div4(x) ((uint8) ((x) >> 2))
+
+static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ return in_near;
+}
+
+static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // need to generate two samples vertically for every one in input
+ int i;
+ for (i=0; i < w; ++i)
+ out[i] = div4(3*in_near[i] + in_far[i] + 2);
+ return out;
+}
+
+static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // need to generate two samples horizontally for every one in input
+ int i;
+ uint8 *input = in_near;
+ if (w == 1) {
+ // if only one sample, can't do any interpolation
+ out[0] = out[1] = input[0];
+ return out;
+ }
+
+ out[0] = input[0];
+ out[1] = div4(input[0]*3 + input[1] + 2);
+ for (i=1; i < w-1; ++i) {
+ int n = 3*input[i]+2;
+ out[i*2+0] = div4(n+input[i-1]);
+ out[i*2+1] = div4(n+input[i+1]);
+ }
+ out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2);
+ out[i*2+1] = input[w-1];
+ return out;
+}
+
+#define div16(x) ((uint8) ((x) >> 4))
+
+static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // need to generate 2x2 samples for every one in input
+ int i,t0,t1;
+ if (w == 1) {
+ out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2);
+ return out;
+ }
+
+ t1 = 3*in_near[0] + in_far[0];
+ out[0] = div4(t1+2);
+ for (i=1; i < w; ++i) {
+ t0 = t1;
+ t1 = 3*in_near[i]+in_far[i];
+ out[i*2-1] = div16(3*t0 + t1 + 8);
+ out[i*2 ] = div16(3*t1 + t0 + 8);
+ }
+ out[w*2-1] = div4(t1+2);
+ return out;
+}
+
+static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // resample with nearest-neighbor
+ int i,j;
+ for (i=0; i < w; ++i)
+ for (j=0; j < hs; ++j)
+ out[i*hs+j] = in_near[i];
+ return out;
+}
+
+#define float2fixed(x) ((int) ((x) * 65536 + 0.5))
+
+// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro)
+// VC6 without processor=Pro is generating multiple LEAs per multiply!
+static void YCbCr_to_RGB_row(uint8 *out, uint8 *y, uint8 *pcb, uint8 *pcr, int count, int step)
+{
+ int i;
+ for (i=0; i < count; ++i) {
+ int y_fixed = (y[i] << 16) + 32768; // rounding
+ int r,g,b;
+ int cr = pcr[i] - 128;
+ int cb = pcb[i] - 128;
+ r = y_fixed + cr*float2fixed(1.40200f);
+ g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f);
+ b = y_fixed + cb*float2fixed(1.77200f);
+ r >>= 16;
+ g >>= 16;
+ b >>= 16;
+ if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
+ if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
+ if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
+ out[0] = (uint8)r;
+ out[1] = (uint8)g;
+ out[2] = (uint8)b;
+ out[3] = 255;
+ out += step;
+ }
+}
+
+#if STBI_SIMD
+static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row;
+
+void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func)
+{
+ stbi_YCbCr_installed = func;
+}
+#endif
+
+
+// clean up the temporary component buffers
+static void cleanup_jpeg(void)
+{
+ int i;
+ for (i=0; i < img_n; ++i) {
+ if (img_comp[i].data) {
+ free(img_comp[i].raw_data);
+ img_comp[i].data = NULL;
+ }
+ if (img_comp[i].linebuf) {
+ free(img_comp[i].linebuf);
+ img_comp[i].linebuf = NULL;
+ }
+ }
+}
+
+typedef struct
+{
+ resample_row_func resample;
+ uint8 *line0,*line1;
+ int hs,vs; // expansion factor in each axis
+ int w_lores; // horizontal pixels pre-expansion
+ int ystep; // how far through vertical expansion we are
+ int ypos; // which pre-expansion row we're on
+} stbi_resample;
+
+static uint8 *load_jpeg_image(int *out_x, int *out_y, int *comp, int req_comp)
+{
+ int n, decode_n;
+ // validate req_comp
+ if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error");
+
+ // load a jpeg image from whichever source
+ if (!decode_jpeg_image()) { cleanup_jpeg(); return NULL; }
+
+ // determine actual number of components to generate
+ n = req_comp ? req_comp : img_n;
+
+ if (img_n == 3 && n < 3)
+ decode_n = 1;
+ else
+ decode_n = img_n;
+
+ // resample and color-convert
+ {
+ int k;
+ uint i,j;
+ uint8 *output;
+ uint8 *coutput[4];
+
+ stbi_resample res_comp[4];
+
+ for (k=0; k < decode_n; ++k) {
+ stbi_resample *r = &res_comp[k];
+
+ // allocate line buffer big enough for upsampling off the edges
+ // with upsample factor of 4
+ img_comp[k].linebuf = (uint8 *) malloc(img_x + 3);
+ if (!img_comp[k].linebuf) { cleanup_jpeg(); return epuc("outofmem", "Out of memory"); }
+
+ r->hs = img_h_max / img_comp[k].h;
+ r->vs = img_v_max / img_comp[k].v;
+ r->ystep = r->vs >> 1;
+ r->w_lores = (img_x + r->hs-1) / r->hs;
+ r->ypos = 0;
+ r->line0 = r->line1 = img_comp[k].data;
+
+ if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;
+ else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2;
+ else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2;
+ else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2;
+ else r->resample = resample_row_generic;
+ }
+
+ // can't error after this so, this is safe
+ output = (uint8 *) malloc(n * img_x * img_y + 1);
+ if (!output) { cleanup_jpeg(); return epuc("outofmem", "Out of memory"); }
+
+ // now go ahead and resample
+ for (j=0; j < img_y; ++j) {
+ uint8 *out = output + n * img_x * j;
+ for (k=0; k < decode_n; ++k) {
+ stbi_resample *r = &res_comp[k];
+ int y_bot = r->ystep >= (r->vs >> 1);
+ coutput[k] = r->resample(img_comp[k].linebuf,
+ y_bot ? r->line1 : r->line0,
+ y_bot ? r->line0 : r->line1,
+ r->w_lores, r->hs);
+ if (++r->ystep >= r->vs) {
+ r->ystep = 0;
+ r->line0 = r->line1;
+ if (++r->ypos < img_comp[k].y)
+ r->line1 += img_comp[k].w2;
+ }
+ }
+ if (n >= 3) {
+ uint8 *y = coutput[0];
+ if (img_n == 3) {
+ #if STBI_SIMD
+ stbi_YCbCr_installed(out, y, coutput[1], coutput[2], img_x, n);
+ #else
+ YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], img_x, n);
+ #endif
+ } else
+ for (i=0; i < img_x; ++i) {
+ out[0] = out[1] = out[2] = y[i];
+ out[3] = 255; // not used if n==3
+ out += n;
+ }
+ } else {
+ uint8 *y = coutput[0];
+ if (n == 1)
+ for (i=0; i < img_x; ++i) out[i] = y[i];
+ else
+ for (i=0; i < img_x; ++i) *out++ = y[i], *out++ = 255;
+ }
+ }
+ cleanup_jpeg();
+ *out_x = img_x;
+ *out_y = img_y;
+ if (comp) *comp = img_n; // report original components, not output
+ return output;
+ }
+}
+
+#ifndef STBI_NO_STDIO
+unsigned char *stbi_jpeg_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return load_jpeg_image(x,y,comp,req_comp);
+}
+
+unsigned char *stbi_jpeg_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_jpeg_load_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+#endif
+
+unsigned char *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer,len);
+ return load_jpeg_image(x,y,comp,req_comp);
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_jpeg_test_file(FILE *f)
+{
+ int n,r;
+ n = ftell(f);
+ start_file(f);
+ r = decode_jpeg_header(SCAN_type);
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_jpeg_test_memory(stbi_uc const *buffer, int len)
+{
+ start_mem(buffer,len);
+ return decode_jpeg_header(SCAN_type);
+}
+
+// @TODO:
+#ifndef STBI_NO_STDIO
+extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+// public domain zlib decode v0.2 Sean Barrett 2006-11-18
+// simple implementation
+// - all input must be provided in an upfront buffer
+// - all output is written to a single output buffer (can malloc/realloc)
+// performance
+// - fast huffman
+
+// fast-way is faster to check than jpeg huffman, but slow way is slower
+#define ZFAST_BITS 9 // accelerate all cases in default tables
+#define ZFAST_MASK ((1 << ZFAST_BITS) - 1)
+
+// zlib-style huffman encoding
+// (jpegs packs from left, zlib from right, so can't share code)
+typedef struct
+{
+ uint16 fast[1 << ZFAST_BITS];
+ uint16 firstcode[16];
+ int maxcode[17];
+ uint16 firstsymbol[16];
+ uint8 size[288];
+ uint16 value[288];
+} zhuffman;
+
+__forceinline static int bitreverse16(int n)
+{
+ n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
+ n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
+ n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4);
+ n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8);
+ return n;
+}
+
+__forceinline static int bit_reverse(int v, int bits)
+{
+ assert(bits <= 16);
+ // to bit reverse n bits, reverse 16 and shift
+ // e.g. 11 bits, bit reverse and shift away 5
+ return bitreverse16(v) >> (16-bits);
+}
+
+static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num)
+{
+ int i,k=0;
+ int code, next_code[16], sizes[17];
+
+ // DEFLATE spec for generating codes
+ memset(sizes, 0, sizeof(sizes));
+ memset(z->fast, 255, sizeof(z->fast));
+ for (i=0; i < num; ++i)
+ ++sizes[sizelist[i]];
+ sizes[0] = 0;
+ for (i=1; i < 16; ++i)
+ assert(sizes[i] <= (1 << i));
+ code = 0;
+ for (i=1; i < 16; ++i) {
+ next_code[i] = code;
+ z->firstcode[i] = (uint16) code;
+ z->firstsymbol[i] = (uint16) k;
+ code = (code + sizes[i]);
+ if (sizes[i])
+ if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG");
+ z->maxcode[i] = code << (16-i); // preshift for inner loop
+ code <<= 1;
+ k += sizes[i];
+ }
+ z->maxcode[16] = 0x10000; // sentinel
+ for (i=0; i < num; ++i) {
+ int s = sizelist[i];
+ if (s) {
+ int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
+ z->size[c] = (uint8)s;
+ z->value[c] = (uint16)i;
+ if (s <= ZFAST_BITS) {
+ int k = bit_reverse(next_code[s],s);
+ while (k < (1 << ZFAST_BITS)) {
+ z->fast[k] = (uint16) c;
+ k += (1 << s);
+ }
+ }
+ ++next_code[s];
+ }
+ }
+ return 1;
+}
+
+// zlib-from-memory implementation for PNG reading
+// because PNG allows splitting the zlib stream arbitrarily,
+// and it's annoying structurally to have PNG call ZLIB call PNG,
+// we require PNG read all the IDATs and combine them into a single
+// memory buffer
+
+static uint8 *zbuffer, *zbuffer_end;
+
+__forceinline static int zget8(void)
+{
+ if (zbuffer >= zbuffer_end) return 0;
+ return *zbuffer++;
+}
+
+//static uint32 code_buffer;
+static int num_bits;
+
+static void fill_bits(void)
+{
+ do {
+ assert(code_buffer < (1U << num_bits));
+ code_buffer |= zget8() << num_bits;
+ num_bits += 8;
+ } while (num_bits <= 24);
+}
+
+__forceinline static unsigned int zreceive(int n)
+{
+ unsigned int k;
+ if (num_bits < n) fill_bits();
+ k = code_buffer & ((1 << n) - 1);
+ code_buffer >>= n;
+ num_bits -= n;
+ return k;
+}
+
+__forceinline static int zhuffman_decode(zhuffman *z)
+{
+ int b,s,k;
+ if (num_bits < 16) fill_bits();
+ b = z->fast[code_buffer & ZFAST_MASK];
+ if (b < 0xffff) {
+ s = z->size[b];
+ code_buffer >>= s;
+ num_bits -= s;
+ return z->value[b];
+ }
+
+ // not resolved by fast table, so compute it the slow way
+ // use jpeg approach, which requires MSbits at top
+ k = bit_reverse(code_buffer, 16);
+ for (s=ZFAST_BITS+1; ; ++s)
+ if (k < z->maxcode[s])
+ break;
+ if (s == 16) return -1; // invalid code!
+ // code size is s, so:
+ b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
+ assert(z->size[b] == s);
+ code_buffer >>= s;
+ num_bits -= s;
+ return z->value[b];
+}
+
+static char *zout;
+static char *zout_start;
+static char *zout_end;
+static int z_expandable;
+
+static int expand(int n) // need to make room for n bytes
+{
+ char *q;
+ int cur, limit;
+ if (!z_expandable) return e("output buffer limit","Corrupt PNG");
+ cur = (int) (zout - zout_start);
+ limit = (int) (zout_end - zout_start);
+ while (cur + n > limit)
+ limit *= 2;
+ q = (char *) realloc(zout_start, limit);
+ if (q == NULL) return e("outofmem", "Out of memory");
+ zout_start = q;
+ zout = q + cur;
+ zout_end = q + limit;
+ return 1;
+}
+
+static zhuffman z_length, z_distance;
+
+static int length_base[31] = {
+ 3,4,5,6,7,8,9,10,11,13,
+ 15,17,19,23,27,31,35,43,51,59,
+ 67,83,99,115,131,163,195,227,258,0,0 };
+
+static int length_extra[31]=
+{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
+
+static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
+257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
+
+static int dist_extra[32] =
+{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+static int parse_huffman_block(void)
+{
+ for(;;) {
+ int z = zhuffman_decode(&z_length);
+ if (z < 256) {
+ if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes
+ if (zout >= zout_end) if (!expand(1)) return 0;
+ *zout++ = (char) z;
+ } else {
+ uint8 *p;
+ int len,dist;
+ if (z == 256) return 1;
+ z -= 257;
+ len = length_base[z];
+ if (length_extra[z]) len += zreceive(length_extra[z]);
+ z = zhuffman_decode(&z_distance);
+ if (z < 0) return e("bad huffman code","Corrupt PNG");
+ dist = dist_base[z];
+ if (dist_extra[z]) dist += zreceive(dist_extra[z]);
+ if (zout - zout_start < dist) return e("bad dist","Corrupt PNG");
+ if (zout + len > zout_end) if (!expand(len)) return 0;
+ p = (uint8 *) (zout - dist);
+ while (len--)
+ *zout++ = *p++;
+ }
+ }
+}
+
+static int compute_huffman_codes(void)
+{
+ static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
+ static zhuffman z_codelength; // static just to save stack space
+ uint8 lencodes[286+32+137];//padding for maximum single op
+ uint8 codelength_sizes[19];
+ int i,n;
+
+ int hlit = zreceive(5) + 257;
+ int hdist = zreceive(5) + 1;
+ int hclen = zreceive(4) + 4;
+
+ memset(codelength_sizes, 0, sizeof(codelength_sizes));
+ for (i=0; i < hclen; ++i) {
+ int s = zreceive(3);
+ codelength_sizes[length_dezigzag[i]] = (uint8) s;
+ }
+ if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;
+
+ n = 0;
+ while (n < hlit + hdist) {
+ int c = zhuffman_decode(&z_codelength);
+ assert(c >= 0 && c < 19);
+ if (c < 16)
+ lencodes[n++] = (uint8) c;
+ else if (c == 16) {
+ c = zreceive(2)+3;
+ memset(lencodes+n, lencodes[n-1], c);
+ n += c;
+ } else if (c == 17) {
+ c = zreceive(3)+3;
+ memset(lencodes+n, 0, c);
+ n += c;
+ } else {
+ assert(c == 18);
+ c = zreceive(7)+11;
+ memset(lencodes+n, 0, c);
+ n += c;
+ }
+ }
+ if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG");
+ if (!zbuild_huffman(&z_length, lencodes, hlit)) return 0;
+ if (!zbuild_huffman(&z_distance, lencodes+hlit, hdist)) return 0;
+ return 1;
+}
+
+static int parse_uncompressed_block(void)
+{
+ uint8 header[4];
+ int len,nlen,k;
+ if (num_bits & 7)
+ zreceive(num_bits & 7); // discard
+ // drain the bit-packed data into header
+ k = 0;
+ while (num_bits > 0) {
+ header[k++] = (uint8) (code_buffer & 255); // wtf this warns?
+ code_buffer >>= 8;
+ num_bits -= 8;
+ }
+ assert(num_bits == 0);
+ // now fill header the normal way
+ while (k < 4)
+ header[k++] = (uint8) zget8();
+ len = header[1] * 256 + header[0];
+ nlen = header[3] * 256 + header[2];
+ if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG");
+ if (zbuffer + len > zbuffer_end) return e("read past buffer","Corrupt PNG");
+ if (zout + len > zout_end)
+ if (!expand(len)) return 0;
+ memcpy(zout, zbuffer, len);
+ zbuffer += len;
+ zout += len;
+ return 1;
+}
+
+static int parse_zlib_header(void)
+{
+ int cmf = zget8();
+ int cm = cmf & 15;
+ /* int cinfo = cmf >> 4; */
+ int flg = zget8();
+ if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec
+ if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png
+ if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png
+ // window = 1 << (8 + cinfo)... but who cares, we fully buffer output
+ return 1;
+}
+
+static uint8 default_length[288], default_distance[32];
+static void init_defaults(void)
+{
+ int i; // use <= to match clearly with spec
+ for (i=0; i <= 143; ++i) default_length[i] = 8;
+ for ( ; i <= 255; ++i) default_length[i] = 9;
+ for ( ; i <= 279; ++i) default_length[i] = 7;
+ for ( ; i <= 287; ++i) default_length[i] = 8;
+
+ for (i=0; i <= 31; ++i) default_distance[i] = 5;
+}
+
+static int parse_zlib(int parse_header)
+{
+ int final, type;
+ if (parse_header)
+ if (!parse_zlib_header()) return 0;
+ num_bits = 0;
+ code_buffer = 0;
+ do {
+ final = zreceive(1);
+ type = zreceive(2);
+ if (type == 0) {
+ if (!parse_uncompressed_block()) return 0;
+ } else if (type == 3) {
+ return 0;
+ } else {
+ if (type == 1) {
+ // use fixed code lengths
+ if (!default_length[0]) init_defaults();
+ if (!zbuild_huffman(&z_length , default_length , 288)) return 0;
+ if (!zbuild_huffman(&z_distance, default_distance, 32)) return 0;
+ } else {
+ if (!compute_huffman_codes()) return 0;
+ }
+ if (!parse_huffman_block()) return 0;
+ }
+ } while (!final);
+ return 1;
+}
+
+static int do_zlib(char *obuf, int olen, int exp, int parse_header)
+{
+ zout_start = obuf;
+ zout = obuf;
+ zout_end = obuf + olen;
+ z_expandable = exp;
+
+ return parse_zlib(parse_header);
+}
+
+char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen)
+{
+ char *p = (char *) malloc(initial_size);
+ if (p == NULL) return NULL;
+ if (do_zlib(p, initial_size, 1, 1)) {
+ *outlen = (int) (zout - zout_start);
+ return zout_start;
+ } else {
+ free(zout_start);
+ return NULL;
+ }
+}
+
+char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)
+{
+ zbuffer = (uint8 *) buffer;
+ zbuffer_end = (uint8 *) buffer+len;
+ return stbi_zlib_decode_malloc_guesssize(16384, outlen);
+}
+
+int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)
+{
+ zbuffer = (uint8 *) ibuffer;
+ zbuffer_end = (uint8 *) ibuffer + ilen;
+ if (do_zlib(obuffer, olen, 0, 1))
+ return (int) (zout - zout_start);
+ else
+ return -1;
+}
+
+char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)
+{
+ char *p = (char *) malloc(16384);
+ if (p == NULL) return NULL;
+ zbuffer = (uint8 *) buffer;
+ zbuffer_end = (uint8 *) buffer+len;
+ if (do_zlib(p, 16384, 1, 0)) {
+ *outlen = (int) (zout - zout_start);
+ return zout_start;
+ } else {
+ free(zout_start);
+ return NULL;
+ }
+}
+
+int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)
+{
+ zbuffer = (uint8 *) ibuffer;
+ zbuffer_end = (uint8 *) ibuffer + ilen;
+ if (do_zlib(obuffer, olen, 0, 0))
+ return (int) (zout - zout_start);
+ else
+ return -1;
+}
+
+// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18
+// simple implementation
+// - only 8-bit samples
+// - no CRC checking
+// - allocates lots of intermediate memory
+// - avoids problem of streaming data between subsystems
+// - avoids explicit window management
+// performance
+// - uses stb_zlib, a PD zlib implementation with fast huffman decoding
+
+
+typedef struct
+{
+ uint32 length;
+ uint32 type;
+} chunk;
+
+#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
+
+static chunk get_chunk_header(void)
+{
+ chunk c;
+ c.length = get32();
+ c.type = get32();
+ return c;
+}
+
+static int check_png_header(void)
+{
+ static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 };
+ int i;
+ for (i=0; i < 8; ++i)
+ if (get8() != png_sig[i]) return e("bad png sig","Not a PNG");
+ return 1;
+}
+
+static uint8 *idata, *expanded, *out;
+
+enum {
+ F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4,
+ F_avg_first, F_paeth_first,
+};
+
+static uint8 first_row_filter[5] =
+{
+ F_none, F_sub, F_none, F_avg_first, F_paeth_first
+};
+
+static int paeth(int a, int b, int c)
+{
+ int p = a + b - c;
+ int pa = abs(p-a);
+ int pb = abs(p-b);
+ int pc = abs(p-c);
+ if (pa <= pb && pa <= pc) return a;
+ if (pb <= pc) return b;
+ return c;
+}
+
+// create the png data from post-deflated data
+static int create_png_image(uint8 *raw, uint32 raw_len, int out_n)
+{
+ uint32 i,j,stride = img_x*out_n;
+ int k;
+ assert(out_n == img_n || out_n == img_n+1);
+ out = (uint8 *) malloc(img_x * img_y * out_n);
+ if (!out) return e("outofmem", "Out of memory");
+ if (raw_len != (img_n * img_x + 1) * img_y) return e("not enough pixels","Corrupt PNG");
+ for (j=0; j < img_y; ++j) {
+ uint8 *cur = out + stride*j;
+ uint8 *prior = cur - stride;
+ int filter = *raw++;
+ if (filter > 4) return e("invalid filter","Corrupt PNG");
+ // if first row, use special filter that doesn't sample previous row
+ if (j == 0) filter = first_row_filter[filter];
+ // handle first pixel explicitly
+ for (k=0; k < img_n; ++k) {
+ switch(filter) {
+ case F_none : cur[k] = raw[k]; break;
+ case F_sub : cur[k] = raw[k]; break;
+ case F_up : cur[k] = raw[k] + prior[k]; break;
+ case F_avg : cur[k] = raw[k] + (prior[k]>>1); break;
+ case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break;
+ case F_avg_first : cur[k] = raw[k]; break;
+ case F_paeth_first: cur[k] = raw[k]; break;
+ }
+ }
+ if (img_n != out_n) cur[img_n] = 255;
+ raw += img_n;
+ cur += out_n;
+ prior += out_n;
+ // this is a little gross, so that we don't switch per-pixel or per-component
+ if (img_n == out_n) {
+ #define CASE(f) \
+ case f: \
+ for (i=1; i < img_x; ++i, raw+=img_n,cur+=img_n,prior+=img_n) \
+ for (k=0; k < img_n; ++k)
+ switch(filter) {
+ CASE(F_none) cur[k] = raw[k]; break;
+ CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break;
+ CASE(F_up) cur[k] = raw[k] + prior[k]; break;
+ CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break;
+ CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break;
+ CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break;
+ CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break;
+ }
+ #undef CASE
+ } else {
+ assert(img_n+1 == out_n);
+ #define CASE(f) \
+ case f: \
+ for (i=1; i < img_x; ++i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \
+ for (k=0; k < img_n; ++k)
+ switch(filter) {
+ CASE(F_none) cur[k] = raw[k]; break;
+ CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break;
+ CASE(F_up) cur[k] = raw[k] + prior[k]; break;
+ CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break;
+ CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break;
+ CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break;
+ CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break;
+ }
+ #undef CASE
+ }
+ }
+ return 1;
+}
+
+static int compute_transparency(uint8 tc[3], int out_n)
+{
+ uint32 i, pixel_count = img_x * img_y;
+ uint8 *p = out;
+
+ // compute color-based transparency, assuming we've
+ // already got 255 as the alpha value in the output
+ assert(out_n == 2 || out_n == 4);
+
+ p = out;
+ if (out_n == 2) {
+ for (i=0; i < pixel_count; ++i) {
+ p[1] = (p[0] == tc[0] ? 0 : 255);
+ p += 2;
+ }
+ } else {
+ for (i=0; i < pixel_count; ++i) {
+ if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
+ p[3] = 0;
+ p += 4;
+ }
+ }
+ return 1;
+}
+
+static int expand_palette(uint8 *palette, int len, int pal_img_n)
+{
+ uint32 i, pixel_count = img_x * img_y;
+ uint8 *p, *temp_out, *orig = out;
+
+ p = (uint8 *) malloc(pixel_count * pal_img_n);
+ if (p == NULL) return e("outofmem", "Out of memory");
+
+ // between here and free(out) below, exitting would leak
+ temp_out = p;
+
+ if (pal_img_n == 3) {
+ for (i=0; i < pixel_count; ++i) {
+ int n = orig[i]*4;
+ p[0] = palette[n ];
+ p[1] = palette[n+1];
+ p[2] = palette[n+2];
+ p += 3;
+ }
+ } else {
+ for (i=0; i < pixel_count; ++i) {
+ int n = orig[i]*4;
+ p[0] = palette[n ];
+ p[1] = palette[n+1];
+ p[2] = palette[n+2];
+ p[3] = palette[n+3];
+ p += 4;
+ }
+ }
+ free(out);
+ out = temp_out;
+ return 1;
+}
+
+static int parse_png_file(int scan, int req_comp)
+{
+ uint8 palette[1024], pal_img_n=0;
+ uint8 has_trans=0, tc[3];
+ uint32 ioff=0, idata_limit=0, i, pal_len=0;
+ int first=1,k;
+
+ if (!check_png_header()) return 0;
+
+ if (scan == SCAN_type) return 1;
+
+ for(;;first=0) {
+ chunk c = get_chunk_header();
+ if (first && c.type != PNG_TYPE('I','H','D','R'))
+ return e("first not IHDR","Corrupt PNG");
+ switch (c.type) {
+ case PNG_TYPE('I','H','D','R'): {
+ int depth,color,interlace,comp,filter;
+ if (!first) return e("multiple IHDR","Corrupt PNG");
+ if (c.length != 13) return e("bad IHDR len","Corrupt PNG");
+ img_x = get32(); if (img_x > (1 << 24)) return e("too large","Very large image (corrupt?)");
+ img_y = get32(); if (img_y > (1 << 24)) return e("too large","Very large image (corrupt?)");
+ depth = get8(); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only");
+ color = get8(); if (color > 6) return e("bad ctype","Corrupt PNG");
+ if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG");
+ comp = get8(); if (comp) return e("bad comp method","Corrupt PNG");
+ filter= get8(); if (filter) return e("bad filter method","Corrupt PNG");
+ interlace = get8(); if (interlace) return e("interlaced","PNG not supported: interlaced mode");
+ if (!img_x || !img_y) return e("0-pixel image","Corrupt PNG");
+ if (!pal_img_n) {
+ img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
+ if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode");
+ if (scan == SCAN_header) return 1;
+ } else {
+ // if paletted, then pal_n is our final components, and
+ // img_n is # components to decompress/filter.
+ img_n = 1;
+ if ((1 << 30) / img_x / 4 < img_y) return e("too large","Corrupt PNG");
+ // if SCAN_header, have to scan to see if we have a tRNS
+ }
+ break;
+ }
+
+ case PNG_TYPE('P','L','T','E'): {
+ if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG");
+ pal_len = c.length / 3;
+ if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG");
+ for (i=0; i < pal_len; ++i) {
+ palette[i*4+0] = get8u();
+ palette[i*4+1] = get8u();
+ palette[i*4+2] = get8u();
+ palette[i*4+3] = 255;
+ }
+ break;
+ }
+
+ case PNG_TYPE('t','R','N','S'): {
+ if (idata) return e("tRNS after IDAT","Corrupt PNG");
+ if (pal_img_n) {
+ if (scan == SCAN_header) { img_n = 4; return 1; }
+ if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG");
+ if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG");
+ pal_img_n = 4;
+ for (i=0; i < c.length; ++i)
+ palette[i*4+3] = get8u();
+ } else {
+ if (!(img_n & 1)) return e("tRNS with alpha","Corrupt PNG");
+ if (c.length != (uint32) img_n*2) return e("bad tRNS len","Corrupt PNG");
+ has_trans = 1;
+ for (k=0; k < img_n; ++k)
+ tc[k] = (uint8) get16(); // non 8-bit images will be larger
+ }
+ break;
+ }
+
+ case PNG_TYPE('I','D','A','T'): {
+ if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG");
+ if (scan == SCAN_header) { img_n = pal_img_n; return 1; }
+ if (ioff + c.length > idata_limit) {
+ uint8 *p;
+ if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
+ while (ioff + c.length > idata_limit)
+ idata_limit *= 2;
+ p = (uint8 *) realloc(idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory");
+ idata = p;
+ }
+ #ifndef STBI_NO_STDIO
+ if (img_file)
+ {
+ if (fread(idata+ioff,1,c.length,img_file) != c.length) return e("outofdata","Corrupt PNG");
+ }
+ else
+ #endif
+ {
+ memcpy(idata+ioff, img_buffer, c.length);
+ img_buffer += c.length;
+ }
+ ioff += c.length;
+ break;
+ }
+
+ case PNG_TYPE('I','E','N','D'): {
+ uint32 raw_len;
+ if (scan != SCAN_load) return 1;
+ if (idata == NULL) return e("no IDAT","Corrupt PNG");
+ expanded = (uint8 *) stbi_zlib_decode_malloc((char *) idata, ioff, (int *) &raw_len);
+ if (expanded == NULL) return 0; // zlib should set error
+ free(idata); idata = NULL;
+ if ((req_comp == img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
+ img_out_n = img_n+1;
+ else
+ img_out_n = img_n;
+ if (!create_png_image(expanded, raw_len, img_out_n)) return 0;
+ if (has_trans)
+ if (!compute_transparency(tc, img_out_n)) return 0;
+ if (pal_img_n) {
+ // pal_img_n == 3 or 4
+ img_n = pal_img_n; // record the actual colors we had
+ img_out_n = pal_img_n;
+ if (req_comp >= 3) img_out_n = req_comp;
+ if (!expand_palette(palette, pal_len, img_out_n))
+ return 0;
+ }
+ free(expanded); expanded = NULL;
+ return 1;
+ }
+
+ default:
+ // if critical, fail
+ if ((c.type & (1 << 29)) == 0) {
+ #ifndef STBI_NO_FAILURE_STRINGS
+ static char invalid_chunk[] = "XXXX chunk not known";
+ invalid_chunk[0] = (uint8) (c.type >> 24);
+ invalid_chunk[1] = (uint8) (c.type >> 16);
+ invalid_chunk[2] = (uint8) (c.type >> 8);
+ invalid_chunk[3] = (uint8) (c.type >> 0);
+ #endif
+ return e(invalid_chunk, "PNG not supported: unknown chunk type");
+ }
+ skip(c.length);
+ break;
+ }
+ // end of chunk, read and skip CRC
+ get8(); get8(); get8(); get8();
+ }
+}
+
+static unsigned char *do_png(int *x, int *y, int *n, int req_comp)
+{
+ unsigned char *result=NULL;
+ if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error");
+ if (parse_png_file(SCAN_load, req_comp)) {
+ result = out;
+ out = NULL;
+ if (req_comp && req_comp != img_out_n) {
+ result = convert_format(result, img_out_n, req_comp);
+ if (result == NULL) return result;
+ }
+ *x = img_x;
+ *y = img_y;
+ if (n) *n = img_n;
+ }
+ free(out); out = NULL;
+ free(expanded); expanded = NULL;
+ free(idata); idata = NULL;
+
+ return result;
+}
+
+#ifndef STBI_NO_STDIO
+unsigned char *stbi_png_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return do_png(x,y,comp,req_comp);
+}
+
+unsigned char *stbi_png_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_png_load_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+#endif
+
+unsigned char *stbi_png_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer,len);
+ return do_png(x,y,comp,req_comp);
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_png_test_file(FILE *f)
+{
+ int n,r;
+ n = ftell(f);
+ start_file(f);
+ r = parse_png_file(SCAN_type,STBI_default);
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_png_test_memory(stbi_uc const *buffer, int len)
+{
+ start_mem(buffer, len);
+ return parse_png_file(SCAN_type,STBI_default);
+}
+
+// TODO: load header from png
+#ifndef STBI_NO_STDIO
+extern int stbi_png_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+// Microsoft/Windows BMP image
+
+static int bmp_test(void)
+{
+ int sz;
+ if (get8() != 'B') return 0;
+ if (get8() != 'M') return 0;
+ get32le(); // discard filesize
+ get16le(); // discard reserved
+ get16le(); // discard reserved
+ get32le(); // discard data offset
+ sz = get32le();
+ if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1;
+ return 0;
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_bmp_test_file (FILE *f)
+{
+ int r,n = ftell(f);
+ start_file(f);
+ r = bmp_test();
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_bmp_test_memory (stbi_uc const *buffer, int len)
+{
+ start_mem(buffer, len);
+ return bmp_test();
+}
+
+// returns 0..31 for the highest set bit
+static int high_bit(unsigned int z)
+{
+ int n=0;
+ if (z == 0) return -1;
+ if (z >= 0x10000) n += 16, z >>= 16;
+ if (z >= 0x00100) n += 8, z >>= 8;
+ if (z >= 0x00010) n += 4, z >>= 4;
+ if (z >= 0x00004) n += 2, z >>= 2;
+ if (z >= 0x00002) n += 1, z >>= 1;
+ return n;
+}
+
+static int bitcount(unsigned int a)
+{
+ a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
+ a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
+ a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
+ a = (a + (a >> 8)); // max 16 per 8 bits
+ a = (a + (a >> 16)); // max 32 per 8 bits
+ return a & 0xff;
+}
+
+static int shiftsigned(int v, int shift, int bits)
+{
+ int result;
+ int z=0;
+
+ if (shift < 0) v <<= -shift;
+ else v >>= shift;
+ result = v;
+
+ z = bits;
+ while (z < 8) {
+ result += v >> z;
+ z += bits;
+ }
+ return result;
+}
+
+static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp)
+{
+ unsigned int mr=0,mg=0,mb=0,ma=0;
+ stbi_uc pal[256][4];
+ int psize=0,i,j,compress=0,width;
+ int bpp, flip_vertically, pad, target, offset, hsz;
+ if (get8() != 'B' || get8() != 'M') return epuc("not BMP", "Corrupt BMP");
+ get32le(); // discard filesize
+ get16le(); // discard reserved
+ get16le(); // discard reserved
+ offset = get32le();
+ hsz = get32le();
+ if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown");
+ failure_reason = "bad BMP";
+ if (hsz == 12) {
+ img_x = get16le();
+ img_y = get16le();
+ } else {
+ img_x = get32le();
+ img_y = get32le();
+ }
+ if (get16le() != 1) return 0;
+ bpp = get16le();
+ if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit");
+ flip_vertically = ((int) img_y) > 0;
+ img_y = abs((int) img_y);
+ if (hsz == 12) {
+ if (bpp < 24)
+ psize = (offset - 14 - 24) / 3;
+ } else {
+ compress = get32le();
+ if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE");
+ get32le(); // discard sizeof
+ get32le(); // discard hres
+ get32le(); // discard vres
+ get32le(); // discard colorsused
+ get32le(); // discard max important
+ if (hsz == 40 || hsz == 56) {
+ if (hsz == 56) {
+ get32le();
+ get32le();
+ get32le();
+ get32le();
+ }
+ if (bpp == 16 || bpp == 32) {
+ mr = mg = mb = 0;
+ if (compress == 0) {
+ if (bpp == 32) {
+ mr = 0xff << 16;
+ mg = 0xff << 8;
+ mb = 0xff << 0;
+ } else {
+ mr = 31 << 10;
+ mg = 31 << 5;
+ mb = 31 << 0;
+ }
+ } else if (compress == 3) {
+ mr = get32le();
+ mg = get32le();
+ mb = get32le();
+ // not documented, but generated by photoshop and handled by mspaint
+ if (mr == mg && mg == mb) {
+ // ?!?!?
+ return NULL;
+ }
+ } else
+ return NULL;
+ }
+ } else {
+ assert(hsz == 108);
+ mr = get32le();
+ mg = get32le();
+ mb = get32le();
+ ma = get32le();
+ get32le(); // discard color space
+ for (i=0; i < 12; ++i)
+ get32le(); // discard color space parameters
+ }
+ if (bpp < 16)
+ psize = (offset - 14 - hsz) >> 2;
+ }
+ img_n = ma ? 4 : 3;
+ if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
+ target = req_comp;
+ else
+ target = img_n; // if they want monochrome, we'll post-convert
+ out = (stbi_uc *) malloc(target * img_x * img_y);
+ if (!out) return epuc("outofmem", "Out of memory");
+ if (bpp < 16) {
+ int z=0;
+ if (psize == 0 || psize > 256) return epuc("invalid", "Corrupt BMP");
+ for (i=0; i < psize; ++i) {
+ pal[i][2] = get8();
+ pal[i][1] = get8();
+ pal[i][0] = get8();
+ if (hsz != 12) get8();
+ pal[i][3] = 255;
+ }
+ skip(offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4));
+ if (bpp == 4) width = (img_x + 1) >> 1;
+ else if (bpp == 8) width = img_x;
+ else return epuc("bad bpp", "Corrupt BMP");
+ pad = (-width)&3;
+ for (j=0; j < (int) img_y; ++j) {
+ for (i=0; i < (int) img_x; i += 2) {
+ int v=get8(),v2=0;
+ if (bpp == 4) {
+ v2 = v & 15;
+ v >>= 4;
+ }
+ out[z++] = pal[v][0];
+ out[z++] = pal[v][1];
+ out[z++] = pal[v][2];
+ if (target == 4) out[z++] = 255;
+ if (i+1 == (int) img_x) break;
+ v = (bpp == 8) ? get8() : v2;
+ out[z++] = pal[v][0];
+ out[z++] = pal[v][1];
+ out[z++] = pal[v][2];
+ if (target == 4) out[z++] = 255;
+ }
+ skip(pad);
+ }
+ } else {
+ int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
+ int z = 0;
+ int easy=0;
+ skip(offset - 14 - hsz);
+ if (bpp == 24) width = 3 * img_x;
+ else if (bpp == 16) width = 2*img_x;
+ else /* bpp = 32 and pad = 0 */ width=0;
+ pad = (-width) & 3;
+ if (bpp == 24) {
+ easy = 1;
+ } else if (bpp == 32) {
+ if (mb == 0xff && mg == 0xff00 && mr == 0xff000000 && ma == 0xff000000)
+ easy = 2;
+ }
+ if (!easy) {
+ if (!mr || !mg || !mb) return epuc("bad masks", "Corrupt BMP");
+ // right shift amt to put high bit in position #7
+ rshift = high_bit(mr)-7; rcount = bitcount(mr);
+ gshift = high_bit(mg)-7; gcount = bitcount(mr);
+ bshift = high_bit(mb)-7; bcount = bitcount(mr);
+ ashift = high_bit(ma)-7; acount = bitcount(mr);
+ }
+ for (j=0; j < (int) img_y; ++j) {
+ if (easy) {
+ for (i=0; i < (int) img_x; ++i) {
+ int a;
+ out[z+2] = get8();
+ out[z+1] = get8();
+ out[z+0] = get8();
+ z += 3;
+ a = (easy == 2 ? get8() : 255);
+ if (target == 4) out[z++] = a;
+ }
+ } else {
+ for (i=0; i < (int) img_x; ++i) {
+ uint32 v = (bpp == 16 ? get16le() : get32le());
+ int a;
+ out[z++] = shiftsigned(v & mr, rshift, rcount);
+ out[z++] = shiftsigned(v & mg, gshift, gcount);
+ out[z++] = shiftsigned(v & mb, bshift, bcount);
+ a = (ma ? shiftsigned(v & ma, ashift, acount) : 255);
+ if (target == 4) out[z++] = a;
+ }
+ }
+ skip(pad);
+ }
+ }
+ if (flip_vertically) {
+ stbi_uc t;
+ for (j=0; j < (int) img_y>>1; ++j) {
+ stbi_uc *p1 = out + j *img_x*target;
+ stbi_uc *p2 = out + (img_y-1-j)*img_x*target;
+ for (i=0; i < (int) img_x*target; ++i) {
+ t = p1[i], p1[i] = p2[i], p2[i] = t;
+ }
+ }
+ }
+
+ if (req_comp && req_comp != target) {
+ out = convert_format(out, target, req_comp);
+ if (out == NULL) return out; // convert_format frees input on failure
+ }
+
+ *x = img_x;
+ *y = img_y;
+ if (comp) *comp = target;
+ return out;
+}
+
+#ifndef STBI_NO_STDIO
+stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_bmp_load_from_file(f, x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+
+stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return bmp_load(x,y,comp,req_comp);
+}
+#endif
+
+stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer, len);
+ return bmp_load(x,y,comp,req_comp);
+}
+
+// Targa Truevision - TGA
+// by Jonathan Dummer
+
+static int tga_test(void)
+{
+ int sz;
+ get8u(); // discard Offset
+ sz = get8u(); // color type
+ if( sz > 1 ) return 0; // only RGB or indexed allowed
+ sz = get8u(); // image type
+ if( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE
+ get16(); // discard palette start
+ get16(); // discard palette length
+ get8(); // discard bits per palette color entry
+ get16(); // discard x origin
+ get16(); // discard y origin
+ if( get16() < 1 ) return 0; // test width
+ if( get16() < 1 ) return 0; // test height
+ sz = get8(); // bits per pixel
+ if( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed
+ return 1; // seems to have passed everything
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_tga_test_file (FILE *f)
+{
+ int r,n = ftell(f);
+ start_file(f);
+ r = tga_test();
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_tga_test_memory (stbi_uc const *buffer, int len)
+{
+ start_mem(buffer, len);
+ return tga_test();
+}
+
+static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp)
+{
+ // read in the TGA header stuff
+ int tga_offset = get8u();
+ int tga_indexed = get8u();
+ int tga_image_type = get8u();
+ int tga_is_RLE = 0;
+ int tga_palette_start = get16le();
+ int tga_palette_len = get16le();
+ int tga_palette_bits = get8u();
+ int tga_x_origin = get16le();
+ int tga_y_origin = get16le();
+ int tga_width = get16le();
+ int tga_height = get16le();
+ int tga_bits_per_pixel = get8u();
+ int tga_inverted = get8u();
+ // image data
+ unsigned char *tga_data;
+ unsigned char *tga_palette = NULL;
+ int i, j;
+ unsigned char raw_data[4];
+ unsigned char trans_data[4];
+ int RLE_count = 0;
+ int RLE_repeating = 0;
+ int read_next_pixel = 1;
+ // do a tiny bit of precessing
+ if( tga_image_type >= 8 )
+ {
+ tga_image_type -= 8;
+ tga_is_RLE = 1;
+ }
+ /* int tga_alpha_bits = tga_inverted & 15; */
+ tga_inverted = 1 - ((tga_inverted >> 5) & 1);
+
+ // error check
+ if( //(tga_indexed) ||
+ (tga_width < 1) || (tga_height < 1) ||
+ (tga_image_type < 1) || (tga_image_type > 3) ||
+ ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) &&
+ (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32))
+ )
+ {
+ return NULL;
+ }
+
+ // If I'm paletted, then I'll use the number of bits from the palette
+ if( tga_indexed )
+ {
+ tga_bits_per_pixel = tga_palette_bits;
+ }
+
+ // tga info
+ *x = tga_width;
+ *y = tga_height;
+ if( (req_comp < 1) || (req_comp > 4) )
+ {
+ // just use whatever the file was
+ req_comp = tga_bits_per_pixel / 8;
+ *comp = req_comp;
+ } else
+ {
+ // force a new number of components
+ *comp = tga_bits_per_pixel/8;
+ }
+ tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp );
+
+ // skip to the data's starting position (offset usually = 0)
+ skip( tga_offset );
+ // do I need to load a palette?
+ if( tga_indexed )
+ {
+ // any data to skip? (offset usually = 0)
+ skip( tga_palette_start );
+ // load the palette
+ tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 );
+ getn( tga_palette, tga_palette_len * tga_palette_bits / 8 );
+ }
+ // load the data
+ for( i = 0; i < tga_width * tga_height; ++i )
+ {
+ // if I'm in RLE mode, do I need to get a RLE chunk?
+ if( tga_is_RLE )
+ {
+ if( RLE_count == 0 )
+ {
+ // yep, get the next byte as a RLE command
+ int RLE_cmd = get8u();
+ RLE_count = 1 + (RLE_cmd & 127);
+ RLE_repeating = RLE_cmd >> 7;
+ read_next_pixel = 1;
+ } else if( !RLE_repeating )
+ {
+ read_next_pixel = 1;
+ }
+ } else
+ {
+ read_next_pixel = 1;
+ }
+ // OK, if I need to read a pixel, do it now
+ if( read_next_pixel )
+ {
+ // load however much data we did have
+ if( tga_indexed )
+ {
+ // read in 1 byte, then perform the lookup
+ int pal_idx = get8u();
+ if( pal_idx >= tga_palette_len )
+ {
+ // invalid index
+ pal_idx = 0;
+ }
+ pal_idx *= tga_bits_per_pixel / 8;
+ for( j = 0; j*8 < tga_bits_per_pixel; ++j )
+ {
+ raw_data[j] = tga_palette[pal_idx+j];
+ }
+ } else
+ {
+ // read in the data raw
+ for( j = 0; j*8 < tga_bits_per_pixel; ++j )
+ {
+ raw_data[j] = get8u();
+ }
+ }
+ // convert raw to the intermediate format
+ switch( tga_bits_per_pixel )
+ {
+ case 8:
+ // Luminous => RGBA
+ trans_data[0] = raw_data[0];
+ trans_data[1] = raw_data[0];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = 255;
+ break;
+ case 16:
+ // Luminous,Alpha => RGBA
+ trans_data[0] = raw_data[0];
+ trans_data[1] = raw_data[0];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = raw_data[1];
+ break;
+ case 24:
+ // BGR => RGBA
+ trans_data[0] = raw_data[2];
+ trans_data[1] = raw_data[1];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = 255;
+ break;
+ case 32:
+ // BGRA => RGBA
+ trans_data[0] = raw_data[2];
+ trans_data[1] = raw_data[1];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = raw_data[3];
+ break;
+ }
+ // clear the reading flag for the next pixel
+ read_next_pixel = 0;
+ } // end of reading a pixel
+ // convert to final format
+ switch( req_comp )
+ {
+ case 1:
+ // RGBA => Luminance
+ tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]);
+ break;
+ case 2:
+ // RGBA => Luminance,Alpha
+ tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]);
+ tga_data[i*req_comp+1] = trans_data[3];
+ break;
+ case 3:
+ // RGBA => RGB
+ tga_data[i*req_comp+0] = trans_data[0];
+ tga_data[i*req_comp+1] = trans_data[1];
+ tga_data[i*req_comp+2] = trans_data[2];
+ break;
+ case 4:
+ // RGBA => RGBA
+ tga_data[i*req_comp+0] = trans_data[0];
+ tga_data[i*req_comp+1] = trans_data[1];
+ tga_data[i*req_comp+2] = trans_data[2];
+ tga_data[i*req_comp+3] = trans_data[3];
+ break;
+ }
+ // in case we're in RLE mode, keep counting down
+ --RLE_count;
+ }
+ // do I need to invert the image?
+ if( tga_inverted )
+ {
+ for( j = 0; j*2 < tga_height; ++j )
+ {
+ int index1 = j * tga_width * req_comp;
+ int index2 = (tga_height - 1 - j) * tga_width * req_comp;
+ for( i = tga_width * req_comp; i > 0; --i )
+ {
+ unsigned char temp = tga_data[index1];
+ tga_data[index1] = tga_data[index2];
+ tga_data[index2] = temp;
+ ++index1;
+ ++index2;
+ }
+ }
+ }
+ // clear my palette, if I had one
+ if( tga_palette != NULL )
+ {
+ free( tga_palette );
+ }
+ // the things I do to get rid of an error message, and yet keep
+ // Microsoft's C compilers happy... [8^(
+ tga_palette_start = tga_palette_len = tga_palette_bits =
+ tga_x_origin = tga_y_origin = 0;
+ // OK, done
+ return tga_data;
+}
+
+#ifndef STBI_NO_STDIO
+stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_tga_load_from_file(f, x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+
+stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return tga_load(x,y,comp,req_comp);
+}
+#endif
+
+stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer, len);
+ return tga_load(x,y,comp,req_comp);
+}
+
+
+// *************************************************************************************************
+// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicholas Schulz, tweaked by STB
+
+static int psd_test(void)
+{
+ if (get32() != 0x38425053) return 0; // "8BPS"
+ else return 1;
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_psd_test_file(FILE *f)
+{
+ int r,n = ftell(f);
+ start_file(f);
+ r = psd_test();
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_psd_test_memory(stbi_uc const *buffer, int len)
+{
+ start_mem(buffer, len);
+ return psd_test();
+}
+
+static stbi_uc *psd_load(int *x, int *y, int *comp, int req_comp)
+{
+ int pixelCount;
+ int channelCount, compression;
+ int channel, i, count, len;
+ int w,h;
+
+ // Check identifier
+ if (get32() != 0x38425053) // "8BPS"
+ return epuc("not PSD", "Corrupt PSD image");
+
+ // Check file type version.
+ if (get16() != 1)
+ return epuc("wrong version", "Unsupported version of PSD image");
+
+ // Skip 6 reserved bytes.
+ skip( 6 );
+
+ // Read the number of channels (R, G, B, A, etc).
+ channelCount = get16();
+ if (channelCount < 0 || channelCount > 16)
+ return epuc("wrong channel count", "Unsupported number of channels in PSD image");
+
+ // Read the rows and columns of the image.
+ h = get32();
+ w = get32();
+
+ // Make sure the depth is 8 bits.
+ if (get16() != 8)
+ return epuc("unsupported bit depth", "PSD bit depth is not 8 bit");
+
+ // Make sure the color mode is RGB.
+ // Valid options are:
+ // 0: Bitmap
+ // 1: Grayscale
+ // 2: Indexed color
+ // 3: RGB color
+ // 4: CMYK color
+ // 7: Multichannel
+ // 8: Duotone
+ // 9: Lab color
+ if (get16() != 3)
+ return epuc("wrong color format", "PSD is not in RGB color format");
+
+ // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
+ skip(get32() );
+
+ // Skip the image resources. (resolution, pen tool paths, etc)
+ skip( get32() );
+
+ // Skip the reserved data.
+ skip( get32() );
+
+ // Find out if the data is compressed.
+ // Known values:
+ // 0: no compression
+ // 1: RLE compressed
+ compression = get16();
+ if (compression > 1)
+ return epuc("unknown compression type", "PSD has an unknown compression format");
+
+ // Create the destination image.
+ out = (stbi_uc *) malloc(4 * w*h);
+ if (!out) return epuc("outofmem", "Out of memory");
+ pixelCount = w*h;
+
+ // Initialize the data to zero.
+ //memset( out, 0, pixelCount * 4 );
+
+ // Finally, the image data.
+ if (compression) {
+ // RLE as used by .PSD and .TIFF
+ // Loop until you get the number of unpacked bytes you are expecting:
+ // Read the next source byte into n.
+ // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
+ // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
+ // Else if n is 128, noop.
+ // Endloop
+
+ // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data,
+ // which we're going to just skip.
+ skip( h * channelCount * 2 );
+
+ // Read the RLE data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ uint8 *p;
+
+ p = out+channel;
+ if (channel >= channelCount) {
+ // Fill this channel with default data.
+ for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4;
+ } else {
+ // Read the RLE data.
+ count = 0;
+ while (count < pixelCount) {
+ len = get8();
+ if (len == 128) {
+ // No-op.
+ } else if (len < 128) {
+ // Copy next len+1 bytes literally.
+ len++;
+ count += len;
+ while (len) {
+ *p = get8();
+ p += 4;
+ len--;
+ }
+ } else if (len > 128) {
+ uint32 val;
+ // Next -len+1 bytes in the dest are replicated from next source byte.
+ // (Interpret len as a negative 8-bit int.)
+ len ^= 0x0FF;
+ len += 2;
+ val = get8();
+ count += len;
+ while (len) {
+ *p = val;
+ p += 4;
+ len--;
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+ // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
+ // where each channel consists of an 8-bit value for each pixel in the image.
+
+ // Read the data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ uint8 *p;
+
+ p = out + channel;
+ if (channel > channelCount) {
+ // Fill this channel with default data.
+ for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4;
+ } else {
+ // Read the data.
+ count = 0;
+ for (i = 0; i < pixelCount; i++)
+ *p = get8(), p += 4;
+ }
+ }
+ }
+
+ if (req_comp && req_comp != 4) {
+ img_x = w;
+ img_y = h;
+ out = convert_format(out, 4, req_comp);
+ if (out == NULL) return out; // convert_format frees input on failure
+ }
+
+ if (comp) *comp = channelCount;
+ *y = h;
+ *x = w;
+
+ return out;
+}
+
+#ifndef STBI_NO_STDIO
+stbi_uc *stbi_psd_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_psd_load_from_file(f, x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+
+stbi_uc *stbi_psd_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return psd_load(x,y,comp,req_comp);
+}
+#endif
+
+stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer, len);
+ return psd_load(x,y,comp,req_comp);
+}
+
+
+// *************************************************************************************************
+// Radiance RGBE HDR loader
+// originally by Nicolas Schulz
+#ifndef STBI_NO_HDR
+static int hdr_test(void)
+{
+ char *signature = "#?RADIANCE\n";
+ int i;
+ for (i=0; signature[i]; ++i)
+ if (get8() != signature[i])
+ return 0;
+ return 1;
+}
+
+int stbi_hdr_test_memory(stbi_uc const *buffer, int len)
+{
+ start_mem(buffer, len);
+ return hdr_test();
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_hdr_test_file(FILE *f)
+{
+ int r,n = ftell(f);
+ start_file(f);
+ r = hdr_test();
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+#define HDR_BUFLEN 1024
+static char *hdr_gettoken(char *buffer)
+{
+ int len=0;
+ char *s = buffer, c = '\0';
+
+ c = get8();
+
+ while (!at_eof() && c != '\n') {
+ buffer[len++] = c;
+ if (len == HDR_BUFLEN-1) {
+ // flush to end of line
+ while (!at_eof() && get8() != '\n')
+ ;
+ break;
+ }
+ c = get8();
+ }
+
+ buffer[len] = 0;
+ return buffer;
+}
+
+static void hdr_convert(float *output, stbi_uc *input, int req_comp)
+{
+ if( input[3] != 0 ) {
+ float f1;
+ // Exponent
+ f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
+ if (req_comp <= 2)
+ output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
+ else {
+ output[0] = input[0] * f1;
+ output[1] = input[1] * f1;
+ output[2] = input[2] * f1;
+ }
+ if (req_comp == 2) output[1] = 1;
+ if (req_comp == 4) output[3] = 1;
+ } else {
+ switch (req_comp) {
+ case 4: output[3] = 1; /* fallthrough */
+ case 3: output[0] = output[1] = output[2] = 0;
+ break;
+ case 2: output[1] = 1; /* fallthrough */
+ case 1: output[0] = 0;
+ break;
+ }
+ }
+}
+
+
+static float *hdr_load(int *x, int *y, int *comp, int req_comp)
+{
+ char buffer[HDR_BUFLEN];
+ char *token;
+ int valid = 0;
+ int width, height;
+ stbi_uc *scanline;
+ float *hdr_data;
+ int len;
+ unsigned char count, value;
+ int i, j, k, c1,c2, z;
+
+
+ // Check identifier
+ if (strcmp(hdr_gettoken(buffer), "#?RADIANCE") != 0)
+ return epf("not HDR", "Corrupt HDR image");
+
+ // Parse header
+ while(1) {
+ token = hdr_gettoken(buffer);
+ if (token[0] == 0) break;
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
+ }
+
+ if (!valid) return epf("unsupported format", "Unsupported HDR format");
+
+ // Parse width and height
+ // can't use sscanf() if we're not using stdio!
+ token = hdr_gettoken(buffer);
+ if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ height = strtol(token, &token, 10);
+ while (*token == ' ') ++token;
+ if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ width = strtol(token, NULL, 10);
+
+ *x = width;
+ *y = height;
+
+ *comp = 3;
+ if (req_comp == 0) req_comp = 3;
+
+ // Read data
+ hdr_data = (float *) malloc(height * width * req_comp * sizeof(float));
+
+ // Load image data
+ // image data is stored as some number of sca
+ if( width < 8 || width >= 32768) {
+ // Read flat data
+ for (j=0; j < height; ++j) {
+ for (i=0; i < width; ++i) {
+ stbi_uc rgbe[4];
+ main_decode_loop:
+ getn(rgbe, 4);
+ hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
+ }
+ }
+ } else {
+ // Read RLE-encoded data
+ scanline = NULL;
+
+ for (j = 0; j < height; ++j) {
+ c1 = get8();
+ c2 = get8();
+ len = get8();
+ if (c1 != 2 || c2 != 2 || (len & 0x80)) {
+ // not run-length encoded, so we have to actually use THIS data as a decoded
+ // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
+ stbi_uc rgbe[4] = { c1,c2,len, get8() };
+ hdr_convert(hdr_data, rgbe, req_comp);
+ i = 1;
+ j = 0;
+ free(scanline);
+ goto main_decode_loop; // yes, this is fucking insane; blame the fucking insane format
+ }
+ len <<= 8;
+ len |= get8();
+ if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); }
+ if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4);
+
+ for (k = 0; k < 4; ++k) {
+ i = 0;
+ while (i < width) {
+ count = get8();
+ if (count > 128) {
+ // Run
+ value = get8();
+ count -= 128;
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = value;
+ } else {
+ // Dump
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = get8();
+ }
+ }
+ }
+ for (i=0; i < width; ++i)
+ hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);
+ }
+ free(scanline);
+ }
+
+ return hdr_data;
+}
+
+#ifndef STBI_NO_STDIO
+float *stbi_hdr_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ start_file(f);
+ return hdr_load(x,y,comp,req_comp);
+}
+#endif
+
+float *stbi_hdr_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ start_mem(buffer, len);
+ return hdr_load(x,y,comp,req_comp);
+}
+
+#endif // STBI_NO_HDR
+
+/////////////////////// write image ///////////////////////
+
+#ifndef STBI_NO_WRITE
+
+static void write8(FILE *f, int x) { uint8 z = (uint8) x; fwrite(&z,1,1,f); }
+
+static void writefv(FILE *f, char *fmt, va_list v)
+{
+ while (*fmt) {
+ switch (*fmt++) {
+ case ' ': break;
+ case '1': { uint8 x = va_arg(v, int); write8(f,x); break; }
+ case '2': { int16 x = va_arg(v, int); write8(f,x); write8(f,x>>8); break; }
+ case '4': { int32 x = va_arg(v, int); write8(f,x); write8(f,x>>8); write8(f,x>>16); write8(f,x>>24); break; }
+ default:
+ assert(0);
+ va_end(v);
+ return;
+ }
+ }
+}
+
+static void writef(FILE *f, char *fmt, ...)
+{
+ va_list v;
+ va_start(v, fmt);
+ writefv(f,fmt,v);
+ va_end(v);
+}
+
+static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad)
+{
+ uint8 bg[3] = { 255, 0, 255}, px[3];
+ uint32 zero = 0;
+ int i,j,k, j_end;
+
+ if (vdir < 0)
+ j_end = -1, j = y-1;
+ else
+ j_end = y, j = 0;
+
+ for (; j != j_end; j += vdir) {
+ for (i=0; i < x; ++i) {
+ uint8 *d = (uint8 *) data + (j*x+i)*comp;
+ if (write_alpha < 0)
+ fwrite(&d[comp-1], 1, 1, f);
+ switch (comp) {
+ case 1:
+ case 2: writef(f, "111", d[0],d[0],d[0]);
+ break;
+ case 4:
+ if (!write_alpha) {
+ for (k=0; k < 3; ++k)
+ px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;
+ writef(f, "111", px[1-rgb_dir],px[1],px[1+rgb_dir]);
+ break;
+ }
+ /* FALLTHROUGH */
+ case 3:
+ writef(f, "111", d[1-rgb_dir],d[1],d[1+rgb_dir]);
+ break;
+ }
+ if (write_alpha > 0)
+ fwrite(&d[comp-1], 1, 1, f);
+ }
+ fwrite(&zero,scanline_pad,1,f);
+ }
+}
+
+static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, char *fmt, ...)
+{
+ FILE *f = fopen(filename, "wb");
+ if (f) {
+ va_list v;
+ va_start(v, fmt);
+ writefv(f, fmt, v);
+ va_end(v);
+ write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
+ fclose(f);
+ }
+ return f != NULL;
+}
+
+int stbi_write_bmp(char const *filename, int x, int y, int comp, void *data)
+{
+ int pad = (-x*3) & 3;
+ return outfile(filename,-1,-1,x,y,comp,data,0,pad,
+ "11 4 22 4" "4 44 22 444444",
+ 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
+ 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
+}
+
+int stbi_write_tga(char const *filename, int x, int y, int comp, void *data)
+{
+ int has_alpha = !(comp & 1);
+ return outfile(filename, -1,-1, x, y, comp, data, has_alpha, 0,
+ "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha);
+}
+
+// any other image formats that do interleaved rgb data?
+// PNG: requires adler32,crc32 -- significant amount of code
+// PSD: no, channels output separately
+// TIFF: no, stripwise-interleaved... i think
+
+#endif // STBI_NO_WRITE
diff --git a/doc/reference/cogl/Makefile.am b/doc/reference/cogl/Makefile.am new file mode 100644 index 00000000..39daa63d --- /dev/null +++ b/doc/reference/cogl/Makefile.am @@ -0,0 +1,93 @@ +## Process this file with automake to produce Makefile.in + +# We require automake 1.6 at least. +AUTOMAKE_OPTIONS = 1.6 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=cogl + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=../../../clutter/cogl + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS=--deprecated-guards="CLUTTER_DISABLE_DEPRECATED" + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS=\ + --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/glib \ + --extra-dir=$(GDPIXBUF_PREFIX)/share/gtk-doc/html/gdk-pixbuf \ + --extra-dir=$(top_srcdir)/doc/reference/clutter/html + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=\ + $(top_srcdir)/clutter/cogl/*.h \ + $(top_srcdir)/clutter/cogl/common/*.h +CFILE_GLOB=\ + $(top_srcdir)/clutter/cogl/common/*.c + +# Header files to ignore when scanning. +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES=\ + cogl-bitmap.h \ + cogl-context.h \ + cogl-defines.h \ + cogl-internal.h \ + cogl-primitives.h \ + cogl-texture.h + +EXTRA_HFILES= + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files= + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) + +INCLUDES=-I$(top_srcdir) -I$(top_srcdir)/clutter $(CLUTTER_CFLAGS) +GTKDOC_LIBS=$(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la $(CLUTTER_LIBS) + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST = diff --git a/doc/reference/cogl/cogl-docs.sgml b/doc/reference/cogl/cogl-docs.sgml new file mode 100644 index 00000000..bac6e42b --- /dev/null +++ b/doc/reference/cogl/cogl-docs.sgml @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"> +<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude"> + <bookinfo> + <title>COGL Reference Manual</title> + <releaseinfo> + </releaseinfo> + + <copyright> + <year>2008</year> + <holder>OpenedHand LTD</holder> + </copyright> + + <legalnotice> + <para> + Permission is granted to copy, distribute and/or modify this + document under the terms of the <citetitle>GNU Free + Documentation License</citetitle>, Version 1.1 or any later + version published by the Free Software Foundation with no + Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. You may obtain a copy of the <citetitle>GNU Free + Documentation License</citetitle> from the Free Software + Foundation by visiting <ulink type="http" + url="http://www.fsf.org">their Web site</ulink> or by writing + to: + + <address> + The Free Software Foundation, Inc., + <street>59 Temple Place</street> - Suite 330, + <city>Boston</city>, <state>MA</state> <postcode>02111-1307</postcode> +, + <country>USA</country> + </address> + </para> + </legalnotice> + + </bookinfo> + + <chapter> + <title>COGL - GL Abstraction API</title> + + <section id="cogl-intro"> + <title>About COGL</title> + <para> + FIXME + </para> + </section> + + <xi:include href="xml/cogl.xml"/> + <xi:include href="xml/cogl-primitives.xml"/> + <xi:include href="xml/cogl-util.xml"/> + <xi:include href="xml/cogl-texture.xml"/> + <xi:include href="xml/cogl-shaders.xml"/> + <xi:include href="xml/cogl-offscreen.xml"/> + </chapter> +</book> diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt new file mode 100644 index 00000000..33d49c9b --- /dev/null +++ b/doc/reference/cogl/cogl-sections.txt @@ -0,0 +1,148 @@ +<SECTION> +<FILE>cogl</FILE> +<TITLE>General API</TITLE> +COGL_PIXEL_FORMAT_24 +COGL_PIXEL_FORMAT_32 +COGL_A_BIT +COGL_BGR_BIT +COGL_AFIRST_BIT +COGL_PREMULT_BIT +COGL_UNORDERED_MASK +COGL_UNPREMULT_MASK +CoglPixelFormat +CoglBufferTarget +<SUBSECTION> +cogl_perspective +cogl_setup_viewport +cogl_get_modelview_matrix +cogl_get_projection_matrix +cogl_get_viewport +<SUBSECTION> +cogl_push_matrix +cogl_pop_matrix +cogl_scale +cogl_translatex +cogl_translate +cogl_rotatex +cogl_rotate +<SUBSECTION> +cogl_clip_set +cogl_clip_unset +<SUBSECTION> +cogl_enable_depth_test +cogl_alpha_func +cogl_fog_set +</SECTION> + +<SECTION> +<FILE>cogl-util</FILE> +<TITLE>Utility API</TITLE> +cogl_create_context +cogl_destroy_context +COGL_INVALID_HANDLE +CoglHandle +CoglFuncPtr +<SUBSECTION> +CoglFeatureFlags +cogl_get_features +cogl_features_available +<SUBSECTION> +cogl_get_proc_address +cogl_check_extension +cogl_get_bitmasks +cogl_paint_init +<SUBSECTION> +cogl_util_next_p2 +</SECTION> + +<SECTION> +<FILE>cogl-primitives</FILE> +<TITLE>Primitives</TITLE> +cogl_color +cogl_fast_fill_rectangle +cogl_fast_fill_rectanglex +cogl_fast_fill_trapezoid +cogl_fast_fill_trapezoidx +cogl_fill +cogl_stroke +cogl_path_move_to +cogl_path_move_to_rel +cogl_path_line_to +cogl_path_line_to_rel +cogl_path_h_line_to +cogl_path_v_line_to +cogl_path_h_line_to_rel +cogl_path_v_line_to_rel +cogl_path_arc +cogl_path_arc_rel +cogl_path_bezier2_to +cogl_path_bezier2_to_rel +cogl_path_bezier3_to +cogl_path_bezier3_to_rel +cogl_path_close +cogl_line +cogl_polyline +cogl_polygon +cogl_rectangle +cogl_arc +cogl_ellipse +cogl_round_rectangle +</SECTION> + +<SECTION> +<FILE>cogl-texture</FILE> +<TITLE>Textures</TITLE> +CoglTextureVertex +cogl_texture_new_with_size +cogl_texture_new_from_file +cogl_texture_new_from_data +cogl_texture_new_from_foreign +cogl_is_texture +cogl_texture_get_width +cogl_texture_get_height +cogl_texture_get_format +cogl_texture_get_rowstride +cogl_texture_get_max_waste +cogl_texture_get_min_filter +cogl_texture_get_mag_filter +cogl_texture_is_sliced +cogl_texture_get_gl_texture +cogl_texture_get_data +cogl_texture_set_filters +cogl_texture_set_region +cogl_texture_ref +cogl_texture_unref +cogl_texture_rectangle +cogl_texture_polygon +</SECTION> + +<SECTION> +<FILE>cogl-shaders</FILE> +<TITLE>Shaders and Programmable Pipeline</TITLE> +cogl_create_shader +cogl_shader_destroy +cogl_shader_source +cogl_shader_compile +cogl_shader_get_info_log +cogl_shader_get_parameteriv +<SUBSECTION> +cogl_create_program +cogl_program_destroy +cogl_program_attach_shader +cogl_program_link +cogl_program_use +cogl_program_get_uniform_location +cogl_program_uniform_1f +</SECTION> + +<SECTION> +<FILE>cogl-offscreen</FILE> +<TITLE>Offscreen Buffers</TITLE> +cogl_offscreen_new_to_texture +cogl_offscreen_new_multisample +cogl_offscreen_ref +cogl_offscreen_unref +cogl_offscreen_blit +cogl_offscreen_blit_region +cogl_draw_buffer +</SECTION> diff --git a/gl/Makefile.am b/gl/Makefile.am index 98949b26..17e5ed46 100644 --- a/gl/Makefile.am +++ b/gl/Makefile.am @@ -1,19 +1,36 @@ -libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/clutter -libclutterinclude_HEADERS = $(top_srcdir)/clutter/cogl/cogl.h \ - $(top_srcdir)/clutter/cogl/gl/cogl-defines.h +libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/cogl +libclutterinclude_HEADERS = $(top_builddir)/clutter/cogl/cogl.h \ + $(top_builddir)/clutter/cogl/cogl-defines-gl.h INCLUDES = \ - -I$(top_srcdir) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/clutter \ -I$(top_srcdir)/clutter/cogl \ - $(CLUTTER_CFLAGS) \ - $(CLUTTER_DEBUG_CFLAGS) \ + -I$(top_srcdir)/clutter/cogl/common \ + -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \ + -I$(top_builddir)/clutter \ + -I$(top_builddir)/clutter/cogl \ + $(CLUTTER_CFLAGS) \ + $(CLUTTER_DEBUG_CFLAGS) \ $(GCC_FLAGS) LDADD = $(CLUTTER_LIBS) noinst_LTLIBRARIES = libclutter-cogl.la -libclutter_cogl_la_SOURCES = \ - $(top_srcdir)/clutter/cogl/cogl.h \ - $(top_srcdir)/clutter/cogl/gl/cogl-defines.h \ - cogl.c +libclutter_cogl_la_SOURCES = \ + $(top_builddir)/clutter/cogl/cogl.h \ + $(top_builddir)/clutter/cogl/gl/cogl-defines.h \ + cogl-defines.h \ + cogl-internal.h \ + cogl-primitives.h \ + cogl-texture.h \ + cogl-fbo.h \ + cogl-context.h \ + cogl.c \ + cogl-primitives.c \ + cogl-texture.c \ + cogl-fbo.c \ + cogl-context.c + +libclutter_cogl_la_LIBADD = $(top_builddir)/clutter/cogl/common/libclutter-cogl-common.la diff --git a/gl/cogl-context.c b/gl/cogl-context.c new file mode 100644 index 00000000..936d7e9b --- /dev/null +++ b/gl/cogl-context.c @@ -0,0 +1,114 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-context.h" + +#include <string.h> + +static CoglContext *_context = NULL; + +gboolean +cogl_create_context () +{ + if (_context != NULL) + return FALSE; + + /* Allocate context memory */ + _context = (CoglContext*) g_malloc (sizeof (CoglContext)); + + /* Init default values */ + _context->feature_flags = 0; + _context->features_cached = FALSE; + + _context->enable_flags = 0; + _context->color_alpha = 255; + + _context->path_nodes = NULL; + _context->path_nodes_cap = 0; + _context->path_nodes_size = 0; + + _context->texture_handles = NULL; + + _context->fbo_handles = NULL; + _context->draw_buffer = COGL_WINDOW_BUFFER; + + _context->pf_glGenRenderbuffersEXT = NULL; + _context->pf_glBindRenderbufferEXT = NULL; + _context->pf_glRenderbufferStorageEXT = NULL; + _context->pf_glGenFramebuffersEXT = NULL; + _context->pf_glBindFramebufferEXT = NULL; + _context->pf_glFramebufferTexture2DEXT = NULL; + _context->pf_glFramebufferRenderbufferEXT = NULL; + _context->pf_glCheckFramebufferStatusEXT = NULL; + _context->pf_glDeleteFramebuffersEXT = NULL; + _context->pf_glBlitFramebufferEXT = NULL; + _context->pf_glRenderbufferStorageMultisampleEXT = NULL; + + _context->pf_glCreateProgramObjectARB = NULL; + _context->pf_glCreateShaderObjectARB = NULL; + _context->pf_glShaderSourceARB = NULL; + _context->pf_glCompileShaderARB = NULL; + _context->pf_glAttachObjectARB = NULL; + _context->pf_glLinkProgramARB = NULL; + _context->pf_glUseProgramObjectARB = NULL; + _context->pf_glGetUniformLocationARB = NULL; + _context->pf_glDeleteObjectARB = NULL; + _context->pf_glGetInfoLogARB = NULL; + _context->pf_glGetObjectParameterivARB = NULL; + _context->pf_glUniform1fARB = NULL; + + /* Init OpenGL state */ + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColorMask (TRUE, TRUE, TRUE, FALSE); + cogl_enable (0); + + return TRUE; +} + +void +cogl_destroy_context () +{ + if (_context == NULL) + return; + + g_free (_context); +} + +CoglContext * +_cogl_context_get_default () +{ + /* Create if doesn't exist yet */ + if (_context == NULL) + cogl_create_context (); + + return _context; +} diff --git a/gl/cogl-context.h b/gl/cogl-context.h new file mode 100644 index 00000000..4e952cc5 --- /dev/null +++ b/gl/cogl-context.h @@ -0,0 +1,95 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_CONTEXT_H +#define __COGL_CONTEXT_H + +#include "cogl-primitives.h" + +typedef struct +{ + /* Features cache */ + CoglFeatureFlags feature_flags; + gboolean features_cached; + + /* Enable cache */ + gulong enable_flags; + guint8 color_alpha; + + /* Primitives */ + CoglFixedVec2 path_start; + CoglFixedVec2 path_pen; + CoglFloatVec2 *path_nodes; + guint path_nodes_cap; + guint path_nodes_size; + CoglFixedVec2 path_nodes_min; + CoglFixedVec2 path_nodes_max; + + /* Textures */ + GArray *texture_handles; + + /* Framebuffer objects */ + GArray *fbo_handles; + CoglBufferTarget draw_buffer; + + /* Relying on glext.h to define these */ + PFNGLGENRENDERBUFFERSEXTPROC pf_glGenRenderbuffersEXT; + PFNGLBINDRENDERBUFFEREXTPROC pf_glBindRenderbufferEXT; + PFNGLRENDERBUFFERSTORAGEEXTPROC pf_glRenderbufferStorageEXT; + PFNGLGENFRAMEBUFFERSEXTPROC pf_glGenFramebuffersEXT; + PFNGLBINDFRAMEBUFFEREXTPROC pf_glBindFramebufferEXT; + PFNGLFRAMEBUFFERTEXTURE2DEXTPROC pf_glFramebufferTexture2DEXT; + PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC pf_glFramebufferRenderbufferEXT; + PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC pf_glCheckFramebufferStatusEXT; + PFNGLDELETEFRAMEBUFFERSEXTPROC pf_glDeleteFramebuffersEXT; + PFNGLBLITFRAMEBUFFEREXTPROC pf_glBlitFramebufferEXT; + PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC pf_glRenderbufferStorageMultisampleEXT; + + PFNGLCREATEPROGRAMOBJECTARBPROC pf_glCreateProgramObjectARB; + PFNGLCREATESHADEROBJECTARBPROC pf_glCreateShaderObjectARB; + PFNGLSHADERSOURCEARBPROC pf_glShaderSourceARB; + PFNGLCOMPILESHADERARBPROC pf_glCompileShaderARB; + PFNGLATTACHOBJECTARBPROC pf_glAttachObjectARB; + PFNGLLINKPROGRAMARBPROC pf_glLinkProgramARB; + PFNGLUSEPROGRAMOBJECTARBPROC pf_glUseProgramObjectARB; + PFNGLGETUNIFORMLOCATIONARBPROC pf_glGetUniformLocationARB; + PFNGLDELETEOBJECTARBPROC pf_glDeleteObjectARB; + PFNGLGETINFOLOGARBPROC pf_glGetInfoLogARB; + PFNGLGETOBJECTPARAMETERIVARBPROC pf_glGetObjectParameterivARB; + PFNGLUNIFORM1FARBPROC pf_glUniform1fARB; + +} CoglContext; + +CoglContext * +_cogl_context_get_default (); + +/* Obtains the context and returns retval if NULL */ +#define _COGL_GET_CONTEXT(ctxvar, retval) \ +CoglContext *ctxvar = _cogl_context_get_default (); \ +if (ctxvar == NULL) return retval; + +#define NO_RETVAL + +#endif /* __COGL_CONTEXT_H */ diff --git a/gl/cogl-fbo.c b/gl/cogl-fbo.c new file mode 100644 index 00000000..f4fb968d --- /dev/null +++ b/gl/cogl-fbo.c @@ -0,0 +1,390 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-texture.h" +#include "cogl-fbo.h" +#include "cogl-context.h" + +/* Expecting EXT functions not to be defined - redirect to pointers in context */ +#define glGenRenderbuffersEXT ctx->pf_glGenRenderbuffersEXT +#define glBindRenderbufferEXT ctx->pf_glBindRenderbufferEXT +#define glRenderbufferStorageEXT ctx->pf_glRenderbufferStorageEXT +#define glGenFramebuffersEXT ctx->pf_glGenFramebuffersEXT +#define glBindFramebufferEXT ctx->pf_glBindFramebufferEXT +#define glFramebufferTexture2DEXT ctx->pf_glFramebufferTexture2DEXT +#define glFramebufferRenderbufferEXT ctx->pf_glFramebufferRenderbufferEXT +#define glCheckFramebufferStatusEXT ctx->pf_glCheckFramebufferStatusEXT +#define glDeleteFramebuffersEXT ctx->pf_glDeleteFramebuffersEXT +#define glBlitFramebufferEXT ctx->pf_glBlitFramebufferEXT +#define glRenderbufferStorageMultisampleEXT ctx->pf_glRenderbufferStorageMultisampleEXT + +static gint +_cogl_fbo_handle_find (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, -1); + + gint i; + + if (ctx->fbo_handles == NULL) + return -1; + + for (i=0; i < ctx->fbo_handles->len; ++i) + if (g_array_index (ctx->fbo_handles, CoglHandle, i) == handle) + return i; + + return -1; +} + +static CoglHandle +_cogl_fbo_handle_new (CoglFbo *fbo) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + CoglHandle handle = (CoglHandle)fbo; + + if (ctx->fbo_handles == NULL) + ctx->fbo_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle)); + + g_array_append_val (ctx->fbo_handles, handle); + + return handle; +} + +static void +_cogl_fbo_handle_release (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + gint i; + + if ( (i = _cogl_fbo_handle_find (handle)) == -1) + return; + + g_array_remove_index_fast (ctx->fbo_handles, i); +} + +static CoglFbo* +_cogl_fbo_pointer_from_handle (CoglHandle handle) +{ + return (CoglFbo*) handle; +} + +gboolean +cogl_is_offscreen_buffer (CoglHandle handle) +{ + if (handle == COGL_INVALID_HANDLE) + return FALSE; + + return _cogl_fbo_handle_find (handle) >= 0; +} + +CoglHandle +cogl_offscreen_new_to_texture (CoglHandle texhandle) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN)) + return COGL_INVALID_HANDLE; + + CoglTexture *tex; + CoglFbo *fbo; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint tex_gl_handle; + GLuint fbo_gl_handle; + GLenum status; + + /* Make texhandle is a valid texture object */ + if (!cogl_is_texture (texhandle)) + return COGL_INVALID_HANDLE; + + tex = _cogl_texture_pointer_from_handle (texhandle); + + /* The texture must not be sliced */ + if (tex->slice_gl_handles == NULL) + return COGL_INVALID_HANDLE; + + if (tex->slice_gl_handles->len != 1) + return COGL_INVALID_HANDLE; + + /* Pick the single texture slice width, height and GL id */ + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + tex_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + + /* Generate framebuffer */ + glGenFramebuffersEXT (1, &fbo_gl_handle); + GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo_gl_handle) ); + GE( glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + tex->gl_target, tex_gl_handle, 0) ); + + /* Make sure it's complete */ + status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT); + + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + { + GE( glDeleteFramebuffersEXT (1, &fbo_gl_handle) ); + GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); + return COGL_INVALID_HANDLE; + } + + GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); + + /* Allocate and init a CoglFbo object (store non-wasted size + for subsequent blits and viewport setup) */ + fbo = (CoglFbo*) g_malloc (sizeof (CoglFbo)); + fbo->ref_count = 1; + fbo->width = x_span->size - x_span->waste; + fbo->height = y_span->size - y_span->waste; + fbo->gl_handle = fbo_gl_handle; + + return _cogl_fbo_handle_new (fbo); +} + +CoglHandle +cogl_offscreen_new_multisample () +{ + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_MULTISAMPLE)) + return COGL_INVALID_HANDLE; + + return COGL_INVALID_HANDLE; +} + +CoglHandle +cogl_offscreen_ref (CoglHandle handle) +{ + CoglFbo *fbo; + + if (!cogl_is_offscreen_buffer (handle)) + return COGL_INVALID_HANDLE; + + fbo = _cogl_fbo_pointer_from_handle (handle); + + fbo->ref_count++; + + return handle; +} + +void +cogl_offscreen_unref (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglFbo *fbo; + + /* Make sure this is a valid fbo handle */ + if (!cogl_is_offscreen_buffer (handle)) + return; + + fbo = _cogl_fbo_pointer_from_handle (handle); + + if (--fbo->ref_count < 1) + { + /* Release handle and destroy object */ + GE( glDeleteFramebuffersEXT (1, &fbo->gl_handle) ); + + _cogl_fbo_handle_release (handle); + + g_free (fbo); + } +} + +void +cogl_offscreen_blit_region (CoglHandle src_buffer, + CoglHandle dst_buffer, + int src_x, + int src_y, + int src_w, + int src_h, + int dst_x, + int dst_y, + int dst_w, + int dst_h) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_BLIT)) + return; + + CoglFbo *src_fbo; + CoglFbo *dst_fbo; + + /* Make sure these are valid fbo handles */ + if (!cogl_is_offscreen_buffer (src_buffer)) + return; + + if (!cogl_is_offscreen_buffer (dst_buffer)) + return; + + src_fbo = _cogl_fbo_pointer_from_handle (src_buffer); + dst_fbo = _cogl_fbo_pointer_from_handle (dst_buffer); + + /* Copy (and scale) a region from one to another framebuffer */ + GE( glBindFramebufferEXT (GL_READ_FRAMEBUFFER_EXT, src_fbo->gl_handle) ); + GE( glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, dst_fbo->gl_handle) ); + GE( glBlitFramebufferEXT (src_x, src_y, src_x + src_w, src_y + src_h, + dst_x, dst_y, dst_x + dst_w, dst_y + dst_h, + GL_COLOR_BUFFER_BIT, GL_LINEAR) ); +} + +void +cogl_offscreen_blit (CoglHandle src_buffer, + CoglHandle dst_buffer) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_BLIT)) + return; + + CoglFbo *src_fbo; + CoglFbo *dst_fbo; + + /* Make sure these are valid fbo handles */ + if (!cogl_is_offscreen_buffer (src_buffer)) + return; + + if (!cogl_is_offscreen_buffer (dst_buffer)) + return; + + src_fbo = _cogl_fbo_pointer_from_handle (src_buffer); + dst_fbo = _cogl_fbo_pointer_from_handle (dst_buffer); + + /* Copy (and scale) whole image from one to another framebuffer */ + GE( glBindFramebufferEXT (GL_READ_FRAMEBUFFER_EXT, src_fbo->gl_handle) ); + GE( glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, dst_fbo->gl_handle) ); + GE( glBlitFramebufferEXT (0, 0, src_fbo->width, src_fbo->height, + 0, 0, dst_fbo->width, dst_fbo->height, + GL_COLOR_BUFFER_BIT, GL_LINEAR) ); +} + +void +cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglFbo *fbo = NULL; + + if (target == COGL_OFFSCREEN_BUFFER) + { + /* Make sure it is a valid fbo handle */ + if (!cogl_is_offscreen_buffer (offscreen)) + return; + + fbo = _cogl_fbo_pointer_from_handle (offscreen); + + /* Check current draw buffer target */ + if (ctx->draw_buffer != COGL_OFFSCREEN_BUFFER) + { + /* Push the viewport and matrix setup if redirecting + from a non-screen buffer */ + GE( glPushAttrib (GL_VIEWPORT_BIT) ); + + GE( glMatrixMode (GL_PROJECTION) ); + GE( glPushMatrix () ); + GE( glLoadIdentity () ); + + GE( glMatrixMode (GL_MODELVIEW) ); + GE( glPushMatrix () ); + GE( glLoadIdentity () ); + } + else + { + /* Override viewport and matrix setup if redirecting + from another offscreen buffer */ + GE( glMatrixMode (GL_PROJECTION) ); + GE( glLoadIdentity () ); + + GE( glMatrixMode (GL_MODELVIEW) ); + GE( glLoadIdentity () ); + } + + /* Setup new viewport and matrices */ + GE( glViewport (0, 0, fbo->width, fbo->height) ); + GE( glTranslatef (-1.0f, -1.0f, 0.0f) ); + GE( glScalef (2.0f / fbo->width, 2.0f / fbo->height, 1.0f) ); + + /* Bind offscreen framebuffer object */ + GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo->gl_handle) ); + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + + /* Some implementation require a clear before drawing + to an fbo. Luckily it is affected by scissor test. */ + /* FIXME: test where exactly this is needed end whether + a glClear with 0 argument is enough */ + GE( glPushAttrib (GL_SCISSOR_BIT) ); + GE( glScissor (0,0,0,0) ); + GE( glEnable (GL_SCISSOR_TEST) ); + GE( glClear (GL_COLOR_BUFFER_BIT) ); + GE( glPopAttrib () ); + + } + else if ((target & COGL_WINDOW_BUFFER) || + (target & COGL_MASK_BUFFER)) + { + /* Check current draw buffer target */ + if (ctx->draw_buffer == COGL_OFFSCREEN_BUFFER) + { + /* Pop viewport and matrices if redirecting back + from an offscreen buffer */ + GE( glPopAttrib () ); + + GE( glMatrixMode (GL_PROJECTION) ); + GE( glPopMatrix () ); + + GE( glMatrixMode (GL_MODELVIEW) ); + GE( glPopMatrix () ); + } + + /* Bind window framebuffer object */ + GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); + + + if (target == COGL_WINDOW_BUFFER) + { + /* Draw to RGB channels */ + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE) ); + } + else if (target == COGL_MASK_BUFFER) + { + /* Draw only to ALPHA channel */ + GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE) ); + } + else + { + /* Draw to all channels */ + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + } + } + + /* Store new target */ + ctx->draw_buffer = target; +} diff --git a/gl/cogl-fbo.h b/gl/cogl-fbo.h new file mode 100644 index 00000000..f2dd5298 --- /dev/null +++ b/gl/cogl-fbo.h @@ -0,0 +1,38 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_FBO_H +#define __COGL_FBO_H + +typedef struct +{ + guint ref_count; + int width; + int height; + GLuint gl_handle; + +} CoglFbo; + +#endif /* __COGL_FBO_H */ diff --git a/gl/cogl-internal.h b/gl/cogl-internal.h new file mode 100644 index 00000000..1f6b8639 --- /dev/null +++ b/gl/cogl-internal.h @@ -0,0 +1,61 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_INTERNAL_H +#define __COGL_INTERNAL_H + +#define COGL_DEBUG 0 + +#if COGL_DEBUG +#define GE(x...) G_STMT_START { \ + GLenum err; \ + (x); \ + while ((err = glGetError()) != GL_NO_ERROR) { \ + fprintf(stderr, "glError: %s caught at %s:%u\n", \ + (char *)error_string(err), \ + __FILE__, __LINE__); \ + } \ +} G_STMT_END +#else +#define GE(x) (x); +#endif + +#define COGL_ENABLE_BLEND (1<<1) +#define COGL_ENABLE_TEXTURE_2D (1<<2) +#define COGL_ENABLE_ALPHA_TEST (1<<3) +#define COGL_ENABLE_TEXTURE_RECT (1<<4) +#define COGL_ENABLE_VERTEX_ARRAY (1<<5) +#define COGL_ENABLE_TEXCOORD_ARRAY (1<<6) + +gint +_cogl_get_format_bpp (CoglPixelFormat format); + +void +cogl_enable (gulong flags); + +gulong +cogl_get_enable (); + +#endif /* __COGL_INTERNAL_H */ diff --git a/gl/cogl-primitives.c b/gl/cogl-primitives.c new file mode 100644 index 00000000..352ad46d --- /dev/null +++ b/gl/cogl-primitives.c @@ -0,0 +1,1092 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-context.h" + +#include <string.h> +#include <gmodule.h> + +#define _COGL_MAX_BEZ_RECURSE_DEPTH 16 + +/** + * SECTION:cogl-primitives + * @short_description: Functions that draw various primitive shapes and + * allow for construction of more complex paths. + * + * There are three levels on which drawing with cogl can be used. The + * highest level functions construct various simple primitive shapes + * to be either filled or stroked. Using a lower-level set of functions + * more complex and arbitrary paths can be constructed by concatenating + * straight line, bezier curve and arc segments. Additionally there + * are utility functions that draw the most common primitives - rectangles + * and trapezoids - in a maximaly optimized fashion. + * + * When constructing arbitrary paths, the current pen location is + * initialized using the move_to command. The subsequent path segments + * implicitly use the last pen location as their first vertex and move + * the pen location to the last vertex they produce at the end. Also + * there are special versions of functions that allow specifying the + * vertices of the path segments relative to the last pen location + * rather then in the absolute coordinates. + */ + +/** + * cogl_fast_fill_rectangle: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * + * Fills a rectangle at the given coordinates with the current + * drawing color in a highly optimizied fashion. + **/ +void +cogl_fast_fill_rectangle (gint x, + gint y, + guint width, + guint height) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0); + + GE( glRecti (x, y, x + width, y + height) ); +} + +/** + * cogl_fast_fill_rectanglex: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * + * A fixed-point version of cogl_fast_fill_rectangle. + **/ +void +cogl_fast_fill_rectanglex (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0); + + GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x), + CLUTTER_FIXED_TO_FLOAT (y), + CLUTTER_FIXED_TO_FLOAT (x + width), + CLUTTER_FIXED_TO_FLOAT (y + height)) ); +} + +/** + * cogl_fast_fill_trapezoid: + * @y1: Y coordinate of the top two vertices. + * @x11: X coordinate of the top-left vertex. + * @x21: X coordinate of the top-right vertex. + * @y2: Y coordinate of the bottom two vertices. + * @x12: X coordinate of the bottom-left vertex. + * @x22: X coordinate of the bottom-right vertex. + * + * Fills a trapezoid at the given coordinates with the current + * drawing color in a highly optimized fashion. + **/ +void +cogl_fast_fill_trapezoid (gint y1, + gint x11, + gint x21, + gint y2, + gint x12, + gint x22) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0); + + GE( glBegin (GL_QUADS) ); + GE( glVertex2i (x11, y1) ); + GE( glVertex2i (x21, y1) ); + GE( glVertex2i (x22, y2) ); + GE( glVertex2i (x12, y2) ); + GE( glEnd () ); +} + +/** + * cogl_fast_fill_trapezoidx: + * @y1: Y coordinate of the top two vertices. + * @x11: X coordinate of the top-left vertex. + * @x21: X coordinate of the top-right vertex. + * @y2: Y coordinate of the bottom two vertices. + * @x12: X coordinate of the bottom-left vertex. + * @x22: X coordinate of the bottom-right vertex. + * + * A fixed-point version of cogl_fast_fill_trapezoid. + **/ +void +cogl_fast_fill_trapezoidx (ClutterFixed y1, + ClutterFixed x11, + ClutterFixed x21, + ClutterFixed y2, + ClutterFixed x12, + ClutterFixed x22) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0); + + GE( glBegin (GL_QUADS) ); + + GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x11), + CLUTTER_FIXED_TO_FLOAT (y1)) ); + GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x21), + CLUTTER_FIXED_TO_FLOAT (y1)) ); + GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x22), + CLUTTER_FIXED_TO_FLOAT (y2)) ); + GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x12), + CLUTTER_FIXED_TO_FLOAT (y2)) ); + GE( glEnd () ); +} + +static void +_cogl_path_clear_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes) + g_free(ctx->path_nodes); + + ctx->path_nodes = (CoglFloatVec2*) g_malloc (2 * sizeof(CoglFloatVec2)); + ctx->path_nodes_size = 0; + ctx->path_nodes_cap = 2; +} + +static void +_cogl_path_add_node (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglFloatVec2 *new_nodes = NULL; + + if (ctx->path_nodes_size == ctx->path_nodes_cap) + { + new_nodes = g_realloc (ctx->path_nodes, + 2 * ctx->path_nodes_cap + * sizeof (CoglFloatVec2)); + + if (new_nodes == NULL) return; + + ctx->path_nodes = new_nodes; + ctx->path_nodes_cap *= 2; + } + + ctx->path_nodes [ctx->path_nodes_size] .x = CLUTTER_FIXED_TO_FLOAT (x); + ctx->path_nodes [ctx->path_nodes_size] .y = CLUTTER_FIXED_TO_FLOAT (y); + ctx->path_nodes_size++; + + if (ctx->path_nodes_size == 1) + { + ctx->path_nodes_min.x = ctx->path_nodes_max.x = x; + ctx->path_nodes_min.y = ctx->path_nodes_max.y = y; + } + else + { + if (x < ctx->path_nodes_min.x) ctx->path_nodes_min.x = x; + if (x > ctx->path_nodes_max.x) ctx->path_nodes_max.x = x; + if (y < ctx->path_nodes_min.y) ctx->path_nodes_min.y = y; + if (y > ctx->path_nodes_max.y) ctx->path_nodes_max.y = y; + } +} + +static void +_cogl_path_stroke_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FLOAT, 0, ctx->path_nodes) ); + GE( glDrawArrays (GL_LINE_STRIP, 0, ctx->path_nodes_size) ); +} + +static void +_cogl_path_fill_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + guint bounds_x; + guint bounds_y; + guint bounds_w; + guint bounds_h; + + GE( glClear (GL_STENCIL_BUFFER_BIT) ); + + GE( glEnable (GL_STENCIL_TEST) ); + GE( glStencilFunc (GL_ALWAYS, 0x0, 0x0) ); + GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); + GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) ); + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FLOAT, 0, ctx->path_nodes) ); + GE( glDrawArrays (GL_TRIANGLE_FAN, 0, ctx->path_nodes_size) ); + + GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) ); + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + + bounds_x = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.x); + bounds_y = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.y); + bounds_w = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.x - ctx->path_nodes_min.x); + bounds_h = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.y - ctx->path_nodes_min.y); + + cogl_fast_fill_rectangle (bounds_x, bounds_y, bounds_w, bounds_h); + + GE( glDisable (GL_STENCIL_TEST) ); +} + +/** + * cogl_fill: + * + * Fills the constructed shape using the current drawing color. + **/ +void +cogl_fill () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes_size == 0) + return; + + _cogl_path_fill_nodes(); + +} + +/** + * cogl_stroke: + * + * Strokes the constructed shape using the current drawing color + * and a width of 1 pixel (regardless of the current transformation + * matrix). + **/ +void +cogl_stroke () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes_size == 0) + return; + + _cogl_path_stroke_nodes(); +} + +/** + * cogl_path_move_to: + * @x: X coordinate of the pen location to move to. + * @y: Y coordinate of the pen location to move to. + * + * Clears the previously constructed shape and begins a new path + * contour by moving the pen to the given coordinates. + **/ +void +cogl_path_move_to (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* FIXME: handle multiple contours maybe? */ + + _cogl_path_clear_nodes (); + _cogl_path_add_node (x, y); + + ctx->path_start.x = x; + ctx->path_start.y = y; + + ctx->path_pen = ctx->path_start; +} + +/** + * cogl_path_move_to_rel: + * @x: X offset from the current pen location to move the pen to. + * @y: Y offset from the current pen location to move the pen to. + * + * Clears the previously constructed shape and begins a new path + * contour by moving the pen to the given coordinates relative + * to the current pen location. + **/ +void +cogl_path_move_to_rel (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_move_to (ctx->path_pen.x + x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_line_to: + * @x: X coordinate of the end line vertex + * @y: Y coordinate of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates. + **/ +void +cogl_path_line_to (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_path_add_node (x, y); + + ctx->path_pen.x = x; + ctx->path_pen.y = y; +} + +/** + * cogl_path_line_to: + * @x: X offset from the current pen location of the end line vertex + * @y: Y offset from the current pen location of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates relative to the current pen location. + **/ +void +cogl_path_line_to_rel (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x + x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_h_line_to: + * @x: X coordinate of the end line vertex + * + * Adds a straight horizontal line segment to the current path that + * ends at the given X coordinate and current pen Y coordinate. + **/ +void +cogl_path_h_line_to (ClutterFixed x) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (x, + ctx->path_pen.y); +} + +/** + * cogl_path_v_line_to: + * @y: Y coordinate of the end line vertex + * + * Adds a stright vertical line segment to the current path that ends + * at the current pen X coordinate and the given Y coordinate. + **/ +void +cogl_path_v_line_to (ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x, + y); +} + +/** + * cogl_path_h_line_to_rel: + * @x: X offset from the current pen location of the end line vertex + * + * Adds a straight horizontal line segment to the current path that + * ends at the given X coordinate relative to the current pen location + * and current pen Y coordinate. + **/ +void +cogl_path_h_line_to_rel (ClutterFixed x) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x + x, + ctx->path_pen.y); +} + +/** + * cogl_path_v_line_to_rel: + * @y: Y offset from the current pen location of the end line vertex + * + * Adds a stright vertical line segment to the current path that ends + * at the current pen X coordinate and the given Y coordinate relative + * to the current pen location. + **/ +void +cogl_path_v_line_to_rel (ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_close: + * + * Closes the path being constructed by adding a straight line segment + * to it that ends at the first vertex of the path. + **/ +void +cogl_path_close () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_path_add_node (ctx->path_start.x, ctx->path_start.y); + ctx->path_pen = ctx->path_start; +} + +/** + * cogl_line: + * @x1: X coordinate of the start line vertex + * @y1: Y coordinate of the start line vertex + * @x2: X coordinate of the end line vertex + * @y2: Y coordinate of the end line vertex + * + * Clears the previously constructed shape and constructs a straight + * line shape start and ending at the given coordinates. + **/ +void +cogl_line (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + cogl_path_move_to (x1, y1); + cogl_path_line_to (x2, y2); +} + +/** + * cogl_polyline: + * @coords: A pointer to the first element of an array of fixed-point + * values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Clears the previously constructed shape and constructs a series of straight + * line segments, starting from the first given vertex coordinate. Each + * subsequent segment stars where the previous one ended and ends at the next + * given vertex coordinate. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. (num_points - 1) segments will + * be constructed. + **/ +void +cogl_polyline (ClutterFixed *coords, + gint num_points) +{ + gint c = 0; + + cogl_path_move_to (coords[0], coords[1]); + + for (c = 1; c < num_points; ++c) + cogl_path_line_to (coords[2*c], coords[2*c+1]); +} + + +/** + * cogl_polygon: + * @coords: A pointer to the first element of an array of fixed-point + * values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Clears the previously constructed shape and constructs a polygonal + * shape of the given number of vertices. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. + **/ +void +cogl_polygon (ClutterFixed *coords, + gint num_points) +{ + cogl_polyline (coords, num_points); + cogl_path_close (); +} + +/** + * cogl_rectangle: + * @x: X coordinate of the top-left corner. + * @y: Y coordinate of the top-left corner. + * @width: Rectangle width. + * @height: Rectangle height. + * + * Clears the previously constructed shape and constructs a rectangular + * shape at the given coordinates. + **/ +void +cogl_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height) +{ + cogl_path_move_to (x, y); + cogl_path_line_to (x + width, y); + cogl_path_line_to (x + width, y + height); + cogl_path_line_to (x, y + height); + cogl_path_close (); +} + +static void +_cogl_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step, + guint move_first) +{ + ClutterAngle a = 0x0; + ClutterAngle temp = 0x0; + ClutterFixed cosa = 0x0; + ClutterFixed sina = 0x0; + ClutterFixed px = 0x0; + ClutterFixed py = 0x0; + + /* Fix invalid angles */ + + if (angle_1 == angle_2 || angle_step == 0x0) + return; + + if (angle_step < 0x0) + angle_step = -angle_step; + + if (angle_2 < angle_1) + { + temp = angle_1; + angle_1 = angle_2; + angle_2 = temp; + } + + /* Walk the arc by given step */ + + for (a = angle_1; a < angle_2; a += angle_step) + { + cosa = clutter_cosi (a); + sina = clutter_sini (a); + + px = center_x + CFX_MUL (cosa, radius_x); + py = center_y + CFX_MUL (sina, radius_y); + + if (a == angle_1 && move_first) + cogl_path_move_to (px, py); + else + cogl_path_line_to (px, py); + } +} + +/** + * cogl_path_arc: + * @center_x: X coordinate of the elliptical arc center + * @center_y: Y coordinate of the elliptical arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Adds an elliptical arc segment to the current path. A straight line + * segment will link the current pen location with the first vertex + * of the arc. + **/ +void +cogl_path_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _cogl_arc (center_x, center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 0 /* no move */); +} + +/** + * cogl_path_arc_rel: + * @center_x: X offset from the current pen location of the elliptical + * arc center + * @center_y: Y offset from the current pen location of the elliptical + * arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Adds an elliptical arc segment to the current path. A straight line + * segment will link the current pen location with the first vertex + * of the arc. + **/ +void +cogl_path_arc_rel (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_arc (ctx->path_pen.x + center_x, + ctx->path_pen.y + center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 0 /* no move */); +} + +/** + * cogl_arc: + * @center_x: X coordinate of the elliptical arc center + * @center_y: Y coordinate of the elliptical arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Clears the previously constructed shape and constructs and elliptical arc + * shape. + **/ +void +cogl_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _cogl_arc (center_x, center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 1 /* move first */); +} + +/** + * cogl_ellipse: + * @center_x: X coordinate of the ellipse center + * @center_y: Y coordinate of the ellipse center + * @radius_x: X radius of the ellipse + * @radius_y: Y radius of the ellipse + * @angle_step: Angle increment resolution for subdivision + * + * Clears the previously constructed shape and constructs an ellipse + * shape. + **/ +void +cogl_ellipse (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_step) +{ + + /* FIXME: if shows to be slow might be optimized + * by mirroring just a quarter of it */ + + _cogl_arc (center_x, center_y, + radius_x, radius_y, + 0, CLUTTER_ANGLE_FROM_DEG(360), + angle_step, 1 /* move first */); + + cogl_path_close(); +} + +/** + * cogl_round_rectangle: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * @radius: Radius of the corner arcs. + * @arc_step: Angle increment resolution for subdivision of + * the corner arcs. + * + * Clears the previously constructed shape and constructs a rectangular + * shape with rounded corners. + **/ +void +cogl_round_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height, + ClutterFixed radius, + ClutterAngle arc_step) +{ + ClutterFixed inner_width = width - (radius << 1); + ClutterFixed inner_height = height - (radius << 1); + + cogl_path_move_to (x, y + radius); + cogl_path_arc_rel (radius, 0, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (180), + CLUTTER_ANGLE_FROM_DEG (270), + arc_step); + + cogl_path_h_line_to_rel (inner_width); + cogl_path_arc_rel (0, radius, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (-90), + CLUTTER_ANGLE_FROM_DEG (0), + arc_step); + + cogl_path_v_line_to_rel (inner_height); + cogl_path_arc_rel (-radius, 0, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (0), + CLUTTER_ANGLE_FROM_DEG (90), + arc_step); + + cogl_path_h_line_to_rel (-inner_width); + cogl_path_arc_rel (0, -radius, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (90), + CLUTTER_ANGLE_FROM_DEG (180), + arc_step); + + cogl_path_close (); +} + +static void +_cogl_path_bezier2_sub (CoglBezQuad *quad) +{ + CoglBezQuad quads[_COGL_MAX_BEZ_RECURSE_DEPTH]; + CoglBezQuad *qleft; + CoglBezQuad *qright; + CoglBezQuad *q; + CoglFixedVec2 mid; + CoglFixedVec2 dif; + CoglFixedVec2 c1; + CoglFixedVec2 c2; + CoglFixedVec2 c3; + gint qindex; + + /* Put first curve on stack */ + quads[0] = *quad; + qindex = 0; + + /* While stack is not empty */ + while (qindex >= 0) + { + + q = &quads[qindex]; + + /* Calculate distance of control point from its + * counterpart on the line between end points */ + mid.x = ((q->p1.x + q->p3.x) >> 1); + mid.y = ((q->p1.y + q->p3.y) >> 1); + dif.x = (q->p2.x - mid.x); + dif.y = (q->p2.y - mid.y); + if (dif.x < 0) dif.x = -dif.x; + if (dif.y < 0) dif.y = -dif.y; + + /* Cancel if the curve is flat enough */ + if (dif.x + dif.y <= CFX_ONE + || qindex == _COGL_MAX_BEZ_RECURSE_DEPTH - 1) + { + /* Add subdivision point (skip last) */ + if (qindex == 0) return; + _cogl_path_add_node (q->p3.x, q->p3.y); + --qindex; continue; + } + + /* Left recursion goes on top of stack! */ + qright = q; qleft = &quads[++qindex]; + + /* Subdivide into 2 sub-curves */ + c1.x = ((q->p1.x + q->p2.x) >> 1); + c1.y = ((q->p1.y + q->p2.y) >> 1); + c3.x = ((q->p2.x + q->p3.x) >> 1); + c3.y = ((q->p2.y + q->p3.y) >> 1); + c2.x = ((c1.x + c3.x) >> 1); + c2.y = ((c1.y + c3.y) >> 1); + + /* Add left recursion onto stack */ + qleft->p1 = q->p1; + qleft->p2 = c1; + qleft->p3 = c2; + + /* Add right recursion onto stack */ + qright->p1 = c2; + qright->p2 = c3; + qright->p3 = q->p3; + } +} + +static void +_cogl_path_bezier3_sub (CoglBezCubic *cubic) +{ + CoglBezCubic cubics[_COGL_MAX_BEZ_RECURSE_DEPTH]; + CoglBezCubic *cleft; + CoglBezCubic *cright; + CoglBezCubic *c; + CoglFixedVec2 dif1; + CoglFixedVec2 dif2; + CoglFixedVec2 mm; + CoglFixedVec2 c1; + CoglFixedVec2 c2; + CoglFixedVec2 c3; + CoglFixedVec2 c4; + CoglFixedVec2 c5; + gint cindex; + + /* Put first curve on stack */ + cubics[0] = *cubic; + cindex = 0; + + while (cindex >= 0) + { + c = &cubics[cindex]; + +#define CFX_MUL2(x) ((x) << 1) +#define CFX_MUL3(x) (((x) << 1) + (x)) +#define CFX_SQ(x) CFX_MUL (x, x) + + /* Calculate distance of control points from their + * counterparts on the line between end points */ + dif1.x = CFX_MUL3 (c->p2.x) - CFX_MUL2 (c->p1.x) - c->p4.x; + dif1.y = CFX_MUL3 (c->p2.y) - CFX_MUL2 (c->p1.y) - c->p4.y; + dif2.x = CFX_MUL3 (c->p3.x) - CFX_MUL2 (c->p4.x) - c->p1.x; + dif2.y = CFX_MUL3 (c->p3.y) - CFX_MUL2 (c->p4.y) - c->p1.y; + if (dif1.x < 0) dif1.x = -dif1.x; + if (dif1.y < 0) dif1.y = -dif1.y; + if (dif2.x < 0) dif2.x = -dif2.x; + if (dif2.y < 0) dif2.y = -dif2.y; + +#undef CFX_MUL2 +#undef CFX_MUL3 +#undef CFX_SQ + + /* Pick the greatest of two distances */ + if (dif1.x < dif2.x) dif1.x = dif2.x; + if (dif1.y < dif2.y) dif1.y = dif2.y; + + /* Cancel if the curve is flat enough */ + if (dif1.x + dif1.y <= CFX_ONE + || cindex == _COGL_MAX_BEZ_RECURSE_DEPTH-1) + { + /* Add subdivision point (skip last) */ + if (cindex == 0) return; + _cogl_path_add_node (c->p4.x, c->p4.y); + --cindex; continue; + } + + /* Left recursion goes on top of stack! */ + cright = c; cleft = &cubics[++cindex]; + + /* Subdivide into 2 sub-curves */ + c1.x = ((c->p1.x + c->p2.x) >> 1); + c1.y = ((c->p1.y + c->p2.y) >> 1); + mm.x = ((c->p2.x + c->p3.x) >> 1); + mm.y = ((c->p2.y + c->p3.y) >> 1); + c5.x = ((c->p3.x + c->p4.x) >> 1); + c5.y = ((c->p3.y + c->p4.y) >> 1); + + c2.x = ((c1.x + mm.x) >> 1); + c2.y = ((c1.y + mm.y) >> 1); + c4.x = ((mm.x + c5.x) >> 1); + c4.y = ((mm.y + c5.y) >> 1); + + c3.x = ((c2.x + c4.x) >> 1); + c3.y = ((c2.y + c4.y) >> 1); + + /* Add left recursion to stack */ + cleft->p1 = c->p1; + cleft->p2 = c1; + cleft->p3 = c2; + cleft->p4 = c3; + + /* Add right recursion to stack */ + cright->p1 = c3; + cright->p2 = c4; + cright->p3 = c5; + cright->p4 = c->p4; + } +} + +/** + * cogl_path_bezier2_to: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * + * Adds a quadratic bezier curve segment to the current path with the given + * second and third control points and using current pen location as the + * first control point. + **/ +void +cogl_path_bezier2_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglBezQuad quad; + + /* Prepare quadratic curve */ + quad.p1 = ctx->path_pen; + quad.p2.x = x1; + quad.p2.y = y1; + quad.p3.x = x2; + quad.p3.y = y2; + + /* Run subdivision */ + _cogl_path_bezier2_sub (&quad); + + /* Add last point */ + _cogl_path_add_node (quad.p3.x, quad.p3.y); + ctx->path_pen = quad.p3; +} + +/** + * cogl_path_bezier3_to: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * @x3: X coordinate of the fourth bezier control point + * @y3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. + **/ +void +cogl_path_bezier3_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglBezCubic cubic; + + /* Prepare cubic curve */ + cubic.p1 = ctx->path_pen; + cubic.p2.x = x1; + cubic.p2.y = y1; + cubic.p3.x = x2; + cubic.p3.y = y2; + cubic.p4.x = x3; + cubic.p4.y = y3; + + /* Run subdivision */ + _cogl_path_bezier3_sub (&cubic); + + /* Add last point */ + _cogl_path_add_node (cubic.p4.x, cubic.p4.y); + ctx->path_pen = cubic.p4; +} + +/** + * cogl_path_bezier2_to_rel: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * + * Adds a quadratic bezier curve segment to the current path with the given + * second and third control points and using current pen location as the + * first control point. The given coordinates are relative to the current + * pen location. + **/ +void +cogl_path_bezier2_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_bezier2_to (ctx->path_pen.x + x1, + ctx->path_pen.y + y2, + ctx->path_pen.x + x2, + ctx->path_pen.y + y2); +} + +/** + * cogl_path_bezier3_to_rel: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * @x3: X coordinate of the fourth bezier control point + * @y3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. The given coordinates are relative to the + * current pen location. + **/ +void +cogl_path_bezier3_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_bezier3_to (ctx->path_pen.x + x1, + ctx->path_pen.y + y2, + ctx->path_pen.x + x2, + ctx->path_pen.y + y2, + ctx->path_pen.x + x3, + ctx->path_pen.y + y3); +} diff --git a/gl/cogl-primitives.h b/gl/cogl-primitives.h new file mode 100644 index 00000000..f43ab6a4 --- /dev/null +++ b/gl/cogl-primitives.h @@ -0,0 +1,61 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_PRIMITIVES_H +#define __COGL_PRIMITIVES_H + +typedef struct _CoglFixedVec2 CoglFixedVec2; +typedef struct _CoglFloatVec2 CoglFloatVec2; +typedef struct _CoglBezQuad CoglBezQuad; +typedef struct _CoglBezCubic CoglBezCubic; + +struct _CoglFixedVec2 +{ + ClutterFixed x; + ClutterFixed y; +}; + +struct _CoglFloatVec2 +{ + GLfloat x; + GLfloat y; +}; + +struct _CoglBezQuad +{ + CoglFixedVec2 p1; + CoglFixedVec2 p2; + CoglFixedVec2 p3; +}; + +struct _CoglBezCubic +{ + CoglFixedVec2 p1; + CoglFixedVec2 p2; + CoglFixedVec2 p3; + CoglFixedVec2 p4; +}; + +#endif /* __COGL_PRIMITIVES_H */ diff --git a/gl/cogl-texture.c b/gl/cogl-texture.c new file mode 100644 index 00000000..5e4d6ee5 --- /dev/null +++ b/gl/cogl-texture.c @@ -0,0 +1,2303 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-bitmap.h" +#include "cogl-texture.h" +#include "cogl-context.h" + +#include <string.h> +#include <stdlib.h> + +/* +#define COGL_DEBUG 1 + +#define GE(x) \ +{ \ + glGetError(); x; \ + GLuint err = glGetError(); \ + if (err != 0) \ + printf("err: 0x%x\n", err); \ +} */ + +struct _CoglSpanIter +{ + gint index; + GArray *array; + CoglTexSliceSpan *span; + ClutterFixed pos; + ClutterFixed next_pos; + ClutterFixed origin; + ClutterFixed cover_start; + ClutterFixed cover_end; + ClutterFixed intersect_start; + ClutterFixed intersect_end; + ClutterFixed intersect_start_local; + ClutterFixed intersect_end_local; + gboolean intersects; +}; + +/* + * _cogl_texture_handle_find: + * @handle: A texture handle + * + * Returns the index of the given CoglHandle if found in the + * handle array. + */ +static gint +_cogl_texture_handle_find (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, -1); + + gint i; + + if (ctx->texture_handles == NULL) + return -1; + + for (i=0; i < ctx->texture_handles->len; ++i) + if (g_array_index (ctx->texture_handles, CoglHandle, i) == handle) + return i; + + return -1; +} + +/* + * _cogl_texture_handle_new: + * @tex: A pointer to an allocated CoglTexture structure + * + * Returns a new CoglHandle for the given CoglTexture + * object. + */ +static CoglHandle +_cogl_texture_handle_new (CoglTexture *tex) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + CoglHandle handle = (CoglHandle)tex; + + if (ctx->texture_handles == NULL) + ctx->texture_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle)); + + g_array_append_val (ctx->texture_handles, handle); + + return handle; +} + +/* + * _cogl_texture_handle_release: + * @handle: A valid CoglHandle + * + * Frees the given CoglHandle for use with another object. + */ +static void +_cogl_texture_handle_release (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + gint i; + + if ( (i = _cogl_texture_handle_find (handle)) == -1) + return; + + g_array_remove_index_fast (ctx->texture_handles, i); +} + +/* + * _cogl_texture_pointer_from_handle: + * @handle: A valid CoglHandle + * + * Returns a pointer to the texture object referenced by + * given handle. + */ +CoglTexture * +_cogl_texture_pointer_from_handle (CoglHandle handle) +{ + return (CoglTexture*) handle; +} + +gboolean +cogl_is_texture (CoglHandle handle) +{ + if (handle == COGL_INVALID_HANDLE) + return FALSE; + + return _cogl_texture_handle_find (handle) >= 0; +} + +static void +_cogl_texture_bitmap_free (CoglTexture *tex) +{ + if (tex->bitmap.data != NULL && tex->bitmap_owner) + g_free (tex->bitmap.data); + + tex->bitmap.data = NULL; + tex->bitmap_owner = FALSE; +} + +static void +_cogl_texture_bitmap_swap (CoglTexture *tex, + CoglBitmap *new_bitmap) +{ + if (tex->bitmap.data != NULL && tex->bitmap_owner) + g_free (tex->bitmap.data); + + tex->bitmap = *new_bitmap; + tex->bitmap_owner = TRUE; +} + +static void +_cogl_span_iter_update (CoglSpanIter *iter) +{ + /* Pick current span */ + iter->span = &g_array_index (iter->array, + CoglTexSliceSpan, + iter->index); + + /* Offset next position by span size */ + iter->next_pos = iter->pos + + CLUTTER_INT_TO_FIXED (iter->span->size - iter->span->waste); + + /* Check if span intersects the area to cover */ + if (iter->next_pos <= iter->cover_start || + iter->pos >= iter->cover_end) + { + /* Intersection undefined */ + iter->intersects = FALSE; + return; + } + + iter->intersects = TRUE; + + /* Clip start position to coverage area */ + if (iter->pos < iter->cover_start) + iter->intersect_start = iter->cover_start; + else + iter->intersect_start = iter->pos; + + /* Clip end position to coverage area */ + if (iter->next_pos > iter->cover_end) + iter->intersect_end = iter->cover_end; + else + iter->intersect_end = iter->next_pos; +} + +static void +_cogl_span_iter_begin (CoglSpanIter *iter, + GArray *array, + ClutterFixed origin, + ClutterFixed cover_start, + ClutterFixed cover_end) +{ + /* Copy info */ + iter->index = 0; + iter->array = array; + iter->span = NULL; + iter->origin = origin; + iter->cover_start = cover_start; + iter->cover_end = cover_end; + iter->pos = iter->origin; + + /* Update intersection */ + _cogl_span_iter_update (iter); +} + +void +_cogl_span_iter_next (CoglSpanIter *iter) +{ + /* Move current position */ + iter->pos = iter->next_pos; + + /* Pick next slice (wrap when last reached) */ + iter->index = (iter->index + 1) % iter->array->len; + + /* Update intersection */ + _cogl_span_iter_update (iter); +} + +static gboolean +_cogl_span_iter_end (CoglSpanIter *iter) +{ + /* End reached when whole area covered */ + return iter->pos >= iter->cover_end; +} + +static void +_cogl_subregion_gl_store_rules (gint bmp_rowstride, + gint bmp_width, + gint bmp_bpp, + gint src_x, + gint src_y, + gboolean pack) +{ + const GLenum ALIGNMENT = pack ? + GL_PACK_ALIGNMENT : GL_UNPACK_ALIGNMENT; + + const GLenum ROW_LENGTH = pack ? + GL_PACK_ROW_LENGTH : GL_UNPACK_ROW_LENGTH; + + const GLenum SKIP_ROWS = pack ? + GL_PACK_SKIP_ROWS : GL_UNPACK_SKIP_ROWS; + + const GLenum SKIP_PIXELS = pack ? + GL_PACK_SKIP_PIXELS : GL_UNPACK_SKIP_PIXELS; + + /* Encode the part of the rowstride that is a multiple of bmp_bpp in + ROW_LENGTH and the remainder in ALIGNMENT */ + GE( glPixelStorei (ROW_LENGTH, bmp_rowstride / bmp_bpp) ); + + if (bmp_rowstride == bmp_width * bmp_bpp) + { + GE( glPixelStorei (ALIGNMENT, 1) ); + } + else + { + if ((bmp_rowstride % 4) == 0) + { + GE( glPixelStorei (ALIGNMENT, 4) ); + } + else + { + if ((bmp_rowstride % 2) == 0) + GE( glPixelStorei (ALIGNMENT, 2) ); + } + } + + GE( glPixelStorei (SKIP_ROWS, src_y) ); + GE( glPixelStorei (SKIP_PIXELS, src_x) ); +} + +static gboolean +_cogl_texture_upload_to_gl (CoglTexture *tex) +{ + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint gl_handle; + gint bpp; + gint x,y; + + bpp = _cogl_get_format_bpp (tex->bitmap.format); + + /* Iterate vertical slices */ + for (y = 0; y < tex->slice_y_spans->len; ++y) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + /* Iterate horizontal slices */ + for (x = 0; x < tex->slice_x_spans->len; ++x) + { + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + + /* Pick the gl texture object handle */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + y * tex->slice_x_spans->len + x); + + /* Setup gl alignment to match rowstride and top-left corner */ + _cogl_subregion_gl_store_rules (tex->bitmap.rowstride, + tex->bitmap.width, + bpp, + x_span->start, + y_span->start, + FALSE); + + /* Upload new image data */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glTexSubImage2D (tex->gl_target, 0, 0, 0, + x_span->size - x_span->waste, + y_span->size - y_span->waste, + tex->gl_format, tex->gl_type, + tex->bitmap.data) ); + } + } + + return TRUE; +} + +static gboolean +_cogl_texture_download_from_gl (CoglTexture *tex, + CoglBitmap *target_bmp, + GLuint target_gl_format, + GLuint target_gl_type) +{ + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint gl_handle; + gint bpp; + gint x,y; + CoglBitmap slice_bmp; + + bpp = _cogl_get_format_bpp (target_bmp->format); + + /* Iterate vertical slices */ + for (y = 0; y < tex->slice_y_spans->len; ++y) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + /* Iterate horizontal slices */ + for (x = 0; x < tex->slice_x_spans->len; ++x) + { + /*if (x != 0 || y != 1) continue;*/ + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + + /* Pick the gl texture object handle */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + y * tex->slice_x_spans->len + x); + + /* If there's any waste we need to copy manually + (no glGetTexSubImage) */ + + if (y_span->waste != 0 || x_span->waste != 0) + { + /* Setup temp bitmap for slice subregion */ + slice_bmp.format = tex->bitmap.format; + slice_bmp.width = x_span->size; + slice_bmp.height = y_span->size; + slice_bmp.rowstride = bpp * slice_bmp.width; + slice_bmp.data = (guchar*) g_malloc (slice_bmp.rowstride * + slice_bmp.height); + + /* Setup gl alignment to 0,0 top-left corner */ + _cogl_subregion_gl_store_rules (slice_bmp.rowstride, + slice_bmp.width, + bpp, 0, 0, TRUE); + + /* Download slice image data into temp bmp */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE (glGetTexImage (tex->gl_target, 0, + target_gl_format, + target_gl_type, + slice_bmp.data) ); + + /* Copy portion of slice from temp to target bmp */ + _cogl_bitmap_copy_subregion (&slice_bmp, + target_bmp, + 0, 0, + x_span->start, + y_span->start, + x_span->size - x_span->waste, + y_span->size - y_span->waste); + /* Free temp bitmap */ + g_free (slice_bmp.data); + } + else + { + /* Setup gl alignment to match rowstride and top-left corner */ + + /* FIXME: for some strange reason any value other than 0 + * for GL_PACK_SKIP_PIXELS or GL_PACK_SKIP_ROWS corrupts the + * memory. As a workaround we offset data pointer manually + + _cogl_subregion_gl_store_rules (target_bmp->rowstride, + target_bmp->width, + bpp, + x_span->start, + y_span->start, + TRUE);*/ + _cogl_subregion_gl_store_rules (target_bmp->rowstride, + target_bmp->width, + bpp, + 0, 0, + TRUE); + + /* Download slice image data */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glGetTexImage (tex->gl_target, 0, + target_gl_format, + target_gl_type, + target_bmp->data + + x_span->start * bpp + + y_span->start * target_bmp->rowstride) ); + } + } + } + + return TRUE; +} + +static gboolean +_cogl_texture_upload_subregion_to_gl (CoglTexture *tex, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + gint width, + gint height, + CoglBitmap *source_bmp, + GLuint source_gl_format, + GLuint source_gl_type) +{ + gint bpp; + CoglSpanIter x_iter; + CoglSpanIter y_iter; + GLuint gl_handle; + gint source_x = 0, source_y = 0; + gint inter_w = 0, inter_h = 0; + gint local_x = 0, local_y = 0; + + bpp = _cogl_get_format_bpp (source_bmp->format); + + /* Iterate vertical spans */ + for (source_y = src_y, + _cogl_span_iter_begin (&y_iter, tex->slice_y_spans, + 0, CLUTTER_INT_TO_FIXED (dst_y), + CLUTTER_INT_TO_FIXED (dst_y + height)); + + !_cogl_span_iter_end (&y_iter); + + _cogl_span_iter_next (&y_iter), + source_y += inter_h ) + { + /* Iterate horizontal spans */ + for (source_x = src_x, + _cogl_span_iter_begin (&x_iter, tex->slice_x_spans, + 0, CLUTTER_INT_TO_FIXED (dst_x), + CLUTTER_INT_TO_FIXED (dst_x + width)); + + !_cogl_span_iter_end (&x_iter); + + _cogl_span_iter_next (&x_iter), + source_x += inter_w ) + { + /* Pick intersection width and height */ + inter_w = CLUTTER_FIXED_TO_INT (x_iter.intersect_end - + x_iter.intersect_start); + inter_h = CLUTTER_FIXED_TO_INT (y_iter.intersect_end - + y_iter.intersect_start); + + /* Localize intersection top-left corner to slice*/ + local_x = CLUTTER_FIXED_TO_INT (x_iter.intersect_start - + x_iter.pos); + local_y = CLUTTER_FIXED_TO_INT (y_iter.intersect_start - + y_iter.pos); + + /* Pick slice GL handle */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + y_iter.index * tex->slice_x_spans->len + + x_iter.index); + + /* Setup gl alignment to match rowstride and top-left corner */ + + _cogl_subregion_gl_store_rules (source_bmp->rowstride, + source_bmp->width, + bpp, + source_x, + source_y, + FALSE); + + /* Upload new image data */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glTexSubImage2D (tex->gl_target, 0, + local_x, local_y, + inter_w, inter_h, + source_gl_format, + source_gl_type, + source_bmp->data) ); + } + } + + return TRUE; +} + +static gint +_cogl_rect_slices_for_size (gint size_to_fill, + gint max_span_size, + gint max_waste, + GArray *out_spans) +{ + gint n_spans = 0; + CoglTexSliceSpan span; + + /* Init first slice span */ + span.start = 0; + span.size = max_span_size; + span.waste = 0; + + /* Repeat until whole area covered */ + while (size_to_fill >= span.size) + { + /* Add another slice span of same size */ + if (out_spans) g_array_append_val (out_spans, span); + span.start += span.size; + size_to_fill -= span.size; + n_spans++; + } + + /* Add one last smaller slice span */ + if (size_to_fill > 0) + { + span.size = size_to_fill; + if (out_spans) g_array_append_val (out_spans, span); + n_spans++; + } + + return n_spans; +} + +static gint +_cogl_pot_slices_for_size (gint size_to_fill, + gint max_span_size, + gint max_waste, + GArray *out_spans) +{ + gint n_spans = 0; + CoglTexSliceSpan span; + + /* Init first slice span */ + span.start = 0; + span.size = max_span_size; + span.waste = 0; + + /* Fix invalid max_waste */ + if (max_waste < 0) max_waste = 0; + + while (TRUE) + { + /* Is the whole area covered? */ + if (size_to_fill > span.size) + { + /* Not yet - add a span of this size */ + if (out_spans) g_array_append_val (out_spans, span); + span.start += span.size; + size_to_fill -= span.size; + n_spans++; + } + else if (span.size - size_to_fill <= max_waste) + { + /* Yes and waste is small enough */ + span.waste = span.size - size_to_fill; + if (out_spans) g_array_append_val (out_spans, span); + return ++n_spans; + } + else + { + /* Yes but waste is too large */ + while (span.size - size_to_fill > max_waste) + { + span.size /= 2; + g_assert (span.size > 0); + } + } + } + + /* Can't get here */ + return 0; +} + +static gboolean +_cogl_texture_size_supported (GLenum gl_target, + GLenum gl_format, + GLenum gl_type, + int width, + int height) +{ + if (gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + /* There is no proxy rectangle texture target so best we can + * do is to check against the safest value (although depending + * on our specific format and type the size could be supported + * when it seems it is not) */ + + GLint max_size = 0; + + GE( glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &max_size) ); + + return (max_size && width <= max_size && height <= max_size); + } + else if (gl_target == GL_TEXTURE_2D) + { + /* Proxy texture allows for a quick check for supported size */ + + GLint new_width = 0; + + GE( glTexImage2D (GL_PROXY_TEXTURE_2D, 0, GL_RGBA, + width, height, 0 /* border */, + gl_format, gl_type, NULL) ); + + GE( glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, + GL_TEXTURE_WIDTH, &new_width) ); + + return new_width != 0; + } + else + { + /* not used */ + return 0; + } +} + +static gboolean +_cogl_texture_slices_create (CoglTexture *tex) +{ + gint bpp; + gint max_width; + gint max_height; + GLuint *gl_handles; + gint n_x_slices; + gint n_y_slices; + gint n_slices; + gint x, y; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + const GLfloat transparent_color[4] = { 0x00, 0x00, 0x00, 0x00 }; + + gint (*slices_for_size) (gint, gint, gint, GArray*); + + bpp = _cogl_get_format_bpp (tex->bitmap.format); + + /* Initialize size of largest slice according to supported features*/ + if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT)) + { + max_width = tex->bitmap.width; + max_height = tex->bitmap.height; + tex->gl_target = GL_TEXTURE_2D; + slices_for_size = _cogl_rect_slices_for_size; + } + else if (cogl_features_available (COGL_FEATURE_TEXTURE_RECTANGLE)) + { + max_width = tex->bitmap.width; + max_height = tex->bitmap.height; + tex->gl_target = GL_TEXTURE_RECTANGLE_ARB; + slices_for_size = _cogl_rect_slices_for_size; + } + else + { + max_width = cogl_util_next_p2 (tex->bitmap.width); + max_height = cogl_util_next_p2 (tex->bitmap.height); + tex->gl_target = GL_TEXTURE_2D; + slices_for_size = _cogl_pot_slices_for_size; + } + + /* Negative number means no slicing forced by the user */ + if (tex->max_waste <= -1) + { + CoglTexSliceSpan span; + + /* Check if size supported else bail out */ + if (!_cogl_texture_size_supported (tex->gl_target, + tex->gl_format, + tex->gl_type, + max_width, + max_height)) + { + return FALSE; + } + + n_x_slices = 1; + n_y_slices = 1; + + /* Init span arrays */ + tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + 1); + + tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + 1); + + /* Add a single span for width and height */ + span.start = 0; + span.size = max_width; + span.waste = max_width - tex->bitmap.width; + g_array_append_val (tex->slice_x_spans, span); + + span.size = max_height; + span.waste = max_height - tex->bitmap.height; + g_array_append_val (tex->slice_y_spans, span); + } + else + { + /* Decrease the size of largest slice until supported by GL */ + while (!_cogl_texture_size_supported (tex->gl_target, + tex->gl_format, + tex->gl_type, + max_width, + max_height)) + { + /* Alternate between width and height */ + if (max_width > max_height) + max_width /= 2; + else + max_height /= 2; + + if (max_width == 0 || max_height == 0) + return FALSE; + } + + /* Determine the slices required to cover the bitmap area */ + n_x_slices = slices_for_size (tex->bitmap.width, + max_width, tex->max_waste, + NULL); + + n_y_slices = slices_for_size (tex->bitmap.height, + max_height, tex->max_waste, + NULL); + + /* Init span arrays with reserved size */ + tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + n_x_slices); + + tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + n_y_slices); + + /* Fill span arrays with info */ + slices_for_size (tex->bitmap.width, + max_width, tex->max_waste, + tex->slice_x_spans); + + slices_for_size (tex->bitmap.height, + max_height, tex->max_waste, + tex->slice_y_spans); + } + + /* Init and resize GL handle array */ + n_slices = n_x_slices * n_y_slices; + + tex->slice_gl_handles = g_array_sized_new (FALSE, FALSE, + sizeof (GLuint), + n_slices); + + g_array_set_size (tex->slice_gl_handles, n_slices); + + + /* Hardware repeated tiling if supported, else tile in software*/ + if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && n_slices == 1) + tex->wrap_mode = GL_REPEAT; + else + tex->wrap_mode = GL_CLAMP_TO_EDGE; + + /* Generate a "working set" of GL texture objects + * (some implementations might supported faster + * re-binding between textures inside a set) */ + gl_handles = (GLuint*) tex->slice_gl_handles->data; + + GE( glGenTextures (n_slices, gl_handles) ); + + + /* Init each GL texture object */ + for (y = 0; y < n_y_slices; ++y) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + for (x = 0; x < n_x_slices; ++x) + { + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + +#if COGL_DEBUG + printf ("CREATE SLICE (%d,%d)\n", x,y); + printf ("size: (%d x %d)\n", + x_span->size - x_span->waste, + y_span->size - y_span->waste); +#endif + /* Setup texture parameters */ + GE( glBindTexture (tex->gl_target, gl_handles[y * n_x_slices + x]) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) ); + + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, + tex->wrap_mode) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, + tex->wrap_mode) ); + + /* Use a transparent border color so that we can leave the + color buffer alone when using texture co-ordinates + outside of the texture */ + GE( glTexParameterfv (tex->gl_target, GL_TEXTURE_BORDER_COLOR, + transparent_color) ); + + /* Pass NULL data to init size and internal format */ + GE( glTexImage2D (tex->gl_target, 0, tex->gl_intformat, + x_span->size, y_span->size, 0, + tex->gl_format, tex->gl_type, 0) ); + } + } + + return TRUE; +} + +static void +_cogl_texture_slices_free (CoglTexture *tex) +{ + if (tex->slice_x_spans != NULL) + g_array_free (tex->slice_x_spans, TRUE); + + if (tex->slice_y_spans != NULL) + g_array_free (tex->slice_y_spans, TRUE); + + if (tex->slice_gl_handles != NULL) + { + if (tex->is_foreign == FALSE) + { + GE( glDeleteTextures (tex->slice_gl_handles->len, + (GLuint*) tex->slice_gl_handles->data) ); + } + + g_array_free (tex->slice_gl_handles, TRUE); + } +} + +static gboolean +_cogl_pixel_format_from_gl_internal (GLenum gl_int_format, + CoglPixelFormat *out_format) +{ + /* It doesn't really matter we convert to exact same + format (some have no cogl match anyway) since format + is re-matched against cogl when getting or setting + texture image data. + */ + + switch (gl_int_format) + { + case GL_ALPHA: case GL_ALPHA4: case GL_ALPHA8: + case GL_ALPHA12: case GL_ALPHA16: + + *out_format = COGL_PIXEL_FORMAT_A_8; + return TRUE; + + case GL_LUMINANCE: case GL_LUMINANCE4: case GL_LUMINANCE8: + case GL_LUMINANCE12: case GL_LUMINANCE16: + + *out_format = COGL_PIXEL_FORMAT_G_8; + return TRUE; + + case GL_RGB: case GL_RGB4: case GL_RGB5: case GL_RGB8: + case GL_RGB10: case GL_RGB12: case GL_RGB16: case GL_R3_G3_B2: + + *out_format = COGL_PIXEL_FORMAT_RGB_888; + return TRUE; + + case GL_RGBA: case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1: + case GL_RGBA8: case GL_RGB10_A2: case GL_RGBA12: case GL_RGBA16: + + *out_format = COGL_PIXEL_FORMAT_RGBA_8888; + return TRUE; + } + + return FALSE; +} + +static CoglPixelFormat +_cogl_pixel_format_to_gl (CoglPixelFormat format, + GLenum *out_glintformat, + GLenum *out_glformat, + GLenum *out_gltype) +{ + CoglPixelFormat required_format; + GLenum glintformat = 0; + GLenum glformat = 0; + GLenum gltype = 0; + + /* No premultiplied formats accepted by GL + * (FIXME: latest hardware?) */ + + if (format & COGL_PREMULT_BIT) + format = (format & COGL_UNPREMULT_MASK); + + /* Everything else accepted + * (FIXME: check YUV support) */ + required_format = format; + + /* Find GL equivalents */ + switch (format) + { + case COGL_PIXEL_FORMAT_A_8: + glintformat = GL_ALPHA; + glformat = GL_ALPHA; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_G_8: + glintformat = GL_LUMINANCE; + glformat = GL_LUMINANCE; + gltype = GL_UNSIGNED_BYTE; + break; + + case COGL_PIXEL_FORMAT_RGB_888: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_BGR_888: + glintformat = GL_RGB; + glformat = GL_BGR; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_RGBA_8888: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_BGRA_8888: + glintformat = GL_RGBA; + glformat = GL_BGRA; + gltype = GL_UNSIGNED_BYTE; + break; + + /* The following two types of channel ordering + * have no GL equivalent unless defined using + * system word byte ordering */ + case COGL_PIXEL_FORMAT_ARGB_8888: + glintformat = GL_RGBA; + glformat = GL_BGRA; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + gltype = GL_UNSIGNED_INT_8_8_8_8; +#else + gltype = GL_UNSIGNED_INT_8_8_8_8_REV; +#endif + break; + + case COGL_PIXEL_FORMAT_ABGR_8888: + glintformat = GL_RGBA; + glformat = GL_RGBA; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + gltype = GL_UNSIGNED_INT_8_8_8_8; +#else + gltype = GL_UNSIGNED_INT_8_8_8_8_REV; +#endif + break; + + /* The following three types of channel ordering + * are always defined using system word byte + * ordering (even according to GLES spec) */ + case COGL_PIXEL_FORMAT_RGB_565: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_SHORT_5_6_5; + break; + case COGL_PIXEL_FORMAT_RGBA_4444: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case COGL_PIXEL_FORMAT_RGBA_5551: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_5_5_5_1; + break; + + /* FIXME: check extensions for YUV support */ + default: + break; + } + + if (out_glintformat != NULL) + *out_glintformat = glintformat; + if (out_glformat != NULL) + *out_glformat = glformat; + if (out_gltype != NULL) + *out_gltype = gltype; + + return required_format; +} + +static gboolean +_cogl_texture_bitmap_prepare (CoglTexture *tex, + CoglPixelFormat internal_format) +{ + CoglBitmap new_bitmap; + CoglPixelFormat new_data_format; + gboolean success; + + /* Was there any internal conversion requested? */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + internal_format = tex->bitmap.format; + + /* Find closest format accepted by GL */ + new_data_format = _cogl_pixel_format_to_gl (internal_format, + &tex->gl_intformat, + &tex->gl_format, + &tex->gl_type); + + /* Convert to internal format */ + if (new_data_format != tex->bitmap.format) + { + success = _cogl_bitmap_convert_and_premult (&tex->bitmap, + &new_bitmap, + new_data_format); + + if (!success) + return FALSE; + + /* Update texture with new data */ + _cogl_texture_bitmap_swap (tex, &new_bitmap); + } + + return TRUE; +} + +static void +_cogl_texture_free (CoglTexture *tex) +{ + /* Frees texture resources but its handle is not + released! Do that separately before this! */ + _cogl_texture_bitmap_free (tex); + _cogl_texture_slices_free (tex); + g_free (tex); +} + +CoglHandle +cogl_texture_new_with_size (guint width, + guint height, + gint max_waste, + CoglPixelFormat internal_format) +{ + CoglTexture *tex; + gint bpp; + gint rowstride; + + /* Since no data, we need some internal format */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + /* Rowstride from width */ + bpp = _cogl_get_format_bpp (internal_format); + rowstride = width * bpp; + + /* Init texture with empty bitmap */ + tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); + + tex->ref_count = 1; +#if COGL_DEBUG + printf ("COGL TEX new %p %i\n", tex, tex->ref_count); +#endif + + tex->is_foreign = FALSE; + + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.format = internal_format; + tex->bitmap.rowstride = rowstride; + tex->bitmap.data = NULL; + tex->bitmap_owner = FALSE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* Find closest GL format match */ + tex->bitmap.format = + _cogl_pixel_format_to_gl (internal_format, + &tex->gl_intformat, + &tex->gl_format, + &tex->gl_type); + + /* Create slices for the given format and size */ + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_new_from_data (guint width, + guint height, + gint max_waste, + CoglPixelFormat format, + CoglPixelFormat internal_format, + guint rowstride, + const guchar *data) +{ + CoglTexture *tex; + gint bpp; + + if (format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + if (data == NULL) + return COGL_INVALID_HANDLE; + + /* Rowstride from width if not given */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = width * bpp; + + /* Create new texture and fill with given data */ + tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); + + tex->ref_count = 1; +#if COGL_DEBUG + printf ("COGL TEX new %p %i\n", tex, tex->ref_count); +#endif + + tex->is_foreign = FALSE; + + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.data = (guchar*)data; + tex->bitmap.format = format; + tex->bitmap.rowstride = rowstride; + tex->bitmap_owner = FALSE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* FIXME: If upload fails we should set some kind of + * error flag but still return texture handle (this + * is to keep the behavior equal to _new_from_file; + * see below) */ + + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_upload_to_gl (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + _cogl_texture_bitmap_free (tex); + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_new_from_file (const gchar *filename, + gint max_waste, + CoglPixelFormat internal_format, + GError **error) +{ + CoglBitmap bmp; + CoglTexture *tex; + + g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE); + + /* Try loading with imaging backend */ + if (!_cogl_bitmap_from_file (&bmp, filename, error)) + { + /* Try fallback */ + if (!_cogl_bitmap_fallback_from_file (&bmp, filename)) + return COGL_INVALID_HANDLE; + else if (error && *error) + { + g_error_free (*error); + *error = NULL; + } + } + + /* Create new texture and fill with loaded data */ + tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture)); + + tex->ref_count = 1; +#if COGL_DEBUG + printf ("COGL TEX new %p %i\n", tex, tex->ref_count); +#endif + + tex->is_foreign = FALSE; + + tex->bitmap = bmp; + tex->bitmap_owner = TRUE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* FIXME: If upload fails we should set some kind of + * error flag but still return texture handle if the + * user decides to destroy another texture and upload + * this one instead (reloading from file is not needed + * in that case). As a rule then, everytime a valid + * CoglHandle is returned, it should also be destroyed + * with cogl_texture_unref at some point! */ + + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_upload_to_gl (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + _cogl_texture_bitmap_free (tex); + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_new_from_foreign (GLuint gl_handle, + GLenum gl_target, + GLuint width, + GLuint height, + GLuint x_pot_waste, + GLuint y_pot_waste, + CoglPixelFormat format) +{ + /* NOTE: width, height and internal format are not queriable + in GLES, hence such a function prototype. However, here + they are still queried from the texture for improved + robustness and for completeness in case GLES 1.0 gets + unsupported in favor of a new version and cleaner api + */ + + GLenum gl_error = 0; + GLboolean gl_istexture; + GLint gl_compressed = GL_FALSE; + GLint gl_int_format = 0; + GLint gl_width = 0; + GLint gl_height = 0; + GLint gl_min_filter; + GLint gl_mag_filter; + guint bpp; + CoglTexture *tex; + CoglTexSliceSpan x_span; + CoglTexSliceSpan y_span; + + /* Allow 2-dimensional textures only */ + if (gl_target != GL_TEXTURE_2D && + gl_target != GL_TEXTURE_RECTANGLE_ARB) + return COGL_INVALID_HANDLE; + + /* Make sure it is a valid GL texture object */ + gl_istexture = GE( glIsTexture (gl_handle) ); + if (gl_istexture == GL_FALSE) + return COGL_INVALID_HANDLE; + + /* Make sure binding succeeds */ + gl_error = glGetError (); + glBindTexture (gl_target, gl_handle); + if (glGetError () != GL_NO_ERROR) + return COGL_INVALID_HANDLE; + + /* Obtain texture parameters + (only level 0 we are interested in) */ + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_COMPRESSED, + &gl_compressed) ); + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_INTERNAL_FORMAT, + &gl_int_format) ); + + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_WIDTH, + &gl_width) ); + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_HEIGHT, + &gl_height) ); + + GE( glGetTexParameteriv (gl_target, + GL_TEXTURE_MIN_FILTER, + &gl_min_filter)); + + GE( glGetTexParameteriv (gl_target, + GL_TEXTURE_MAG_FILTER, + &gl_mag_filter)); + + /* Validate width and height */ + if (gl_width <= 0 || gl_height <= 0) + return COGL_INVALID_HANDLE; + + /* Validate pot waste */ + if (x_pot_waste < 0 || x_pot_waste >= gl_width || + y_pot_waste < 0 || y_pot_waste >= gl_height) + return COGL_INVALID_HANDLE; + + /* Compressed texture images not supported */ + if (gl_compressed == GL_TRUE) + return COGL_INVALID_HANDLE; + + /* Try and match to a cogl format */ + if (!_cogl_pixel_format_from_gl_internal (gl_int_format, + &format)) + { + return COGL_INVALID_HANDLE; + } + + bpp = _cogl_get_format_bpp (format); + + /* Create new texture */ + tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture)); + + tex->ref_count = 1; +#if COGL_DEBUG + printf ("COGL TEX new %p %i\n", tex, tex->ref_count); +#endif + + /* Setup bitmap info */ + tex->is_foreign = TRUE; + + tex->bitmap.format = format; + tex->bitmap.width = gl_width - x_pot_waste; + tex->bitmap.height = gl_height - y_pot_waste; + tex->bitmap.rowstride = tex->bitmap.width * bpp; + tex->bitmap_owner = FALSE; + + tex->gl_target = gl_target; + tex->gl_intformat = gl_int_format; + tex->gl_format = gl_int_format; + tex->gl_type = GL_UNSIGNED_BYTE; + + tex->min_filter = gl_min_filter; + tex->mag_filter = gl_mag_filter; + tex->max_waste = 0; + + /* Create slice arrays */ + tex->slice_x_spans = + g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), 1); + + tex->slice_y_spans = + g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), 1); + + tex->slice_gl_handles = + g_array_sized_new (FALSE, FALSE, + sizeof (GLuint), 1); + + /* Store info for a single slice */ + x_span.start = 0; + x_span.size = gl_width; + x_span.waste = x_pot_waste; + g_array_append_val (tex->slice_x_spans, x_span); + + y_span.start = 0; + y_span.size = gl_height; + y_span.waste = y_pot_waste; + g_array_append_val (tex->slice_y_spans, y_span); + + g_array_append_val (tex->slice_gl_handles, gl_handle); + + /* Replace mipmap min filter modes with single level ones */ + if (gl_min_filter != GL_NEAREST && gl_min_filter != GL_LINEAR) + { + if (gl_min_filter == GL_NEAREST_MIPMAP_NEAREST) + { + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST) ); + tex->min_filter = CGL_NEAREST; + } + else + { + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR) ); + tex->min_filter = CGL_LINEAR; + } + } + + /* Force appropriate wrap parameter */ + if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) && + gl_target == GL_TEXTURE_2D) + { + /* Hardware repeated tiling */ + tex->wrap_mode = GL_REPEAT; + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_REPEAT) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_REPEAT) ); + } + else + { + /* Any tiling will be done in software */ + tex->wrap_mode = GL_CLAMP_TO_EDGE; + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) ); + } + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_ref (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return COGL_INVALID_HANDLE; + + tex = _cogl_texture_pointer_from_handle (handle); + + tex->ref_count++; +#if COGL_DEBUG + printf ("COGL TEX ref %p %i\n", tex, tex->ref_count); +#endif + + return handle; +} + +void +cogl_texture_unref (CoglHandle handle) +{ + /* Check if valid texture handle */ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + +#if COGL_DEBUG + printf ("COGL TEX unref %p %i\n", tex, tex->ref_count - 1); +#endif + + if (--tex->ref_count < 1) + { +#if COGL_DEBUG + printf ("COGL TEX free %p %i\n", tex, tex->ref_count - 1); +#endif + + /* Free texture handle and resources */ + _cogl_texture_handle_release (tex); + _cogl_texture_free (tex); + } +} + +guint +cogl_texture_get_width (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.width; +} + +guint +cogl_texture_get_height (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.height; +} + +CoglPixelFormat +cogl_texture_get_format (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return COGL_PIXEL_FORMAT_ANY; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.format; +} + +guint +cogl_texture_get_rowstride (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.rowstride; +} + +gint +cogl_texture_get_max_waste (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->max_waste; +} + +gboolean +cogl_texture_is_sliced (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + if (tex->slice_gl_handles == NULL) + return FALSE; + + if (tex->slice_gl_handles->len <= 1) + return FALSE; + + return TRUE; +} + +gboolean +cogl_texture_get_gl_texture (CoglHandle handle, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + if (tex->slice_gl_handles == NULL) + return FALSE; + + if (tex->slice_gl_handles->len < 1) + return FALSE; + + if (out_gl_handle != NULL) + *out_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + + if (out_gl_target != NULL) + *out_gl_target = tex->gl_target; + + return TRUE; +} + +COGLenum +cogl_texture_get_min_filter (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->min_filter; +} + +COGLenum +cogl_texture_get_mag_filter (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->mag_filter; +} + +void +cogl_texture_set_filters (CoglHandle handle, + COGLenum min_filter, + COGLenum mag_filter) +{ + CoglTexture *tex; + GLuint gl_handle; + int i; + + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Store new values */ + tex->min_filter = min_filter; + tex->mag_filter = mag_filter; + + /* Make sure slices were created */ + if (tex->slice_gl_handles == NULL) + return; + + /* Apply new filters to every slice */ + for (i=0; i<tex->slice_gl_handles->len; ++i) + { + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, i); + GE( glBindTexture (tex->gl_target, gl_handle) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) ); + } +} + +gboolean +cogl_texture_set_region (CoglHandle handle, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + guint dst_width, + guint dst_height, + gint width, + gint height, + CoglPixelFormat format, + guint rowstride, + const guchar *data) +{ + CoglTexture *tex; + gint bpp; + CoglBitmap source_bmp; + CoglBitmap temp_bmp; + gboolean source_bmp_owner = FALSE; + CoglPixelFormat closest_format; + GLenum closest_gl_format; + GLenum closest_gl_type; + gboolean success; + + /* Check if valid texture handle */ + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Check for valid format */ + if (format == COGL_PIXEL_FORMAT_ANY) + return FALSE; + + /* Init source bitmap */ + source_bmp.width = width; + source_bmp.height = height; + source_bmp.format = format; + source_bmp.data = (guchar*)data; + + /* Rowstride from width if none specified */ + bpp = _cogl_get_format_bpp (format); + source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride; + + /* Find closest format to internal that's supported by GL */ + closest_format = _cogl_pixel_format_to_gl (tex->bitmap.format, + NULL, /* don't need */ + &closest_gl_format, + &closest_gl_type); + + /* If no direct match, convert */ + if (closest_format != format) + { + /* Convert to required format */ + success = _cogl_bitmap_convert_and_premult (&source_bmp, + &temp_bmp, + closest_format); + + /* Swap bitmaps if succeeded */ + if (!success) return FALSE; + source_bmp = temp_bmp; + source_bmp_owner = TRUE; + } + + /* Send data to GL */ + _cogl_texture_upload_subregion_to_gl (tex, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + &source_bmp, + closest_gl_format, + closest_gl_type); + + /* Free data if owner */ + if (source_bmp_owner) + g_free (source_bmp.data); + + return TRUE; +} + +gint +cogl_texture_get_data (CoglHandle handle, + CoglPixelFormat format, + guint rowstride, + guchar *data) +{ + CoglTexture *tex; + gint bpp; + gint byte_size; + CoglPixelFormat closest_format; + gint closest_bpp; + GLenum closest_gl_format; + GLenum closest_gl_type; + CoglBitmap target_bmp; + CoglBitmap new_bmp; + gboolean success; + guchar *src; + guchar *dst; + gint y; + + /* Check if valid texture handle */ + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Default to internal format if none specified */ + if (format == COGL_PIXEL_FORMAT_ANY) + format = tex->bitmap.format; + + /* Rowstride from texture width if none specified */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = tex->bitmap.width * bpp; + + /* Return byte size if only that requested */ + byte_size = tex->bitmap.height * rowstride; + if (data == NULL) return byte_size; + + /* Find closest format that's supported by GL */ + closest_format = _cogl_pixel_format_to_gl (format, + NULL, /* don't need */ + &closest_gl_format, + &closest_gl_type); + + closest_bpp = _cogl_get_format_bpp (closest_format); + + /* Is the requested format supported? */ + if (closest_format == format) + { + /* Target user data directly */ + target_bmp = tex->bitmap; + target_bmp.format = format; + target_bmp.rowstride = rowstride; + target_bmp.data = data; + } + else + { + /* Target intermediate buffer */ + target_bmp = tex->bitmap; + target_bmp.format = closest_format; + target_bmp.rowstride = target_bmp.width * closest_bpp; + target_bmp.data = (guchar*) g_malloc (target_bmp.height + * target_bmp.rowstride); + } + + /* Retrieve data from slices */ + _cogl_texture_download_from_gl (tex, &target_bmp, + closest_gl_format, + closest_gl_type); + + /* Was intermediate used? */ + if (closest_format != format) + { + /* Convert to requested format */ + success = _cogl_bitmap_convert_and_premult (&target_bmp, + &new_bmp, + format); + + /* Free intermediate data and return if failed */ + g_free (target_bmp.data); + if (!success) return 0; + + /* Copy to user buffer */ + for (y = 0; y < new_bmp.height; ++y) + { + src = new_bmp.data + y * new_bmp.rowstride; + dst = data + y * rowstride; + memcpy (dst, src, new_bmp.width); + } + + /* Free converted data */ + g_free (new_bmp.data); + } + + return byte_size; +} + +static void +_cogl_texture_quad_sw (CoglTexture *tex, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglSpanIter iter_x , iter_y; + ClutterFixed tw , th; + ClutterFixed tqx , tqy; + ClutterFixed first_tx , first_ty; + ClutterFixed first_qx , first_qy; + ClutterFixed slice_tx1 , slice_ty1; + ClutterFixed slice_tx2 , slice_ty2; + ClutterFixed slice_qx1 , slice_qy1; + ClutterFixed slice_qx2 , slice_qy2; + GLuint gl_handle; + +#if COGL_DEBUG + printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); +#endif + + /* Prepare GL state */ + gulong enable_flags = 0; + + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; + + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + { + enable_flags |= COGL_ENABLE_BLEND; + } + + cogl_enable (enable_flags); + + /* Scale ratio from texture to quad widths */ + tw = CLUTTER_INT_TO_FIXED (tex->bitmap.width); + th = CLUTTER_INT_TO_FIXED (tex->bitmap.height); + + tqx = CFX_QDIV (x2-x1, CFX_QMUL (tw, (tx2 - tx1))); + tqy = CFX_QDIV (y2-y1, CFX_QMUL (th, (ty2 - ty1))); + + /* Integral texture coordinate for first tile */ + first_tx = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (tx1)); + first_ty = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (ty1)); + + /* Denormalize texture coordinates */ + first_tx = CFX_QMUL (first_tx, tw); + first_ty = CFX_QMUL (first_ty, th); + tx1 = CFX_QMUL (tx1, tw); + ty1 = CFX_QMUL (ty1, th); + tx2 = CFX_QMUL (tx2, tw); + ty2 = CFX_QMUL (ty2, th); + + /* Quad coordinate of the first tile */ + first_qx = x1 - CFX_QMUL (tx1 - first_tx, tqx); + first_qy = y1 - CFX_QMUL (ty1 - first_ty, tqy); + + + /* Iterate until whole quad height covered */ + for (_cogl_span_iter_begin (&iter_y, tex->slice_y_spans, + first_ty, ty1, ty2) ; + !_cogl_span_iter_end (&iter_y) ; + _cogl_span_iter_next (&iter_y) ) + { + /* Discard slices out of quad early */ + if (!iter_y.intersects) continue; + + /* Span-quad intersection in quad coordinates */ + slice_qy1 = first_qy + + CFX_QMUL (iter_y.intersect_start - first_ty, tqy); + + slice_qy2 = first_qy + + CFX_QMUL (iter_y.intersect_end - first_ty, tqy); + + /* Localize slice texture coordinates */ + slice_ty1 = iter_y.intersect_start - iter_y.pos; + slice_ty2 = iter_y.intersect_end - iter_y.pos; + + /* Normalize texture coordinates to current slice + (rectangle texture targets take denormalized) */ + if (tex->gl_target != GL_TEXTURE_RECTANGLE_ARB) + { + slice_ty1 /= iter_y.span->size; + slice_ty2 /= iter_y.span->size; + } + + + /* Iterate until whole quad width covered */ + for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans, + first_tx, tx1, tx2) ; + !_cogl_span_iter_end (&iter_x) ; + _cogl_span_iter_next (&iter_x) ) + { + /* Discard slices out of quad early */ + if (!iter_x.intersects) continue; + + /* Span-quad intersection in quad coordinates */ + slice_qx1 = first_qx + + CFX_QMUL (iter_x.intersect_start - first_tx, tqx); + + slice_qx2 = first_qx + + CFX_QMUL (iter_x.intersect_end - first_tx, tqx); + + /* Localize slice texture coordinates */ + slice_tx1 = iter_x.intersect_start - iter_x.pos; + slice_tx2 = iter_x.intersect_end - iter_x.pos; + + /* Normalize texture coordinates to current slice + (rectangle texture targets take denormalized) */ + if (tex->gl_target != GL_TEXTURE_RECTANGLE_ARB) + { + slice_tx1 /= iter_x.span->size; + slice_tx2 /= iter_x.span->size; + } + +#if COGL_DEBUG + printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index); + printf("qx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx1)); + printf("qy1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy1)); + printf("qx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx2)); + printf("qy2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy2)); + printf("tx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx1)); + printf("ty1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty1)); + printf("tx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx2)); + printf("ty2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty2)); +#endif + + /* Pick and bind opengl texture object */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + iter_y.index * iter_x.array->len + + iter_x.index); + + GE( glBindTexture (tex->gl_target, gl_handle) ); + +#define CFX_F CLUTTER_FIXED_TO_FLOAT + + /* Draw textured quad */ + GE( glBegin (GL_QUADS) ); + + GE( glTexCoord2f (CFX_F(slice_tx1), CFX_F(slice_ty1)) ); + GE( glVertex2f (CFX_F(slice_qx1), CFX_F(slice_qy1)) ); + + GE( glTexCoord2f (CFX_F(slice_tx2), CFX_F(slice_ty1)) ); + GE( glVertex2f (CFX_F(slice_qx2), CFX_F(slice_qy1)) ); + + GE( glTexCoord2f (CFX_F(slice_tx2), CFX_F(slice_ty2)) ); + GE( glVertex2f (CFX_F(slice_qx2), CFX_F(slice_qy2)) ); + + GE( glTexCoord2f (CFX_F(slice_tx1), CFX_F(slice_ty2)) ); + GE( glVertex2f (CFX_F(slice_qx1), CFX_F(slice_qy2)) ); + + glEnd (); + +#undef CFX_F + } + } +} + +static void +_cogl_texture_quad_hw (CoglTexture *tex, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint gl_handle; + +#if COGL_DEBUG + printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); +#endif + + /* Prepare GL state */ + gulong enable_flags = 0; + + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; + + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + { + enable_flags |= COGL_ENABLE_BLEND; + } + + cogl_enable (enable_flags); + + /* Pick and bind opengl texture object */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + GE( glBindTexture (tex->gl_target, gl_handle) ); + + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + + /* Don't include the waste in the texture coordinates */ + tx1 = tx1 * (x_span->size - x_span->waste) / x_span->size; + tx2 = tx2 * (x_span->size - x_span->waste) / x_span->size; + ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size; + ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; + + /* Denormalize texture coordinates for rectangle textures */ + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + tx1 *= x_span->size; + tx2 *= x_span->size; + ty1 *= y_span->size; + ty2 *= y_span->size; + } + +#define CFX_F(x) CLUTTER_FIXED_TO_FLOAT(x) + + /* Draw textured quad */ + GE( glBegin (GL_QUADS) ); + + GE( glTexCoord2f (CFX_F(tx1), CFX_F(ty1)) ); + GE( glVertex2f (CFX_F(x1), CFX_F(y1)) ); + + GE( glTexCoord2f (CFX_F(tx2), CFX_F(ty1)) ); + GE( glVertex2f (CFX_F(x2), CFX_F(y1)) ); + + GE( glTexCoord2f (CFX_F(tx2), CFX_F(ty2)) ); + GE( glVertex2f (CFX_F(x2), CFX_F(y2)) ); + + GE( glTexCoord2f (CFX_F(tx1), CFX_F(ty2)) ); + GE( glVertex2f (CFX_F(x1), CFX_F(y2)) ); + + glEnd (); + +#undef CFX_F +} + +void +cogl_texture_rectangle (CoglHandle handle, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + CoglTexture *tex; + ClutterFixed tempx; + + /* Check if valid texture */ + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Make sure we got stuff to draw */ + if (tex->slice_gl_handles == NULL) + return; + + if (tex->slice_gl_handles->len == 0) + return; + + if (tx1 == tx2 || ty1 == ty2) + return; + + /* Fix quad coord ordering + (atm this is required for sw tiling to iterate + over slices properly) */ + if (x1 > x2) + { + tempx = x1; + x1 = x2; + x2 = tempx; + } + + if (y1 > y2) + { + tempx = y1; + y1 = y2; + y2 = tempx; + } + + /* Fix texture coord ordering */ + if (tx1 > tx2) + { + tempx = tx1; + tx1 = tx2; + tx2 = tempx; + } + + if (ty1 > ty2) + { + tempx = ty1; + ty1 = ty2; + ty2 = tempx; + } + + /* Pick tiling mode according to hw support */ + if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && tex->slice_gl_handles->len == 1) + { + _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + } + else + { + if (tex->slice_gl_handles->len == 1 + && tx1 >= -CFX_ONE && tx2 <= CFX_ONE + && ty1 >= -CFX_ONE && ty2 <= CFX_ONE) + { + _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + } + else + { + _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + } + } +} + +void +cogl_texture_polygon (CoglHandle handle, + guint n_vertices, + CoglTextureVertex *vertices, + gboolean use_color) +{ + CoglTexture *tex; + int i, x, y, vnum; + GLuint gl_handle; + CoglTexSliceSpan *y_span, *x_span; + + /* Check if valid texture */ + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* The polygon will have artifacts where the slices join if the wrap + mode is GL_LINEAR because the filtering will pull in pixels from + the transparent border. To make it clear that the function + shouldn't be used in these circumstances we just bail out and + draw nothing */ + if (tex->slice_gl_handles->len != 1 + && (tex->min_filter != GL_NEAREST || tex->mag_filter != GL_NEAREST)) + { + static gboolean shown_warning = FALSE; + + if (!shown_warning) + { + g_warning ("cogl_texture_polygon does not work for sliced textures " + "when the minification and magnification filters are not " + "CGL_NEAREST"); + shown_warning = TRUE; + } + return; + } + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Prepare GL state */ + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + cogl_enable (COGL_ENABLE_TEXTURE_RECT | COGL_ENABLE_BLEND); + else + cogl_enable (COGL_ENABLE_TEXTURE_2D | COGL_ENABLE_BLEND); + + /* Temporarily change the wrapping mode on all of the slices to use + a transparent border */ + for (i = 0; i < tex->slice_gl_handles->len; i++) + { + GE( glBindTexture (tex->gl_target, + g_array_index (tex->slice_gl_handles, GLuint, i)) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_BORDER) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_BORDER) ); + } + + i = 0; + + /* Render all of the slices with the full geometry but use a + transparent border color so that any part of the texture not + covered by the slice will be ignored */ + for (y = 0; y < tex->slice_y_spans->len; y++) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + for (x = 0; x < tex->slice_x_spans->len; x++) + { + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, i++); + + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glBegin (GL_TRIANGLE_FAN) ); + + for (vnum = 0; vnum < n_vertices; vnum++) + { + GLfloat tx, ty; + + if (use_color) + cogl_color (&vertices[vnum].color); + + /* Transform the texture co-ordinates so they are + relative to the slice */ + tx = (CLUTTER_FIXED_TO_FLOAT (vertices[vnum].tx) + - x_span->start / (GLfloat) tex->bitmap.width) + * tex->bitmap.width / x_span->size; + ty = (CLUTTER_FIXED_TO_FLOAT (vertices[vnum].ty) + - y_span->start / (GLfloat) tex->bitmap.height) + * tex->bitmap.height / y_span->size; + + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + tx *= x_span->size; + ty *= y_span->size; + } + + GE( glTexCoord2f (tx, ty) ); + + GE( glVertex3f (CLUTTER_FIXED_TO_FLOAT (vertices[vnum].x), + CLUTTER_FIXED_TO_FLOAT (vertices[vnum].y), + CLUTTER_FIXED_TO_FLOAT (vertices[vnum].z)) ); + } + + GE( glEnd () ); + } + } + + /* Restore the wrapping mode */ + for (i = 0; i < tex->slice_gl_handles->len; i++) + { + GE( glBindTexture (tex->gl_target, + g_array_index (tex->slice_gl_handles, GLuint, i)) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, tex->wrap_mode) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, tex->wrap_mode) ); + } +} diff --git a/gl/cogl-texture.h b/gl/cogl-texture.h new file mode 100644 index 00000000..d4104804 --- /dev/null +++ b/gl/cogl-texture.h @@ -0,0 +1,64 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_TEXTURE_H +#define __COGL_TEXTURE_H + +#include "cogl-bitmap.h" + +typedef struct _CoglTexture CoglTexture; +typedef struct _CoglTexSliceSpan CoglTexSliceSpan; +typedef struct _CoglSpanIter CoglSpanIter; + +struct _CoglTexSliceSpan +{ + gint start; + gint size; + gint waste; +}; + +struct _CoglTexture +{ + guint ref_count; + CoglBitmap bitmap; + gboolean bitmap_owner; + GLenum gl_target; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + GArray *slice_x_spans; + GArray *slice_y_spans; + GArray *slice_gl_handles; + gint max_waste; + COGLenum min_filter; + COGLenum mag_filter; + gboolean is_foreign; + GLint wrap_mode; +}; + +CoglTexture* +_cogl_texture_pointer_from_handle (CoglHandle handle); + +#endif /* __COGL_TEXTURE_H */ @@ -39,23 +39,12 @@ typedef CoglFuncPtr (*GLXGetProcAddressProc) (const guint8 *procName); #endif -static gulong __enable_flags = 0; - -/* FBO Procs */ -typedef void (*GenFramebuffers) (GLsizei n, GLuint *ids); -typedef void (*BindFramebuffer) (GLenum target, GLuint framebuffer); -typedef void (*FramebufferTexture2D) (GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, - GLint level); -typedef GLenum (*CheckFramebufferStatus)(GLenum target); -typedef void (*DeleteFramebuffers) (GLsizei n, const GLuint *framebuffers); - -static GenFramebuffers _gen_framebuffers = NULL; -static BindFramebuffer _bind_framebuffer = NULL; -static FramebufferTexture2D _framebuffer_texture_2d = NULL; -static CheckFramebufferStatus _check_framebuffer_status = NULL; -static DeleteFramebuffers _delete_framebuffers = NULL; +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-context.h" + +/* GL error to string conversion */ #if COGL_DEBUG struct token_string { @@ -89,19 +78,21 @@ error_string(GLenum errorCode) } #endif -#if COGL_DEBUG -#define GE(x...) G_STMT_START { \ - GLenum err; \ - (x); \ - while ((err = glGetError()) != GL_NO_ERROR) { \ - fprintf(stderr, "glError: %s caught at %s:%u\n", \ - (char *)error_string(err), \ - __FILE__, __LINE__); \ - } \ -} G_STMT_END -#else -#define GE(x) (x); -#endif + +/* Expecting ARB functions not to be defined */ +#define glCreateProgramObjectARB ctx->pf_glCreateProgramObjectARB +#define glCreateShaderObjectARB ctx->pf_glCreateShaderObjectARB +#define glShaderSourceARB ctx->pf_glShaderSourceARB +#define glCompileShaderARB ctx->pf_glCompileShaderARB +#define glAttachObjectARB ctx->pf_glAttachObjectARB +#define glLinkProgramARB ctx->pf_glLinkProgramARB +#define glUseProgramObjectARB ctx->pf_glUseProgramObjectARB +#define glGetUniformLocationARB ctx->pf_glGetUniformLocationARB +#define glDeleteObjectARB ctx->pf_glDeleteObjectARB +#define glGetInfoLogARB ctx->pf_glGetInfoLogARB +#define glGetObjectParameterivARB ctx->pf_glGetObjectParameterivARB +#define glUniform1fARB ctx->pf_glUniform1fARB + CoglFuncPtr cogl_get_proc_address (const gchar* name) @@ -217,9 +208,6 @@ cogl_paint_init (const ClutterColor *color) * glDepthFunc (GL_LEQUAL); * glAlphaFunc (GL_GREATER, 0.1); */ - - cogl_enable (CGL_ENABLE_BLEND); - glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } /* FIXME: inline most of these */ @@ -272,59 +260,155 @@ cogl_rotate (gint angle, gint x, gint y, gint z) glRotatef ((float)angle, (float)x, (float)y, (float)z); } +static inline gboolean +cogl_toggle_flag (CoglContext *ctx, + gulong new_flags, + gulong flag, + GLenum gl_flag) +{ + /* Toggles and caches a single enable flag on or off + * by comparing to current state + */ + if (new_flags & flag) + { + if (!(ctx->enable_flags & flag)) + { + GE( glEnable (gl_flag) ); + ctx->enable_flags |= flag; + return TRUE; + } + } + else if (ctx->enable_flags & flag) + { + GE( glDisable (gl_flag) ); + ctx->enable_flags &= ~flag; + } + + return FALSE; +} + +static inline gboolean +cogl_toggle_client_flag (CoglContext *ctx, + gulong new_flags, + gulong flag, + GLenum gl_flag) +{ + /* Toggles and caches a single client-side enable flag + * on or off by comparing to current state + */ + if (new_flags & flag) + { + if (!(ctx->enable_flags & flag)) + { + GE( glEnableClientState (gl_flag) ); + ctx->enable_flags |= flag; + return TRUE; + } + } + else if (ctx->enable_flags & flag) + { + GE( glDisableClientState (gl_flag) ); + ctx->enable_flags &= ~flag; + } + + return FALSE; +} + void cogl_enable (gulong flags) { /* This function essentially caches glEnable state() in the * hope of lessening number GL traffic. */ - if (flags & CGL_ENABLE_BLEND) + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (cogl_toggle_flag (ctx, flags, + COGL_ENABLE_BLEND, + GL_BLEND)) { - if (!(__enable_flags & CGL_ENABLE_BLEND)) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + cogl_toggle_flag (ctx, flags, + COGL_ENABLE_TEXTURE_2D, + GL_TEXTURE_2D); + + cogl_toggle_client_flag (ctx, flags, + COGL_ENABLE_VERTEX_ARRAY, + GL_VERTEX_ARRAY); + + cogl_toggle_client_flag (ctx, flags, + COGL_ENABLE_TEXCOORD_ARRAY, + GL_TEXTURE_COORD_ARRAY); + +#ifdef GL_TEXTURE_RECTANGLE_ARB + cogl_toggle_flag (ctx, flags, + COGL_ENABLE_TEXTURE_RECT, + GL_TEXTURE_RECTANGLE_ARB); +#endif +} + +gulong +cogl_get_enable () +{ + _COGL_GET_CONTEXT (ctx, 0); + + return ctx->enable_flags; +} + +/* +void +cogl_enable (gulong flags) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (flags & COGL_ENABLE_BLEND) + { + if (!(ctx->enable_flags & COGL_ENABLE_BLEND)) { glEnable (GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - __enable_flags |= CGL_ENABLE_BLEND; + ctx->enable_flags |= COGL_ENABLE_BLEND; } } - else if (__enable_flags & CGL_ENABLE_BLEND) + else if (ctx->enable_flags & COGL_ENABLE_BLEND) { glDisable (GL_BLEND); - __enable_flags &= ~CGL_ENABLE_BLEND; + ctx->enable_flags &= ~COGL_ENABLE_BLEND; } - if (flags & CGL_ENABLE_TEXTURE_2D) + if (flags & COGL_ENABLE_TEXTURE_2D) { - if (!(__enable_flags & CGL_ENABLE_TEXTURE_2D)) + if (!(ctx->enable_flags & COGL_ENABLE_TEXTURE_2D)) { glEnable (GL_TEXTURE_2D); - __enable_flags |= CGL_ENABLE_TEXTURE_2D; + ctx->enable_flags |= COGL_ENABLE_TEXTURE_2D; } } - else if (__enable_flags & CGL_ENABLE_TEXTURE_2D) + else if (ctx->enable_flags & COGL_ENABLE_TEXTURE_2D) { glDisable (GL_TEXTURE_2D); - __enable_flags &= ~CGL_ENABLE_TEXTURE_2D; + ctx->enable_flags &= ~COGL_ENABLE_TEXTURE_2D; } - + #ifdef GL_TEXTURE_RECTANGLE_ARB - if (flags & CGL_ENABLE_TEXTURE_RECT) + if (flags & COGL_ENABLE_TEXTURE_RECT) { - if (!(__enable_flags & CGL_ENABLE_TEXTURE_RECT)) + if (!(ctx->enable_flags & COGL_ENABLE_TEXTURE_RECT)) { glEnable (GL_TEXTURE_RECTANGLE_ARB); - __enable_flags |= CGL_ENABLE_TEXTURE_RECT; + ctx->enable_flags |= COGL_ENABLE_TEXTURE_RECT; } } - else if (__enable_flags & CGL_ENABLE_TEXTURE_RECT) + else if (ctx->enable_flags & COGL_ENABLE_TEXTURE_RECT) { glDisable (GL_TEXTURE_RECTANGLE_ARB); - __enable_flags &= ~CGL_ENABLE_TEXTURE_RECT; + ctx->enable_flags &= ~COGL_ENABLE_TEXTURE_RECT; } #endif } - +*/ void cogl_enable_depth_test (gboolean setting) { @@ -345,7 +429,15 @@ cogl_enable_depth_test (gboolean setting) void cogl_color (const ClutterColor *color) { - glColor4ub (color->red, color->green, color->blue, color->alpha); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + glColor4ub (color->red, + color->green, + color->blue, + color->alpha); + + /* Store alpha for proper blending enables */ + ctx->color_alpha = color->alpha; } void @@ -354,198 +446,64 @@ cogl_clip_set (ClutterFixed x_offset, ClutterFixed width, ClutterFixed height) { - GE( glEnable (GL_STENCIL_TEST) ); + if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES)) + { + GLdouble eqn_left[4] = { 1.0, 0, 0, + -CLUTTER_FIXED_TO_FLOAT (x_offset) }; + GLdouble eqn_right[4] = { -1.0, 0, 0, + CLUTTER_FIXED_TO_FLOAT (x_offset + width) }; + GLdouble eqn_top[4] = { 0, 1.0, 0, -CLUTTER_FIXED_TO_FLOAT (y_offset) }; + GLdouble eqn_bottom[4] = { 0, -1.0, 0, CLUTTER_FIXED_TO_FLOAT + (y_offset + height) }; + + GE( glClipPlane (GL_CLIP_PLANE0, eqn_left) ); + GE( glClipPlane (GL_CLIP_PLANE1, eqn_right) ); + GE( glClipPlane (GL_CLIP_PLANE2, eqn_top) ); + GE( glClipPlane (GL_CLIP_PLANE3, eqn_bottom) ); + GE( glEnable (GL_CLIP_PLANE0) ); + GE( glEnable (GL_CLIP_PLANE1) ); + GE( glEnable (GL_CLIP_PLANE2) ); + GE( glEnable (GL_CLIP_PLANE3) ); + } + else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) + { + GE( glEnable (GL_STENCIL_TEST) ); - GE( glClearStencil (0.0f) ); - GE( glClear (GL_STENCIL_BUFFER_BIT) ); + GE( glClearStencil (0.0f) ); + GE( glClear (GL_STENCIL_BUFFER_BIT) ); - GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); - GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); + GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); + GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); - GE( glColor3f (1.0f, 1.0f, 1.0f) ); + GE( glColor3f (1.0f, 1.0f, 1.0f) ); - GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x_offset), - CLUTTER_FIXED_TO_FLOAT (y_offset), - CLUTTER_FIXED_TO_FLOAT (x_offset + width), - CLUTTER_FIXED_TO_FLOAT (y_offset + height)) ); + GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x_offset), + CLUTTER_FIXED_TO_FLOAT (y_offset), + CLUTTER_FIXED_TO_FLOAT (x_offset + width), + CLUTTER_FIXED_TO_FLOAT (y_offset + height)) ); - GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); - GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + } } void cogl_clip_unset (void) { - GE( glDisable (GL_STENCIL_TEST) ); -} - -gboolean -cogl_texture_can_size (COGLenum target, - COGLenum pixel_format, - COGLenum pixel_type, - int width, - int height) -{ -#ifdef GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB - if (target == CGL_TEXTURE_RECTANGLE_ARB) + if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES)) { - GLint max_size = 0; - - GE( glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &max_size) ); - - return (max_size && width <= max_size && height <= max_size); + GE( glDisable (GL_CLIP_PLANE3) ); + GE( glDisable (GL_CLIP_PLANE2) ); + GE( glDisable (GL_CLIP_PLANE1) ); + GE( glDisable (GL_CLIP_PLANE0) ); } - else /* Assumes CGL_TEXTURE_2D */ -#endif + else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) { - GLint new_width = 0; - - GE( glTexImage2D (GL_PROXY_TEXTURE_2D, 0, GL_RGBA, - width, height, 0 /* border */, - pixel_format, pixel_type, NULL) ); - - GE( glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, - GL_TEXTURE_WIDTH, &new_width) ); - - return new_width != 0; + GE( glDisable (GL_STENCIL_TEST) ); } } void -cogl_texture_quad (gint x1, - gint x2, - gint y1, - gint y2, - ClutterFixed tx1, - ClutterFixed ty1, - ClutterFixed tx2, - ClutterFixed ty2) -{ - gdouble txf1, tyf1, txf2, tyf2; - - txf1 = CLUTTER_FIXED_TO_DOUBLE (tx1); - tyf1 = CLUTTER_FIXED_TO_DOUBLE (ty1); - txf2 = CLUTTER_FIXED_TO_DOUBLE (tx2); - tyf2 = CLUTTER_FIXED_TO_DOUBLE (ty2); - - glBegin (GL_QUADS); - glTexCoord2f (txf2, tyf2); glVertex2i (x2, y2); - glTexCoord2f (txf1, tyf2); glVertex2i (x1, y2); - glTexCoord2f (txf1, tyf1); glVertex2i (x1, y1); - glTexCoord2f (txf2, tyf1); glVertex2i (x2, y1); - glEnd (); -} - -void -cogl_textures_create (guint num, COGLuint *textures) -{ - GE( glGenTextures (num, textures) ); -} - -void -cogl_textures_destroy (guint num, const COGLuint *textures) -{ - GE( glDeleteTextures (num, textures) ); -} - -void -cogl_texture_bind (COGLenum target, COGLuint texture) -{ - GE( glBindTexture (target, texture) ); -} - -void -cogl_texture_set_alignment (COGLenum target, - guint alignment, - guint row_length) -{ - GE( glPixelStorei (GL_UNPACK_ROW_LENGTH, row_length) ); - GE( glPixelStorei (GL_UNPACK_ALIGNMENT, alignment) ); -} - -void -cogl_texture_set_filters (COGLenum target, - COGLenum min_filter, - COGLenum max_filter) -{ - GE( glTexParameteri(target, GL_TEXTURE_MAG_FILTER, max_filter) ); - GE( glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filter) ); -} - -void -cogl_texture_set_wrap (COGLenum target, - COGLenum wrap_s, - COGLenum wrap_t) -{ - GE( glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap_s) ); - GE( glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap_s) ); -} - -void -cogl_texture_image_2d (COGLenum target, - COGLint internal_format, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels) -{ - GE( glTexImage2D (target, - 0, /* No mipmap support as yet */ - internal_format, - width, - height, - 0, /* 0 pixel border */ - format, - type, - pixels) ); -} - -void -cogl_texture_sub_image_2d (COGLenum target, - gint xoff, - gint yoff, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels) -{ - GE( glTexSubImage2D (target, - 0, - xoff, - yoff, - width, - height, - format, - type, - pixels)); -} - -void -cogl_rectangle (gint x, gint y, guint width, guint height) -{ - GE( glRecti (x, y, x + width, y + height) ); -} - -/* FIXME: Should use ClutterReal or Fixed */ -void -cogl_trapezoid (gint y1, - gint x11, - gint x21, - gint y2, - gint x12, - gint x22) -{ - GE( glBegin (GL_QUADS) ); - GE( glVertex2i (x11, y1) ); - GE( glVertex2i (x21, y1) ); - GE( glVertex2i (x22, y2) ); - GE( glVertex2i (x12, y2) ); - GE( glEnd () ); -} - -void cogl_alpha_func (COGLenum func, ClutterFixed ref) { @@ -649,13 +607,17 @@ cogl_setup_viewport (guint width, GE( glTranslatef (0.0f, -1.0 * height, 0.0f) ); } -ClutterFeatureFlags -cogl_get_features () +static void +_cogl_features_init () { + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + ClutterFeatureFlags flags = 0; const gchar *gl_extensions; + int max_clip_planes = 0; + int stencil_bits = 0; - flags = CLUTTER_FEATURE_TEXTURE_READ_PIXELS; + flags = COGL_FEATURE_TEXTURE_READ_PIXELS; gl_extensions = (const gchar*) glGetString (GL_EXTENSIONS); @@ -663,53 +625,194 @@ cogl_get_features () if (cogl_check_extension ("GL_ARB_texture_rectangle", gl_extensions) || cogl_check_extension ("GL_EXT_texture_rectangle", gl_extensions)) { - flags |= CLUTTER_FEATURE_TEXTURE_RECTANGLE; + flags |= COGL_FEATURE_TEXTURE_RECTANGLE; } #endif - + + if (cogl_check_extension ("GL_ARB_texture_non_power_of_two", gl_extensions)) + { + flags |= COGL_FEATURE_TEXTURE_NPOT; + } + #ifdef GL_YCBCR_MESA if (cogl_check_extension ("GL_MESA_ycbcr_texture", gl_extensions)) { - flags |= CLUTTER_FEATURE_TEXTURE_YUV; + flags |= COGL_FEATURE_TEXTURE_YUV; } #endif - if (cogl_check_extension ("GL_ARB_vertex_shader", gl_extensions) && + if (cogl_check_extension ("GL_ARB_shader_objects", gl_extensions) && + cogl_check_extension ("GL_ARB_vertex_shader", gl_extensions) && cogl_check_extension ("GL_ARB_fragment_shader", gl_extensions)) { - flags |= CLUTTER_FEATURE_SHADERS_GLSL; + ctx->pf_glCreateProgramObjectARB = + (PFNGLCREATEPROGRAMOBJECTARBPROC) + cogl_get_proc_address ("glCreateProgramObjectARB"); + + ctx->pf_glCreateShaderObjectARB = + (PFNGLCREATESHADEROBJECTARBPROC) + cogl_get_proc_address ("glCreateShaderObjectARB"); + + ctx->pf_glShaderSourceARB = + (PFNGLSHADERSOURCEARBPROC) + cogl_get_proc_address ("glShaderSourceARB"); + + ctx->pf_glCompileShaderARB = + (PFNGLCOMPILESHADERARBPROC) + cogl_get_proc_address ("glCompileShaderARB"); + + ctx->pf_glAttachObjectARB = + (PFNGLATTACHOBJECTARBPROC) + cogl_get_proc_address ("glAttachObjectARB"); + + ctx->pf_glLinkProgramARB = + (PFNGLLINKPROGRAMARBPROC) + cogl_get_proc_address ("glLinkProgramARB"); + + ctx->pf_glUseProgramObjectARB = + (PFNGLUSEPROGRAMOBJECTARBPROC) + cogl_get_proc_address ("glUseProgramObjectARB"); + + ctx->pf_glGetUniformLocationARB = + (PFNGLGETUNIFORMLOCATIONARBPROC) + cogl_get_proc_address ("glGetUniformLocationARB"); + + ctx->pf_glDeleteObjectARB = + (PFNGLDELETEOBJECTARBPROC) + cogl_get_proc_address ("glDeleteObjectARB"); + + ctx->pf_glGetInfoLogARB = + (PFNGLGETINFOLOGARBPROC) + cogl_get_proc_address ("glGetInfoLogARB"); + + ctx->pf_glGetObjectParameterivARB = + (PFNGLGETOBJECTPARAMETERIVARBPROC) + cogl_get_proc_address ("glGetObjectParameterivARB"); + + ctx->pf_glUniform1fARB = + (PFNGLUNIFORM1FARBPROC) + cogl_get_proc_address ("glUniform1fARB"); + + if (ctx->pf_glCreateProgramObjectARB && + ctx->pf_glCreateShaderObjectARB && + ctx->pf_glShaderSourceARB && + ctx->pf_glCompileShaderARB && + ctx->pf_glAttachObjectARB && + ctx->pf_glLinkProgramARB && + ctx->pf_glUseProgramObjectARB && + ctx->pf_glGetUniformLocationARB && + ctx->pf_glDeleteObjectARB && + ctx->pf_glGetInfoLogARB && + ctx->pf_glGetObjectParameterivARB && + ctx->pf_glUniform1fARB) + flags |= COGL_FEATURE_SHADERS_GLSL; } + if (cogl_check_extension ("GL_EXT_framebuffer_object", gl_extensions) || cogl_check_extension ("GL_ARB_framebuffer_object", gl_extensions)) + { + ctx->pf_glGenRenderbuffersEXT = + (PFNGLGENRENDERBUFFERSEXTPROC) + cogl_get_proc_address ("glGenRenderbuffersEXT"); + + ctx->pf_glBindRenderbufferEXT = + (PFNGLBINDRENDERBUFFEREXTPROC) + cogl_get_proc_address ("glBindRenderbufferEXT"); + + ctx->pf_glRenderbufferStorageEXT = + (PFNGLRENDERBUFFERSTORAGEEXTPROC) + cogl_get_proc_address ("glRenderbufferStorageEXT"); + + ctx->pf_glGenFramebuffersEXT = + (PFNGLGENFRAMEBUFFERSEXTPROC) + cogl_get_proc_address ("glGenFramebuffersEXT"); + + ctx->pf_glBindFramebufferEXT = + (PFNGLBINDFRAMEBUFFEREXTPROC) + cogl_get_proc_address ("glBindFramebufferEXT"); + + ctx->pf_glFramebufferTexture2DEXT = + (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) + cogl_get_proc_address ("glFramebufferTexture2DEXT"); + + ctx->pf_glFramebufferRenderbufferEXT = + (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) + cogl_get_proc_address ("glFramebufferRenderbufferEXT"); + + ctx->pf_glCheckFramebufferStatusEXT = + (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) + cogl_get_proc_address ("glCheckFramebufferStatusEXT"); + + ctx->pf_glDeleteFramebuffersEXT = + (PFNGLDELETEFRAMEBUFFERSEXTPROC) + cogl_get_proc_address ("glDeleteFramebuffersEXT"); + + if (ctx->pf_glGenRenderbuffersEXT && + ctx->pf_glBindRenderbufferEXT && + ctx->pf_glRenderbufferStorageEXT && + ctx->pf_glGenFramebuffersEXT && + ctx->pf_glBindFramebufferEXT && + ctx->pf_glFramebufferTexture2DEXT && + ctx->pf_glFramebufferRenderbufferEXT && + ctx->pf_glCheckFramebufferStatusEXT && + ctx->pf_glDeleteFramebuffersEXT) + flags |= COGL_FEATURE_OFFSCREEN; + } + + if (cogl_check_extension ("GL_EXT_framebuffer_blit", gl_extensions)) + { + ctx->pf_glBlitFramebufferEXT = + (PFNGLBLITFRAMEBUFFEREXTPROC) + cogl_get_proc_address ("glBlitFramebufferEXT"); + + if (ctx->pf_glBlitFramebufferEXT) + flags |= COGL_FEATURE_OFFSCREEN_BLIT; + } + + if (cogl_check_extension ("GL_EXT_framebuffer_multisample", gl_extensions)) { - _gen_framebuffers = - (GenFramebuffers) cogl_get_proc_address ("glGenFramebuffersEXT"); - - _bind_framebuffer = - (BindFramebuffer) cogl_get_proc_address ("glBindFramebufferEXT"); - - _framebuffer_texture_2d = - (FramebufferTexture2D) - cogl_get_proc_address ("glFramebufferTexture2DEXT"); - - _check_framebuffer_status = - (CheckFramebufferStatus) - cogl_get_proc_address ("glCheckFramebufferStatusEXT"); - - _delete_framebuffers = - (DeleteFramebuffers) - cogl_get_proc_address ("glDeleteFramebuffersEXT"); - - if (_gen_framebuffers - && _bind_framebuffer - && _framebuffer_texture_2d - && _check_framebuffer_status - && _delete_framebuffers) - flags |= CLUTTER_FEATURE_OFFSCREEN; + ctx->pf_glRenderbufferStorageMultisampleEXT = + (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) + cogl_get_proc_address ("glRenderbufferStorageMultisampleEXT"); + + if (ctx->pf_glRenderbufferStorageMultisampleEXT) + flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE; } - return flags; + GE( glGetIntegerv (GL_STENCIL_BITS, &stencil_bits) ); + if (stencil_bits > 0) + flags |= COGL_FEATURE_STENCIL_BUFFER; + + GE( glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); + if (max_clip_planes >= 4) + flags |= COGL_FEATURE_FOUR_CLIP_PLANES; + + /* Cache features */ + ctx->feature_flags = flags; + ctx->features_cached = TRUE; +} + +ClutterFeatureFlags +cogl_get_features () +{ + _COGL_GET_CONTEXT (ctx, 0); + + if (!ctx->features_cached) + _cogl_features_init (); + + return ctx->feature_flags; +} + +gboolean +cogl_features_available (CoglFeatureFlags features) +{ + _COGL_GET_CONTEXT (ctx, 0); + + if (!ctx->features_cached) + _cogl_features_init (); + + return (ctx->feature_flags & features) == features; } void @@ -835,217 +938,79 @@ cogl_fog_set (const ClutterColor *fog_color, glFogf (GL_FOG_END, CLUTTER_FIXED_TO_FLOAT (stop)); } -/* FBOs - offscreen */ - -COGLuint -cogl_offscreen_create (COGLuint target_texture) -{ -#ifdef GL_FRAMEBUFFER_EXT - COGLuint handle; - GLenum status; - - if (_gen_framebuffers == NULL - || _bind_framebuffer == NULL - || _framebuffer_texture_2d == NULL - || _check_framebuffer_status == NULL) - { - /* tmp warning - need error reporting */ - g_warning("Missing GL_FRAMEBUFFER_EXT API\n"); - return 0; - } - - _gen_framebuffers (1, &handle); - _bind_framebuffer (GL_FRAMEBUFFER_EXT, handle); - - _framebuffer_texture_2d (GL_FRAMEBUFFER_EXT, - GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_RECTANGLE_ARB, - target_texture, - 0); - - status = _check_framebuffer_status (GL_FRAMEBUFFER_EXT); - - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) - { - _delete_framebuffers (1, &handle); - return 0; - } - - _bind_framebuffer (GL_FRAMEBUFFER_EXT, 0); - - return handle; -#else - /* tmp warning - need error reporting */ - g_warning("No GL_FRAMEBUFFER_EXT\n"); - return 0; -#endif -} - -void -cogl_offscreen_destroy (COGLuint offscreen_handle) -{ - if (_delete_framebuffers) - _delete_framebuffers (1, &offscreen_handle); -} - -void -cogl_offscreen_redirect_start (COGLuint offscreen_handle, - gint width, - gint height) -{ - /* FIXME: silly we need to pass width / height to init viewport */ -#ifdef GL_FRAMEBUFFER_EXT - - if (_bind_framebuffer == NULL) - return; - - _bind_framebuffer (GL_FRAMEBUFFER_EXT, offscreen_handle); - - glViewport (0, 0, width, height); - - glMatrixMode (GL_PROJECTION); - glPushMatrix(); - glLoadIdentity (); - - glMatrixMode (GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity (); - - glTranslatef (-1.0f, -1.0f, 0.0f); - glScalef (2.0f / (float)width, 2.0f / (float)height, 1.0f); - - /* Clear the scene, appears needed on some backends - OSX */ - glClearColor (0.0, 0.0, 0.0, 0.0); - glClear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - -#endif -} - -void -cogl_offscreen_redirect_end (COGLuint offscreen_handle, - gint width, - gint height) -{ - /* FIXME: silly we need to pass width / height to reset to */ - if (_bind_framebuffer == NULL) - return; - -#ifdef GL_FRAMEBUFFER_EXT - glViewport (0, 0, width, height); - - glMatrixMode (GL_PROJECTION); - glPopMatrix(); - - glMatrixMode (GL_MODELVIEW); - glPopMatrix(); - - _bind_framebuffer (GL_FRAMEBUFFER_EXT, 0); -#endif -} - - /* Shader Magic follows */ -#ifdef __GNUC__ - -#define PROC(rettype, retval, procname, args...) \ - static rettype (*proc) (args) = NULL; \ - if (proc == NULL) \ - { \ - proc = (void*)cogl_get_proc_address (#procname);\ - if (!proc)\ - {\ - g_warning ("failed to lookup proc: %s", #procname);\ - return retval;\ - }\ - } -#else - -#define PROC(rettype, retval, procname, ...) \ - static rettype (*proc) (__VA_ARGS__) = NULL; \ - if (proc == NULL) \ - { \ - proc = (void*)cogl_get_proc_address (#procname);\ - if (!proc)\ - {\ - g_warning ("failed to lookup proc: %s", #procname);\ - return retval;\ - }\ - } - -#endif - COGLhandle cogl_create_program (void) { - PROC (GLhandleARB, 0, glCreateProgramObjectARB, void); - return proc (); + _COGL_GET_CONTEXT (ctx, 0); + return glCreateProgramObjectARB (); } COGLhandle cogl_create_shader (COGLenum shaderType) { - PROC (GLhandleARB, 0, glCreateShaderObjectARB, GLenum); - return proc (shaderType); + _COGL_GET_CONTEXT (ctx, 0); + return glCreateShaderObjectARB (shaderType); } void cogl_shader_source (COGLhandle shader, const gchar *source) { - PROC (GLvoid,, glShaderSourceARB, GLhandleARB, GLsizei, const GLcharARB **, const GLint *) - proc (shader, 1, &source, NULL); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glShaderSourceARB (shader, 1, &source, NULL); } void cogl_shader_compile (COGLhandle shader_handle) { - PROC (GLvoid,, glCompileShaderARB, GLhandleARB); - proc (shader_handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glCompileShaderARB (shader_handle); } void cogl_program_attach_shader (COGLhandle program_handle, COGLhandle shader_handle) { - PROC (GLvoid,, glAttachObjectARB, GLhandleARB, GLhandleARB); - proc (program_handle, shader_handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glAttachObjectARB (program_handle, shader_handle); } void cogl_program_link (COGLhandle program_handle) { - PROC (GLvoid,, glLinkProgramARB, GLhandleARB); - proc (program_handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glLinkProgramARB (program_handle); } void cogl_program_use (COGLhandle program_handle) { - PROC (GLvoid,, glUseProgramObjectARB, GLhandleARB); - proc (program_handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glUseProgramObjectARB (program_handle); } COGLint cogl_program_get_uniform_location (COGLhandle program_handle, const gchar *uniform_name) { - PROC (GLint,0, glGetUniformLocationARB, GLhandleARB, const GLcharARB *) - return proc (program_handle, uniform_name); + _COGL_GET_CONTEXT (ctx, 0); + return glGetUniformLocationARB (program_handle, uniform_name); } void cogl_program_destroy (COGLhandle handle) { - PROC (GLvoid,, glDeleteObjectARB, GLhandleARB); - proc (handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glDeleteObjectARB (handle); } void cogl_shader_destroy (COGLhandle handle) { - PROC (GLvoid,, glDeleteObjectARB, GLhandleARB); - proc (handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glDeleteObjectARB (handle); } void @@ -1054,8 +1019,8 @@ cogl_shader_get_info_log (COGLhandle handle, gchar *buffer) { COGLint len; - PROC (GLvoid,, glGetInfoLogARB, GLhandleARB, GLsizei, GLsizei *, GLcharARB *); - proc (handle, size-1, &len, buffer); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glGetInfoLogARB (handle, size-1, &len, buffer); buffer[len]='\0'; } @@ -1064,15 +1029,14 @@ cogl_shader_get_parameteriv (COGLhandle handle, COGLenum pname, COGLint *dest) { - PROC (GLvoid,, glGetObjectParameterivARB, GLhandleARB, GLenum, GLint*) - proc (handle, pname, dest); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glGetObjectParameterivARB (handle, pname, dest); } - void cogl_program_uniform_1f (COGLint uniform_no, gfloat value) { - PROC (GLvoid,, glUniform1fARB, GLint, GLfloat); - proc (uniform_no, value); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glUniform1fARB (uniform_no, value); } diff --git a/gles/Makefile.am b/gles/Makefile.am index 32dda4a6..c629e1ee 100644 --- a/gles/Makefile.am +++ b/gles/Makefile.am @@ -1,19 +1,36 @@ -libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/clutter -libclutterinclude_HEADERS = $(top_srcdir)/clutter/cogl/cogl.h \ - $(top_srcdir)/clutter/cogl/gles/cogl-defines.h +libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/cogl +libclutterinclude_HEADERS = $(top_builddir)/clutter/cogl/cogl.h \ + $(top_builddir)/clutter/cogl/cogl-defines-gles.h INCLUDES = \ - -I$(top_srcdir) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/clutter \ -I$(top_srcdir)/clutter/cogl \ - $(CLUTTER_CFLAGS) \ - $(CLUTTER_DEBUG_CFLAGS) \ + -I$(top_srcdir)/clutter/cogl/common \ + -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \ + -I$(top_builddir)/clutter \ + -I$(top_builddir)/clutter/cogl \ + $(CLUTTER_CFLAGS) \ + $(CLUTTER_DEBUG_CFLAGS) \ $(GCC_FLAGS) LDADD = $(CLUTTER_LIBS) noinst_LTLIBRARIES = libclutter-cogl.la -libclutter_cogl_la_SOURCES = \ - $(top_srcdir)/clutter/cogl/cogl.h \ - $(top_srcdir)/clutter/cogl/gl/cogl-defines.h \ - cogl.c +libclutter_cogl_la_SOURCES = \ + $(top_builddir)/clutter/cogl/cogl.h \ + $(top_builddir)/clutter/cogl/gles/cogl-defines.h \ + cogl-defines.h \ + cogl-internal.h \ + cogl-primitives.h \ + cogl-texture.h \ + cogl-fbo.h \ + cogl-context.h \ + cogl.c \ + cogl-primitives.c \ + cogl-texture.c \ + cogl-fbo.c \ + cogl-context.c + +libclutter_cogl_la_LIBADD = $(top_builddir)/clutter/cogl/common/libclutter-cogl-common.la diff --git a/gles/cogl-context.c b/gles/cogl-context.c new file mode 100644 index 00000000..0027e96d --- /dev/null +++ b/gles/cogl-context.c @@ -0,0 +1,96 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" + +#include <GLES/gl.h> +#include <string.h> + +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-context.h" + +static CoglContext *_context = NULL; + +gboolean +cogl_create_context () +{ + if (_context != NULL) + return FALSE; + + /* Allocate context memory */ + _context = (CoglContext*) g_malloc (sizeof (CoglContext)); + + /* Init default values */ + _context->feature_flags = 0; + _context->features_cached = FALSE; + + _context->enable_flags = 0; + _context->color_alpha = 255; + + _context->path_nodes = NULL; + _context->path_nodes_cap = 0; + _context->path_nodes_size = 0; + + _context->texture_handles = NULL; + _context->texture_vertices_size = 0; + _context->texture_vertices = NULL; + + _context->fbo_handles = NULL; + _context->draw_buffer = COGL_WINDOW_BUFFER; + + /* Init OpenGL state */ + glTexEnvx (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColorMask (TRUE, TRUE, TRUE, FALSE); + cogl_enable (0); + + return TRUE; +} + +void +cogl_destroy_context () +{ + if (_context == NULL) + return; + + if (_context->texture_vertices) + g_free (_context->texture_vertices); + + g_free (_context); +} + +CoglContext * +_cogl_context_get_default () +{ + /* Create if doesn't exists yet */ + if (_context == NULL) + cogl_create_context (); + + return _context; +} diff --git a/gles/cogl-context.h b/gles/cogl-context.h new file mode 100644 index 00000000..c5117a9f --- /dev/null +++ b/gles/cogl-context.h @@ -0,0 +1,78 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_CONTEXT_H +#define __COGL_CONTEXT_H + +#include "cogl-primitives.h" + +typedef struct +{ + GLfixed v[3]; + GLfixed t[2]; + GLfixed c[4]; +} CoglTextureGLVertex; + +typedef struct +{ + /* Features cache */ + CoglFeatureFlags feature_flags; + gboolean features_cached; + + /* Enable cache */ + gulong enable_flags; + guint8 color_alpha; + + /* Primitives */ + CoglFixedVec2 path_start; + CoglFixedVec2 path_pen; + CoglFixedVec2 *path_nodes; + guint path_nodes_cap; + guint path_nodes_size; + CoglFixedVec2 path_nodes_min; + CoglFixedVec2 path_nodes_max; + + /* Textures */ + GArray *texture_handles; + CoglTextureGLVertex *texture_vertices; + gulong texture_vertices_size; + + /* Framebuffer objects */ + GArray *fbo_handles; + CoglBufferTarget draw_buffer; + +} CoglContext; + +CoglContext * +_cogl_context_get_default (); + +/* Obtains the context and returns retval if NULL */ +#define _COGL_GET_CONTEXT(ctxvar, retval) \ +CoglContext *ctxvar = _cogl_context_get_default (); \ +if (ctxvar == NULL) return retval; + +#define NO_RETVAL + +#endif /* __COGL_CONTEXT_H */ diff --git a/gles/cogl-defines.h b/gles/cogl-defines.h.in index 83f2ae15..b304680f 100644 --- a/gles/cogl-defines.h +++ b/gles/cogl-defines.h.in @@ -444,18 +444,6 @@ typedef GLuint COGLhandle; /* extras */ -#define CGL_TEXTURE_2D GL_TEXTURE_2D -#define CGL_ARGB GL_ARGB - -/* FIXME: There is no BGR support in GLES - so with below BGR textures are - * borked. Will likely need a feature flag and some coversion.. - */ -#define CGL_BGR GL_RGB -#define CGL_BGRA GL_RGBA - -/* Its unlikely we support this */ -#define CGL_TEXTURE_RECTANGLE_ARB 0 - /* YUV textures also unsupported */ #define CGL_YCBCR_MESA 0 #define CGL_UNSIGNED_SHORT_8_8_REV_MESA 0 diff --git a/gles/cogl-fbo.c b/gles/cogl-fbo.c new file mode 100644 index 00000000..e8f186b3 --- /dev/null +++ b/gles/cogl-fbo.c @@ -0,0 +1,188 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-texture.h" +#include "cogl-fbo.h" +#include "cogl-context.h" + +/* Expecting EXT functions not to be defined - redirect to pointers in context */ +#define glGenRenderbuffersEXT ctx->pf_glGenRenderbuffersEXT +#define glBindRenderbufferEXT ctx->pf_glBindRenderbufferEXT +#define glRenderbufferStorageEXT ctx->pf_glRenderbufferStorageEXT +#define glGenFramebuffersEXT ctx->pf_glGenFramebuffersEXT +#define glBindFramebufferEXT ctx->pf_glBindFramebufferEXT +#define glFramebufferTexture2DEXT ctx->pf_glFramebufferTexture2DEXT +#define glFramebufferRenderbufferEXT ctx->pf_glFramebufferRenderbufferEXT +#define glCheckFramebufferStatusEXT ctx->pf_glCheckFramebufferStatusEXT +#define glDeleteFramebuffersEXT ctx->pf_glDeleteFramebuffersEXT +#define glBlitFramebufferEXT ctx->pf_glBlitFramebufferEXT +#define glRenderbufferStorageMultisampleEXT ctx->pf_glRenderbufferStorageMultisampleEXT + +static gint +_cogl_fbo_handle_find (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, -1); + + gint i; + + if (ctx->fbo_handles == NULL) + return -1; + + for (i=0; i < ctx->fbo_handles->len; ++i) + if (g_array_index (ctx->fbo_handles, CoglHandle, i) == handle) + return i; + + return -1; +} +/* +static CoglHandle +_cogl_fbo_handle_new (CoglFbo *fbo) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + CoglHandle handle = (CoglHandle)fbo; + + if (ctx->fbo_handles == NULL) + ctx->fbo_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle)); + + g_array_append_val (ctx->fbo_handles, handle); + + return handle; +} + +static void +_cogl_fbo_handle_release (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + gint i; + + if ( (i = _cogl_fbo_handle_find (handle)) == -1) + return; + + g_array_remove_index_fast (ctx->fbo_handles, i); +} + +static CoglFbo* +_cogl_fbo_pointer_from_handle (CoglHandle handle) +{ + return (CoglFbo*) handle; +}*/ + +gboolean +cogl_is_offscreen_buffer (CoglHandle handle) +{ + if (handle == COGL_INVALID_HANDLE) + return FALSE; + + return _cogl_fbo_handle_find (handle) >= 0; +} + +CoglHandle +cogl_offscreen_new_to_texture (CoglHandle texhandle) +{ + return COGL_INVALID_HANDLE; +} + +CoglHandle +cogl_offscreen_new_multisample () +{ + return COGL_INVALID_HANDLE; +} + +CoglHandle +cogl_offscreen_ref (CoglHandle handle) +{ + return COGL_INVALID_HANDLE; +} + +void +cogl_offscreen_unref (CoglHandle handle) +{ +} + +void +cogl_offscreen_blit_region (CoglHandle src_buffer, + CoglHandle dst_buffer, + int src_x, + int src_y, + int src_w, + int src_h, + int dst_x, + int dst_y, + int dst_w, + int dst_h) +{ +} + +void +cogl_offscreen_blit (CoglHandle src_buffer, + CoglHandle dst_buffer) +{ +} + +void +cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + switch (target) + { + case COGL_OFFSCREEN_BUFFER: + + /* Not supported */ + return; + + case COGL_WINDOW_BUFFER: + + /* Draw to RGB channels */ + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE) ); + + break; + + case COGL_MASK_BUFFER: + + /* Draw only to ALPHA channel */ + GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE) ); + + break; + case COGL_WINDOW_BUFFER & COGL_MASK_BUFFER: + + /* Draw to all channels */ + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + + break; + } + + /* Store new target */ + ctx->draw_buffer = target; +} diff --git a/gles/cogl-fbo.h b/gles/cogl-fbo.h new file mode 100644 index 00000000..f2dd5298 --- /dev/null +++ b/gles/cogl-fbo.h @@ -0,0 +1,38 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_FBO_H +#define __COGL_FBO_H + +typedef struct +{ + guint ref_count; + int width; + int height; + GLuint gl_handle; + +} CoglFbo; + +#endif /* __COGL_FBO_H */ diff --git a/gles/cogl-internal.h b/gles/cogl-internal.h new file mode 100644 index 00000000..69baa4a9 --- /dev/null +++ b/gles/cogl-internal.h @@ -0,0 +1,63 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_INTERNAL_H +#define __COGL_INTERNAL_H + +#define COGL_DEBUG 0 + +#if COGL_DEBUG +#define GE(x...) { \ + GLenum err; \ + (x); \ + fprintf(stderr, "%s\n", #x); \ + while ((err = glGetError()) != GL_NO_ERROR) { \ + fprintf(stderr, "glError: %s caught at %s:%u\n", \ + (char *)error_string(err), \ + __FILE__, __LINE__); \ + } \ +} +#else +#define GE(x) (x); +#endif + +#define COGL_ENABLE_BLEND (1<<1) +#define COGL_ENABLE_TEXTURE_2D (1<<2) +#define COGL_ENABLE_ALPHA_TEST (1<<3) +#define COGL_ENABLE_TEXTURE_RECT (1<<4) +#define COGL_ENABLE_VERTEX_ARRAY (1<<5) +#define COGL_ENABLE_TEXCOORD_ARRAY (1<<6) +#define COGL_ENABLE_COLOR_ARRAY (1<<7) + +gint +_cogl_get_format_bpp (CoglPixelFormat format); + +void +cogl_enable (gulong flags); + +gulong +cogl_get_enable (); + +#endif /* __COGL_INTERNAL_H */ diff --git a/gles/cogl-primitives.c b/gles/cogl-primitives.c new file mode 100644 index 00000000..1bba58f5 --- /dev/null +++ b/gles/cogl-primitives.c @@ -0,0 +1,1112 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-context.h" + +#include <string.h> +#include <gmodule.h> + +#define _COGL_MAX_BEZ_RECURSE_DEPTH 16 + +/** + * SECTION:cogl-primitives + * @short_description: Functions that draw various primitive shapes and + * allow for construction of more complex paths. + * + * There are three levels on which drawing with cogl can be used. The + * highest level functions construct various simple primitive shapes + * to be either filled or stroked. Using a lower-level set of functions + * more complex and arbitrary paths can be constructed by concatenating + * straight line, bezier curve and arc segments. Additionally there + * are utility functions that draw the most common primitives - rectangles + * and trapezoids - in a maximaly optimized fashion. + * + * When constructing arbitrary paths, the current pen location is + * initialized using the move_to command. The subsequent path segments + * implicitly use the last pen location as their first vertex and move + * the pen location to the last vertex they produce at the end. Also + * there are special versions of functions that allow specifying the + * vertices of the path segments relative to the last pen location + * rather then in the absolute coordinates. + */ + +/** + * cogl_fast_fill_rectangle: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * + * Fills a rectangle at the given coordinates with the current + * drawing color in a highly optimizied fashion. + **/ +void +cogl_fast_fill_rectangle (gint x, + gint y, + guint width, + guint height) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* 32-bit integers are not supported as coord types + in GLES . Fixed type has got 16 bits left of the + point which is equal to short anyway. */ + + GLshort rect_verts[8] = { + (GLshort) x, (GLshort) y, + (GLshort) (x + width), (GLshort) y, + (GLshort) x, (GLshort) (y + height), + (GLshort) (x + width), (GLshort) (y + height) + }; + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_SHORT, 0, rect_verts) ); + GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); +} + +/** + * cogl_fast_fill_rectanglex: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * + * A fixed-point version of cogl_fast_fill_rectangle. + **/ +void +cogl_fast_fill_rectanglex (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + GLfixed rect_verts[8] = { + x, y, + x + width, y, + x, y + height, + x + width, y + height + }; + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FIXED, 0, rect_verts) ); + GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); +} + +/** + * cogl_fast_fill_trapezoid: + * @y1: Y coordinate of the top two vertices. + * @x11: X coordinate of the top-left vertex. + * @x21: X coordinate of the top-right vertex. + * @y2: Y coordinate of the bottom two vertices. + * @x12: X coordinate of the bottom-left vertex. + * @x22: X coordinate of the bottom-right vertex. + * + * Fills a trapezoid at the given coordinates with the current + * drawing color in a highly optimized fashion. + **/ +void +cogl_fast_fill_trapezoid (gint y1, + gint x11, + gint x21, + gint y2, + gint x12, + gint x22) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + GLshort trap_verts[8] = { + (GLshort) x11, (GLshort) y1, + (GLshort) x21, (GLshort) y1, + (GLshort) x12, (GLshort) y2, + (GLshort) x22, (GLshort) y2 + }; + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_SHORT, 0, trap_verts) ); + GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); +} + +/** + * cogl_fast_fill_trapezoidx: + * @y1: Y coordinate of the top two vertices. + * @x11: X coordinate of the top-left vertex. + * @x21: X coordinate of the top-right vertex. + * @y2: Y coordinate of the bottom two vertices. + * @x12: X coordinate of the bottom-left vertex. + * @x22: X coordinate of the bottom-right vertex. + * + * A fixed-point version of cogl_fast_fill_trapezoid. + **/ +void +cogl_fast_fill_trapezoidx (ClutterFixed y1, + ClutterFixed x11, + ClutterFixed x21, + ClutterFixed y2, + ClutterFixed x12, + ClutterFixed x22) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + GLfixed trap_verts[8] = { + x11, y1, + x21, y1, + x12, y2, + x22, y2 + }; + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FIXED, 0, trap_verts) ); + GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); +} + +static void +_cogl_path_clear_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes) + g_free (ctx->path_nodes); + + ctx->path_nodes = (CoglFixedVec2*) g_malloc (2 * sizeof(CoglFixedVec2)); + ctx->path_nodes_size = 0; + ctx->path_nodes_cap = 2; +} + +static void +_cogl_path_add_node (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglFixedVec2 *new_nodes = NULL; + + if (ctx->path_nodes_size == ctx->path_nodes_cap) + { + new_nodes = g_realloc (ctx->path_nodes, + 2 * ctx->path_nodes_cap + * sizeof (CoglFixedVec2)); + + if (new_nodes == NULL) return; + + ctx->path_nodes = new_nodes; + ctx->path_nodes_cap *= 2; + } + + ctx->path_nodes [ctx->path_nodes_size] .x = x; + ctx->path_nodes [ctx->path_nodes_size] .y = y; + ctx->path_nodes_size++; + + if (ctx->path_nodes_size == 1) + { + ctx->path_nodes_min.x = ctx->path_nodes_max.x = x; + ctx->path_nodes_min.y = ctx->path_nodes_max.y = y; + } + else + { + if (x < ctx->path_nodes_min.x) ctx->path_nodes_min.x = x; + if (x > ctx->path_nodes_max.x) ctx->path_nodes_max.x = x; + if (y < ctx->path_nodes_min.y) ctx->path_nodes_min.y = y; + if (y > ctx->path_nodes_max.y) ctx->path_nodes_max.y = y; + } +} + +static void +_cogl_path_stroke_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FIXED, 0, ctx->path_nodes) ); + GE( glDrawArrays (GL_LINE_STRIP, 0, ctx->path_nodes_size) ); +} + +static void +_cogl_path_fill_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + guint bounds_x; + guint bounds_y; + guint bounds_w; + guint bounds_h; + + GE( glClear (GL_STENCIL_BUFFER_BIT) ); + + GE( glEnable (GL_STENCIL_TEST) ); + GE( glStencilFunc (GL_ALWAYS, 0x0, 0x0) ); + GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); + GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) ); + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FIXED, 0, ctx->path_nodes) ); + GE( glDrawArrays (GL_TRIANGLE_FAN, 0, ctx->path_nodes_size) ); + + GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) ); + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + + bounds_x = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.x); + bounds_y = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.y); + bounds_w = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.x - ctx->path_nodes_min.x); + bounds_h = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.y - ctx->path_nodes_min.y); + + cogl_fast_fill_rectangle (bounds_x, bounds_y, bounds_w, bounds_h); + + GE( glDisable (GL_STENCIL_TEST) ); +} + +/** + * cogl_fill: + * + * Fills the constructed shape using the current drawing color. + **/ +void +cogl_fill () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes_size == 0) + return; + + _cogl_path_fill_nodes(); +} + +/** + * cogl_stroke: + * + * Strokes the constructed shape using the current drawing color + * and a width of 1 pixel (regardless of the current transformation + * matrix). + **/ +void +cogl_stroke () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes_size == 0) + return; + + _cogl_path_stroke_nodes(); +} + +/** + * cogl_path_move_to: + * @x: X coordinate of the pen location to move to. + * @y: Y coordinate of the pen location to move to. + * + * Clears the previously constructed shape and begins a new path + * contour by moving the pen to the given coordinates. + **/ +void +cogl_path_move_to (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* FIXME: handle multiple contours maybe? */ + + _cogl_path_clear_nodes (); + _cogl_path_add_node (x, y); + + ctx->path_start.x = x; + ctx->path_start.y = y; + + ctx->path_pen = ctx->path_start; +} + +/** + * cogl_path_move_to_rel: + * @x: X offset from the current pen location to move the pen to. + * @y: Y offset from the current pen location to move the pen to. + * + * Clears the previously constructed shape and begins a new path + * contour by moving the pen to the given coordinates relative + * to the current pen location. + **/ +void +cogl_path_move_to_rel (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_move_to (ctx->path_pen.x + x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_line_to: + * @x: X coordinate of the end line vertex + * @y: Y coordinate of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates. + **/ +void +cogl_path_line_to (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_path_add_node (x, y); + + ctx->path_pen.x = x; + ctx->path_pen.y = y; +} + +/** + * cogl_path_line_to: + * @x: X offset from the current pen location of the end line vertex + * @y: Y offset from the current pen location of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates relative to the current pen location. + **/ +void +cogl_path_line_to_rel (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x + x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_h_line_to: + * @x: X coordinate of the end line vertex + * + * Adds a straight horizontal line segment to the current path that + * ends at the given X coordinate and current pen Y coordinate. + **/ +void +cogl_path_h_line_to (ClutterFixed x) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (x, + ctx->path_pen.y); +} + +/** + * cogl_path_v_line_to: + * @y: Y coordinate of the end line vertex + * + * Adds a stright vertical line segment to the current path that ends + * at the current pen X coordinate and the given Y coordinate. + **/ +void +cogl_path_v_line_to (ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x, + y); +} + +/** + * cogl_path_h_line_to_rel: + * @x: X offset from the current pen location of the end line vertex + * + * Adds a straight horizontal line segment to the current path that + * ends at the given X coordinate relative to the current pen location + * and current pen Y coordinate. + **/ +void +cogl_path_h_line_to_rel (ClutterFixed x) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x + x, + ctx->path_pen.y); +} + +/** + * cogl_path_v_line_to_rel: + * @y: Y offset from the current pen location of the end line vertex + * + * Adds a stright vertical line segment to the current path that ends + * at the current pen X coordinate and the given Y coordinate relative + * to the current pen location. + **/ +void +cogl_path_v_line_to_rel (ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_close: + * + * Closes the path being constructed by adding a straight line segment + * to it that ends at the first vertex of the path. + **/ +void +cogl_path_close () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_path_add_node (ctx->path_start.x, ctx->path_start.y); + ctx->path_pen = ctx->path_start; +} + +/** + * cogl_line: + * @x1: X coordinate of the start line vertex + * @y1: Y coordinate of the start line vertex + * @x2: X coordinate of the end line vertex + * @y2: Y coordinate of the end line vertex + * + * Clears the previously constructed shape and constructs a straight + * line shape start and ending at the given coordinates. + **/ +void +cogl_line (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + cogl_path_move_to (x1, y1); + cogl_path_line_to (x2, y2); +} + +/** + * cogl_polyline: + * @coords: A pointer to the first element of an array of fixed-point + * values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Clears the previously constructed shape and constructs a series of straight + * line segments, starting from the first given vertex coordinate. Each + * subsequent segment stars where the previous one ended and ends at the next + * given vertex coordinate. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. (num_points - 1) segments will + * be constructed. + **/ +void +cogl_polyline (ClutterFixed *coords, + gint num_points) +{ + gint c = 0; + + cogl_path_move_to(coords[0], coords[1]); + + for (c = 1; c < num_points; ++c) + cogl_path_line_to(coords[2*c], coords[2*c+1]); +} + +/** + * cogl_polygon: + * @coords: A pointer to the first element of an array of fixed-point + * values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Clears the previously constructed shape and constructs a polygonal + * shape of the given number of vertices. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. + **/ +void +cogl_polygon (ClutterFixed *coords, + gint num_points) +{ + cogl_polyline (coords, num_points); + cogl_path_close (); +} + +/** + * cogl_rectangle: + * @x: X coordinate of the top-left corner. + * @y: Y coordinate of the top-left corner. + * @width: Rectangle width. + * @height: Rectangle height. + * + * Clears the previously constructed shape and constructs a rectangular + * shape at the given coordinates. + **/ +void +cogl_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height) +{ + cogl_path_move_to (x, y); + cogl_path_line_to (x + width, y); + cogl_path_line_to (x + width, y + height); + cogl_path_line_to (x, y + height); + cogl_path_close (); +} + +static void +_cogl_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step, + guint move_first) +{ + ClutterAngle a = 0x0; + ClutterAngle temp = 0x0; + ClutterFixed cosa = 0x0; + ClutterFixed sina = 0x0; + ClutterFixed px = 0x0; + ClutterFixed py = 0x0; + + /* Fix invalid angles */ + + if (angle_1 == angle_2 || angle_step == 0x0) + return; + + if (angle_step < 0x0) + angle_step = -angle_step; + + if (angle_2 < angle_1) + { + temp = angle_1; + angle_1 = angle_2; + angle_2 = temp; + } + + /* Walk the arc by given step */ + + for (a = angle_1; a < angle_2; a += angle_step) + { + cosa = clutter_cosi (a); + sina = clutter_sini (a); + + px = center_x + CFX_MUL (cosa, radius_x); + py = center_y + CFX_MUL (sina, radius_y); + + if (a == angle_1 && move_first) + cogl_path_move_to (px, py); + else + cogl_path_line_to (px, py); + } +} + +/** + * cogl_path_arc: + * @center_x: X coordinate of the elliptical arc center + * @center_y: Y coordinate of the elliptical arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Adds an elliptical arc segment to the current path. A straight line + * segment will link the current pen location with the first vertex + * of the arc. + **/ +void +cogl_path_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _cogl_arc (center_x, center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 0 /* no move */); +} + +/** + * cogl_path_arc_rel: + * @center_x: X offset from the current pen location of the elliptical + * arc center + * @center_y: Y offset from the current pen location of the elliptical + * arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Adds an elliptical arc segment to the current path. A straight line + * segment will link the current pen location with the first vertex + * of the arc. + **/ +void +cogl_path_arc_rel (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_arc (ctx->path_pen.x + center_x, + ctx->path_pen.y + center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 0 /* no move */); +} + +/** + * cogl_arc: + * @center_x: X coordinate of the elliptical arc center + * @center_y: Y coordinate of the elliptical arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Clears the previously constructed shape and constructs and elliptical arc + * shape. + **/ +void +cogl_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _cogl_arc (center_x, center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 1 /* move first */); +} + +/** + * cogl_ellipse: + * @center_x: X coordinate of the ellipse center + * @center_y: Y coordinate of the ellipse center + * @radius_x: X radius of the ellipse + * @radius_y: Y radius of the ellipse + * @angle_step: Angle increment resolution for subdivision + * + * Clears the previously constructed shape and constructs an ellipse + * shape. + **/ +void +cogl_ellipse (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_step) +{ + + /* FIXME: if shows to be slow might be optimized + * by mirroring just a quarter of it */ + + _cogl_arc (center_x, center_y, + radius_x, radius_y, + 0, CLUTTER_ANGLE_FROM_DEG(360), + angle_step, 1 /* move first */); + + cogl_path_close(); +} + +/** + * cogl_round_rectangle: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * @radius: Radius of the corner arcs. + * @arc_step: Angle increment resolution for subdivision of + * the corner arcs. + * + * Clears the previously constructed shape and constructs a rectangular + * shape with rounded corners. + **/ +void +cogl_round_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height, + ClutterFixed radius, + ClutterAngle arc_step) +{ + ClutterFixed inner_width = width - (radius << 1); + ClutterFixed inner_height = height - (radius << 1); + + cogl_path_move_to (x, y + radius); + cogl_path_arc_rel (radius, 0, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (180), + CLUTTER_ANGLE_FROM_DEG (270), + arc_step); + + cogl_path_h_line_to_rel (inner_width); + cogl_path_arc_rel (0, radius, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (-90), + CLUTTER_ANGLE_FROM_DEG (0), + arc_step); + + cogl_path_v_line_to_rel (inner_height); + cogl_path_arc_rel (-radius, 0, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (0), + CLUTTER_ANGLE_FROM_DEG (90), + arc_step); + + cogl_path_h_line_to_rel (-inner_width); + cogl_path_arc_rel (0, -radius, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (90), + CLUTTER_ANGLE_FROM_DEG (180), + arc_step); + + cogl_path_close (); +} + +static void +_cogl_path_bezier2_sub (CoglBezQuad *quad) +{ + CoglBezQuad quads[_COGL_MAX_BEZ_RECURSE_DEPTH]; + CoglBezQuad *qleft; + CoglBezQuad *qright; + CoglBezQuad *q; + CoglFixedVec2 mid; + CoglFixedVec2 dif; + CoglFixedVec2 c1; + CoglFixedVec2 c2; + CoglFixedVec2 c3; + gint qindex; + + /* Put first curve on stack */ + quads[0] = *quad; + qindex = 0; + + /* While stack is not empty */ + while (qindex >= 0) + { + + q = &quads[qindex]; + + /* Calculate distance of control point from its + * counterpart on the line between end points */ + mid.x = ((q->p1.x + q->p3.x) >> 1); + mid.y = ((q->p1.y + q->p3.y) >> 1); + dif.x = (q->p2.x - mid.x); + dif.y = (q->p2.y - mid.y); + if (dif.x < 0) dif.x = -dif.x; + if (dif.y < 0) dif.y = -dif.y; + + /* Cancel if the curve is flat enough */ + if (dif.x + dif.y <= CFX_ONE + || qindex == _COGL_MAX_BEZ_RECURSE_DEPTH - 1) + { + /* Add subdivision point (skip last) */ + if (qindex == 0) return; + _cogl_path_add_node (q->p3.x, q->p3.y); + --qindex; continue; + } + + /* Left recursion goes on top of stack! */ + qright = q; qleft = &quads[++qindex]; + + /* Subdivide into 2 sub-curves */ + c1.x = ((q->p1.x + q->p2.x) >> 1); + c1.y = ((q->p1.y + q->p2.y) >> 1); + c3.x = ((q->p2.x + q->p3.x) >> 1); + c3.y = ((q->p2.y + q->p3.y) >> 1); + c2.x = ((c1.x + c3.x) >> 1); + c2.y = ((c1.y + c3.y) >> 1); + + /* Add left recursion onto stack */ + qleft->p1 = q->p1; + qleft->p2 = c1; + qleft->p3 = c2; + + /* Add right recursion onto stack */ + qright->p1 = c2; + qright->p2 = c3; + qright->p3 = q->p3; + } +} + +static void +_cogl_path_bezier3_sub (CoglBezCubic *cubic) +{ + CoglBezCubic cubics[_COGL_MAX_BEZ_RECURSE_DEPTH]; + CoglBezCubic *cleft; + CoglBezCubic *cright; + CoglBezCubic *c; + CoglFixedVec2 dif1; + CoglFixedVec2 dif2; + CoglFixedVec2 mm; + CoglFixedVec2 c1; + CoglFixedVec2 c2; + CoglFixedVec2 c3; + CoglFixedVec2 c4; + CoglFixedVec2 c5; + gint cindex; + + /* Put first curve on stack */ + cubics[0] = *cubic; + cindex = 0; + + while (cindex >= 0) + { + c = &cubics[cindex]; + +#define CFX_MUL2(x) ((x) << 1) +#define CFX_MUL3(x) (((x) << 1) + (x)) +#define CFX_SQ(x) CFX_MUL (x, x) + + /* Calculate distance of control points from their + * counterparts on the line between end points */ + dif1.x = CFX_MUL3 (c->p2.x) - CFX_MUL2 (c->p1.x) - c->p4.x; + dif1.y = CFX_MUL3 (c->p2.y) - CFX_MUL2 (c->p1.y) - c->p4.y; + dif2.x = CFX_MUL3 (c->p3.x) - CFX_MUL2 (c->p4.x) - c->p1.x; + dif2.y = CFX_MUL3 (c->p3.y) - CFX_MUL2 (c->p4.y) - c->p1.y; + if (dif1.x < 0) dif1.x = -dif1.x; + if (dif1.y < 0) dif1.y = -dif1.y; + if (dif2.x < 0) dif2.x = -dif2.x; + if (dif2.y < 0) dif2.y = -dif2.y; + +#undef CFX_MUL2 +#undef CFX_MUL3 +#undef CFX_SQ + + /* Pick the greatest of two distances */ + if (dif1.x < dif2.x) dif1.x = dif2.x; + if (dif1.y < dif2.y) dif1.y = dif2.y; + + /* Cancel if the curve is flat enough */ + if (dif1.x + dif1.y <= CFX_ONE + || cindex == _COGL_MAX_BEZ_RECURSE_DEPTH-1) + { + /* Add subdivision point (skip last) */ + if (cindex == 0) return; + _cogl_path_add_node (c->p4.x, c->p4.y); + --cindex; continue; + } + + /* Left recursion goes on top of stack! */ + cright = c; cleft = &cubics[++cindex]; + + /* Subdivide into 2 sub-curves */ + c1.x = ((c->p1.x + c->p2.x) >> 1); + c1.y = ((c->p1.y + c->p2.y) >> 1); + mm.x = ((c->p2.x + c->p3.x) >> 1); + mm.y = ((c->p2.y + c->p3.y) >> 1); + c5.x = ((c->p3.x + c->p4.x) >> 1); + c5.y = ((c->p3.y + c->p4.y) >> 1); + + c2.x = ((c1.x + mm.x) >> 1); + c2.y = ((c1.y + mm.y) >> 1); + c4.x = ((mm.x + c5.x) >> 1); + c4.y = ((mm.y + c5.y) >> 1); + + c3.x = ((c2.x + c4.x) >> 1); + c3.y = ((c2.y + c4.y) >> 1); + + /* Add left recursion to stack */ + cleft->p1 = c->p1; + cleft->p2 = c1; + cleft->p3 = c2; + cleft->p4 = c3; + + /* Add right recursion to stack */ + cright->p1 = c3; + cright->p2 = c4; + cright->p3 = c5; + cright->p4 = c->p4; + } +} + +/** + * cogl_path_bezier2_to: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * + * Adds a quadratic bezier curve segment to the current path with the given + * second and third control points and using current pen location as the + * first control point. + **/ +void +cogl_path_bezier2_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglBezQuad quad; + + /* Prepare quadratic curve */ + quad.p1 = ctx->path_pen; + quad.p2.x = x1; + quad.p2.y = y1; + quad.p3.x = x2; + quad.p3.y = y2; + + /* Run subdivision */ + _cogl_path_bezier2_sub (&quad); + + /* Add last point */ + _cogl_path_add_node (quad.p3.x, quad.p3.y); + ctx->path_pen = quad.p3; +} + +/** + * cogl_path_bezier3_to: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * @x3: X coordinate of the fourth bezier control point + * @y3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. + **/ +void +cogl_path_bezier3_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglBezCubic cubic; + + /* Prepare cubic curve */ + cubic.p1 = ctx->path_pen; + cubic.p2.x = x1; + cubic.p2.y = y1; + cubic.p3.x = x2; + cubic.p3.y = y2; + cubic.p4.x = x3; + cubic.p4.y = y3; + + /* Run subdivision */ + _cogl_path_bezier3_sub (&cubic); + + /* Add last point */ + _cogl_path_add_node (cubic.p4.x, cubic.p4.y); + ctx->path_pen = cubic.p4; +} + +/** + * cogl_path_bezier2_to_rel: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * + * Adds a quadratic bezier curve segment to the current path with the given + * second and third control points and using current pen location as the + * first control point. The given coordinates are relative to the current + * pen location. + **/ +void +cogl_path_bezier2_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_bezier2_to (ctx->path_pen.x + x1, + ctx->path_pen.y + y2, + ctx->path_pen.x + x2, + ctx->path_pen.y + y2); +} + +/** + * cogl_path_bezier3_to_rel: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * @x3: X coordinate of the fourth bezier control point + * @y3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. The given coordinates are relative to the + * current pen location. + **/ +void +cogl_path_bezier3_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_bezier3_to (ctx->path_pen.x + x1, + ctx->path_pen.y + y2, + ctx->path_pen.x + x2, + ctx->path_pen.y + y2, + ctx->path_pen.x + x3, + ctx->path_pen.y + y3); +} diff --git a/gles/cogl-primitives.h b/gles/cogl-primitives.h new file mode 100644 index 00000000..4c96f409 --- /dev/null +++ b/gles/cogl-primitives.h @@ -0,0 +1,54 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_PRIMITIVES_H +#define __COGL_PRIMITIVES_H + +typedef struct _CoglFixedVec2 CoglFixedVec2; +typedef struct _CoglBezQuad CoglBezQuad; +typedef struct _CoglBezCubic CoglBezCubic; + +struct _CoglFixedVec2 +{ + ClutterFixed x; + ClutterFixed y; +}; + +struct _CoglBezQuad +{ + CoglFixedVec2 p1; + CoglFixedVec2 p2; + CoglFixedVec2 p3; +}; + +struct _CoglBezCubic +{ + CoglFixedVec2 p1; + CoglFixedVec2 p2; + CoglFixedVec2 p3; + CoglFixedVec2 p4; +}; + +#endif /* __COGL_PRIMITIVES_H */ diff --git a/gles/cogl-texture.c b/gles/cogl-texture.c new file mode 100644 index 00000000..2eae2c9b --- /dev/null +++ b/gles/cogl-texture.c @@ -0,0 +1,2185 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-bitmap.h" +#include "cogl-texture.h" +#include "cogl-context.h" + +#include <string.h> +#include <stdlib.h> +/* +#define COGL_DEBUG 1 + +#define GE(x) \ +{ \ + glGetError(); x; \ + GLuint err = glGetError(); \ + if (err != 0) \ + printf("err: 0x%x\n", err); \ +} */ + +struct _CoglSpanIter +{ + gint index; + GArray *array; + CoglTexSliceSpan *span; + ClutterFixed pos; + ClutterFixed next_pos; + ClutterFixed origin; + ClutterFixed cover_start; + ClutterFixed cover_end; + ClutterFixed intersect_start; + ClutterFixed intersect_end; + ClutterFixed intersect_start_local; + ClutterFixed intersect_end_local; + gboolean intersects; +}; + +/* + * _cogl_texture_handle_find: + * @handle: A texture handle + * + * Returns the index of the given CoglHandle if found in the + * handle array. + */ +static gint +_cogl_texture_handle_find (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, -1); + + gint i; + + if (ctx->texture_handles == NULL) + return -1; + + for (i=0; i < ctx->texture_handles->len; ++i) + if (g_array_index (ctx->texture_handles, CoglHandle, i) == handle) + return i; + + return -1; +} + +/* + * _cogl_texture_handle_new: + * @tex: A pointer to an allocated CoglTexture structure + * + * Returns a new CoglHandle for the given CoglTexture + * object. + */ +static CoglHandle +_cogl_texture_handle_new (CoglTexture *tex) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + CoglHandle handle = (CoglHandle)tex; + + if (ctx->texture_handles == NULL) + ctx->texture_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle)); + + g_array_append_val (ctx->texture_handles, handle); + + return handle; +} + +/* + * _cogl_texture_handle_release: + * @handle: A valid CoglHandle + * + * Frees the given CoglHandle for use with another object. + */ +static void +_cogl_texture_handle_release (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + gint i; + + if ( (i = _cogl_texture_handle_find (handle)) == -1) + return; + + g_array_remove_index_fast (ctx->texture_handles, i); +} + +/* + * _cogl_texture_pointer_from_handle: + * @handle: A valid CoglHandle + * + * Returns a pointer to the texture object referenced by + * given handle. + */ +CoglTexture * +_cogl_texture_pointer_from_handle (CoglHandle handle) +{ + return (CoglTexture*) handle; +} + +CoglHandle +_cogl_texture_handle_from_pointer (CoglTexture *tex) +{ + return (CoglHandle) tex; +} + +gboolean +cogl_is_texture (CoglHandle handle) +{ + if (handle == COGL_INVALID_HANDLE) + return FALSE; + + return _cogl_texture_handle_find (handle) >= 0; +} + +static void +_cogl_texture_bitmap_free (CoglTexture *tex) +{ + if (tex->bitmap.data != NULL && tex->bitmap_owner) + g_free (tex->bitmap.data); + + tex->bitmap.data = NULL; + tex->bitmap_owner = FALSE; +} + +static void +_cogl_texture_bitmap_swap (CoglTexture *tex, + CoglBitmap *new_bitmap) +{ + if (tex->bitmap.data != NULL && tex->bitmap_owner) + g_free (tex->bitmap.data); + + tex->bitmap = *new_bitmap; + tex->bitmap_owner = TRUE; +} + +static void +_cogl_span_iter_update (CoglSpanIter *iter) +{ + /* Pick current span */ + iter->span = &g_array_index (iter->array, + CoglTexSliceSpan, + iter->index); + + /* Offset next position by span size */ + iter->next_pos = iter->pos + + CLUTTER_INT_TO_FIXED (iter->span->size - iter->span->waste); + + /* Check if span intersects the area to cover */ + if (iter->next_pos <= iter->cover_start || + iter->pos >= iter->cover_end) + { + /* Intersection undefined */ + iter->intersects = FALSE; + return; + } + + iter->intersects = TRUE; + + /* Clip start position to coverage area */ + if (iter->pos < iter->cover_start) + iter->intersect_start = iter->cover_start; + else + iter->intersect_start = iter->pos; + + /* Clip end position to coverage area */ + if (iter->next_pos > iter->cover_end) + iter->intersect_end = iter->cover_end; + else + iter->intersect_end = iter->next_pos; +} + +static void +_cogl_span_iter_begin (CoglSpanIter *iter, + GArray *array, + ClutterFixed origin, + ClutterFixed cover_start, + ClutterFixed cover_end) +{ + /* Copy info */ + iter->index = 0; + iter->array = array; + iter->span = NULL; + iter->origin = origin; + iter->cover_start = cover_start; + iter->cover_end = cover_end; + iter->pos = iter->origin; + + /* Update intersection */ + _cogl_span_iter_update (iter); +} + +void +_cogl_span_iter_next (CoglSpanIter *iter) +{ + /* Move current position */ + iter->pos = iter->next_pos; + + /* Pick next slice (wrap when last reached) */ + iter->index = (iter->index + 1) % iter->array->len; + + /* Update intersection */ + _cogl_span_iter_update (iter); +} + +static gboolean +_cogl_span_iter_end (CoglSpanIter *iter) +{ + /* End reached when whole area covered */ + return iter->pos >= iter->cover_end; +} + +static gboolean +_cogl_texture_upload_to_gl (CoglTexture *tex) +{ + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint gl_handle; + gint bpp; + gint x,y; + CoglBitmap slice_bmp; + + /* FIXME: might optimize by not copying to intermediate slice + bitmap when source rowstride = bpp * width and the texture + image is not sliced */ + + bpp = _cogl_get_format_bpp (tex->bitmap.format); + + /* Iterate vertical slices */ + for (y = 0; y < tex->slice_y_spans->len; ++y) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + /* Iterate horizontal slices */ + for (x = 0; x < tex->slice_x_spans->len; ++x) + { + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + + /* Pick the gl texture object handle */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + y * tex->slice_x_spans->len + x); + + /* Setup temp bitmap for slice subregion */ + slice_bmp.format = tex->bitmap.format; + slice_bmp.width = x_span->size - x_span->waste; + slice_bmp.height = y_span->size - y_span->waste; + slice_bmp.rowstride = bpp * slice_bmp.width; + slice_bmp.data = (guchar*) g_malloc (slice_bmp.rowstride * + slice_bmp.height); + + /* Copy subregion data */ + _cogl_bitmap_copy_subregion (&tex->bitmap, + &slice_bmp, + x_span->start, + y_span->start, + 0, 0, + slice_bmp.width, + slice_bmp.height); + + /* Upload new image data */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glPixelStorei (GL_UNPACK_ALIGNMENT, 1) ); + + GE( glTexSubImage2D (tex->gl_target, 0, 0, 0, + slice_bmp.width, + slice_bmp.height, + tex->gl_format, tex->gl_type, + slice_bmp.data) ); + + /* Free temp bitmap */ + g_free (slice_bmp.data); + } + } + + return TRUE; +} + +static gboolean +_cogl_texture_download_from_gl (CoglTexture *tex, + CoglBitmap *target_bmp, + GLuint target_gl_format, + GLuint target_gl_type) +{ + gint bpp; + GLint viewport[4]; + CoglHandle handle; + ClutterColor cback = {0x0, 0x0, 0x0, 0x0}; + ClutterColor cwhite = {0xFF, 0xFF, 0xFF, 0xFF}; + CoglBitmap rect_bmp; + ClutterFixed rx1, ry1; + ClutterFixed rx2, ry2; + ClutterFixed tx1, ty1; + ClutterFixed tx2, ty2; + int bw, bh; + + handle = _cogl_texture_handle_from_pointer (tex); + bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); + + /* Viewport needs to have some size and be inside the window for this */ + GE( glGetIntegerv (GL_VIEWPORT, viewport) ); + + if (viewport[0] < 0 || viewport[1] < 0 || + viewport[2] <= 0 || viewport[3] <= 0) + return FALSE; + + /* Setup orthographic projection into current viewport + (0,0 in bottom-left corner to draw the texture + upside-down so we match the way glReadPixels works) */ + + GE( glMatrixMode (GL_PROJECTION) ); + GE( glPushMatrix () ); + GE( glLoadIdentity () ); + + GE( glOrthox (0, CLUTTER_INT_TO_FIXED (viewport[2]), + 0, CLUTTER_INT_TO_FIXED (viewport[3]), + CLUTTER_INT_TO_FIXED (0), + CLUTTER_INT_TO_FIXED (100)) ); + + GE( glMatrixMode (GL_MODELVIEW) ); + GE( glPushMatrix () ); + GE( glLoadIdentity () ); + + /* Draw to all channels */ + cogl_draw_buffer (COGL_WINDOW_BUFFER | COGL_MASK_BUFFER, 0); + + + /* If whole image fits into the viewport and target buffer + has got no special rowstride, we can do it in one pass */ + if (tex->bitmap.width < viewport[2] - viewport[0] && + tex->bitmap.height < viewport[3] - viewport[1] && + tex->bitmap.rowstride == bpp * tex->bitmap.width) + { + /* Clear buffer with transparent black, draw with white + for direct copy to framebuffer */ + cogl_paint_init (&cback); + cogl_color (&cwhite); + + /* Draw the texture image */ + cogl_texture_rectangle (handle, + 0, 0, + CLUTTER_INT_TO_FIXED (tex->bitmap.width), + CLUTTER_INT_TO_FIXED (tex->bitmap.height), + 0, 0, CFX_ONE, CFX_ONE); + + /* Read into target bitmap */ + GE( glPixelStorei (GL_PACK_ALIGNMENT, 1) ); + GE( glReadPixels (viewport[0], viewport[1], + tex->bitmap.width, + tex->bitmap.height, + GL_RGBA, GL_UNSIGNED_BYTE, + target_bmp->data) ); + } + else + { + ry1 = 0; ry2 = 0; + ty1 = 0; ty2 = 0; + +#define CFIX CLUTTER_INT_TO_FIXED + + /* Walk Y axis until whole bitmap height consumed */ + for (bh = tex->bitmap.height; bh > 0; bh -= viewport[3]) + { + /* Rectangle Y coords */ + ry1 = ry2; + ry2 += (bh < viewport[3]) ? bh : viewport[3]; + + /* Normalized texture Y coords */ + ty1 = ty2; + ty2 = CFX_QDIV (CFIX (ry2), CFIX (tex->bitmap.height)); + + rx1 = 0; rx2 = 0; + tx1 = 0; tx2 = 0; + + /* Walk X axis until whole bitmap width consumed */ + for (bw = tex->bitmap.width; bw > 0; bw-=viewport[2]) + { + /* Rectangle X coords */ + rx1 = rx2; + rx2 += (bw < viewport[2]) ? bw : viewport[2]; + + /* Normalized texture X coords */ + tx1 = tx2; + tx2 = CFX_QDIV (CFIX (rx2), CFIX (tex->bitmap.width)); + + /* Clear buffer with transparent black, draw with white + for direct copy to framebuffer */ + cogl_paint_init (&cback); + cogl_color (&cwhite); + + /* Draw a portion of texture */ + cogl_texture_rectangle (handle, + 0, 0, + CFIX (rx2 - rx1), + CFIX (ry2 - ry1), + tx1, ty1, + tx2, ty2); + + /* Read into a temporary bitmap */ + rect_bmp.format = COGL_PIXEL_FORMAT_RGBA_8888; + rect_bmp.width = rx2 - rx1; + rect_bmp.height = ry2 - ry1; + rect_bmp.rowstride = bpp * rect_bmp.width; + rect_bmp.data = (guchar*) g_malloc (rect_bmp.rowstride * + rect_bmp.height); + + GE( glPixelStorei (GL_PACK_ALIGNMENT, 1) ); + GE( glReadPixels (viewport[0], viewport[1], + rect_bmp.width, + rect_bmp.height, + GL_RGBA, GL_UNSIGNED_BYTE, + rect_bmp.data) ); + + /* Copy to target bitmap */ + _cogl_bitmap_copy_subregion (&rect_bmp, + target_bmp, + 0,0, + rx1,ry1, + rect_bmp.width, + rect_bmp.height); + + /* Free temp bitmap */ + g_free (rect_bmp.data); + } + } + +#undef CFIX + } + + glMatrixMode (GL_PROJECTION); + glPopMatrix (); + glMatrixMode (GL_MODELVIEW); + glPopMatrix (); + + cogl_draw_buffer (COGL_WINDOW_BUFFER, 0); + + return TRUE; +} + +static void +_cogl_texture_set_alignment_for_rowstride (gint rowstride) +{ + gint alignment = 1; + + while ((rowstride & 1) == 0 && alignment < 8) + { + alignment <<= 1; + rowstride >>= 1; + } + + GE( glPixelStorei (GL_UNPACK_ALIGNMENT, alignment) ); +} + +static gboolean +_cogl_texture_upload_subregion_to_gl (CoglTexture *tex, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + gint width, + gint height, + CoglBitmap *source_bmp, + GLuint source_gl_format, + GLuint source_gl_type) +{ + gint bpp; + CoglSpanIter x_iter; + CoglSpanIter y_iter; + GLuint gl_handle; + gint source_x = 0, source_y = 0; + gint inter_w = 0, inter_h = 0; + gint local_x = 0, local_y = 0; + CoglBitmap slice_bmp; + + bpp = _cogl_get_format_bpp (source_bmp->format); + + /* FIXME: might optimize by not copying to intermediate slice + bitmap when source rowstride = bpp * width and the texture + image is not sliced */ + + /* Iterate vertical spans */ + for (source_y = src_y, + _cogl_span_iter_begin (&y_iter, tex->slice_y_spans, + 0, CLUTTER_INT_TO_FIXED (dst_y), + CLUTTER_INT_TO_FIXED (dst_y + height)); + + !_cogl_span_iter_end (&y_iter); + + _cogl_span_iter_next (&y_iter), + source_y += inter_h ) + { + /* Skip non-intersecting ones */ + if (!y_iter.intersects) + { + inter_h = 0; + continue; + } + + /* Iterate horizontal spans */ + for (source_x = src_x, + _cogl_span_iter_begin (&x_iter, tex->slice_x_spans, + 0, CLUTTER_INT_TO_FIXED (dst_x), + CLUTTER_INT_TO_FIXED (dst_x + width)); + + !_cogl_span_iter_end (&x_iter); + + _cogl_span_iter_next (&x_iter), + source_x += inter_w ) + { + /* Skip non-intersecting ones */ + if (!x_iter.intersects) + { + inter_w = 0; + continue; + } + + /* Pick intersection width and height */ + inter_w = CLUTTER_FIXED_TO_INT (x_iter.intersect_end - + x_iter.intersect_start); + inter_h = CLUTTER_FIXED_TO_INT (y_iter.intersect_end - + y_iter.intersect_start); + + /* Localize intersection top-left corner to slice*/ + local_x = CLUTTER_FIXED_TO_INT (x_iter.intersect_start - + x_iter.pos); + local_y = CLUTTER_FIXED_TO_INT (y_iter.intersect_start - + y_iter.pos); + + /* Pick slice GL handle */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + y_iter.index * tex->slice_x_spans->len + + x_iter.index); + + /* Setup temp bitmap for slice subregion */ + slice_bmp.format = tex->bitmap.format; + slice_bmp.width = inter_w; + slice_bmp.height = inter_h; + slice_bmp.rowstride = bpp * slice_bmp.width; + slice_bmp.data = (guchar*) malloc (slice_bmp.rowstride * + slice_bmp.height); + + /* Copy subregion data */ + _cogl_bitmap_copy_subregion (source_bmp, + &slice_bmp, + source_x, + source_y, + 0, 0, + slice_bmp.width, + slice_bmp.height); + + /* Upload new image data */ + GE( glPixelStorei (GL_UNPACK_ALIGNMENT, 1) ); + + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glTexSubImage2D (tex->gl_target, 0, + local_x, local_y, + inter_w, inter_h, + source_gl_format, + source_gl_type, + slice_bmp.data) ); + + /* Free temp bitmap */ + g_free (slice_bmp.data); + } + } + + return TRUE; +} + +static gint +_cogl_pot_slices_for_size (gint size_to_fill, + gint max_span_size, + gint max_waste, + GArray *out_spans) +{ + gint n_spans = 0; + CoglTexSliceSpan span; + + /* Init first slice span */ + span.start = 0; + span.size = max_span_size; + span.waste = 0; + + /* Fix invalid max_waste */ + if (max_waste < 0) max_waste = 0; + + while (TRUE) + { + /* Is the whole area covered? */ + if (size_to_fill > span.size) + { + /* Not yet - add a span of this size */ + if (out_spans) g_array_append_val (out_spans, span); + span.start += span.size; + size_to_fill -= span.size; + n_spans++; + } + else if (span.size - size_to_fill <= max_waste) + { + /* Yes and waste is small enough */ + span.waste = span.size - size_to_fill; + if (out_spans) g_array_append_val (out_spans, span); + return ++n_spans; + } + else + { + /* Yes but waste is too large */ + while (span.size - size_to_fill > max_waste) + { + span.size /= 2; + g_assert (span.size > 0); + } + } + } + + /* Can't get here */ + return 0; +} + +static gboolean +_cogl_texture_size_supported (GLenum gl_target, + GLenum gl_format, + GLenum gl_type, + int width, + int height) +{ + return TRUE; +} + +static gboolean +_cogl_texture_slices_create (CoglTexture *tex) +{ + gint bpp; + gint max_width; + gint max_height; + GLuint *gl_handles; + gint n_x_slices; + gint n_y_slices; + gint n_slices; + gint x, y; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + + gint (*slices_for_size) (gint, gint, gint, GArray*); + + bpp = _cogl_get_format_bpp (tex->bitmap.format); + + /* Initialize size of largest slice according to supported features */ + max_width = cogl_util_next_p2 (tex->bitmap.width); + max_height = cogl_util_next_p2 (tex->bitmap.height); + tex->gl_target = GL_TEXTURE_2D; + slices_for_size = _cogl_pot_slices_for_size; + + /* Negative number means no slicing forced by the user */ + if (tex->max_waste <= -1) + { + CoglTexSliceSpan span; + + /* Check if size supported else bail out */ + if (!_cogl_texture_size_supported (tex->gl_target, + tex->gl_format, + tex->gl_type, + max_width, + max_height)) + { + return FALSE; + } + + n_x_slices = 1; + n_y_slices = 1; + + /* Init span arrays */ + tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + 1); + + tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + 1); + + /* Add a single span for width and height */ + span.start = 0; + span.size = max_width; + span.waste = max_width - tex->bitmap.width; + g_array_append_val (tex->slice_x_spans, span); + + span.size = max_height; + span.waste = max_height - tex->bitmap.height; + g_array_append_val (tex->slice_y_spans, span); + } + else + { + /* Decrease the size of largest slice until supported by GL */ + while (!_cogl_texture_size_supported (tex->gl_target, + tex->gl_format, + tex->gl_type, + max_width, + max_height)) + { + /* Alternate between width and height */ + if (max_width > max_height) + max_width /= 2; + else + max_height /= 2; + + if (max_width == 0 || max_height == 0) + return FALSE; + } + + /* Determine the slices required to cover the bitmap area */ + n_x_slices = slices_for_size (tex->bitmap.width, + max_width, tex->max_waste, + NULL); + + n_y_slices = slices_for_size (tex->bitmap.height, + max_height, tex->max_waste, + NULL); + + /* Init span arrays with reserved size */ + tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + n_x_slices); + + tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + n_y_slices); + + /* Fill span arrays with info */ + slices_for_size (tex->bitmap.width, + max_width, tex->max_waste, + tex->slice_x_spans); + + slices_for_size (tex->bitmap.height, + max_height, tex->max_waste, + tex->slice_y_spans); + } + + /* Init and resize GL handle array */ + n_slices = n_x_slices * n_y_slices; + + tex->slice_gl_handles = g_array_sized_new (FALSE, FALSE, + sizeof (GLuint), + n_slices); + + g_array_set_size (tex->slice_gl_handles, n_slices); + + /* Generate a "working set" of GL texture objects + * (some implementations might supported faster + * re-binding between textures inside a set) */ + gl_handles = (GLuint*) tex->slice_gl_handles->data; + + GE( glGenTextures (n_slices, gl_handles) ); + + + /* Init each GL texture object */ + for (y = 0; y < n_y_slices; ++y) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + for (x = 0; x < n_x_slices; ++x) + { + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + +#if COGL_DEBUG + printf ("CREATE SLICE (%d,%d)\n", x,y); + printf ("size: (%d x %d)\n", + x_span->size - x_span->waste, + y_span->size - y_span->waste); +#endif + /* Setup texture parameters */ + GE( glBindTexture (tex->gl_target, gl_handles[y * n_x_slices + x]) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) ); + + /* Pass NULL data to init size and internal format */ + GE( glTexImage2D (tex->gl_target, 0, tex->gl_intformat, + x_span->size, y_span->size, 0, + tex->gl_format, tex->gl_type, 0) ); + } + } + + return TRUE; +} + +static void +_cogl_texture_slices_free (CoglTexture *tex) +{ + if (tex->slice_x_spans != NULL) + g_array_free (tex->slice_x_spans, TRUE); + + if (tex->slice_y_spans != NULL) + g_array_free (tex->slice_y_spans, TRUE); + + if (tex->slice_gl_handles != NULL) + { + if (tex->is_foreign == FALSE) + { + GE( glDeleteTextures (tex->slice_gl_handles->len, + (GLuint*) tex->slice_gl_handles->data) ); + } + + g_array_free (tex->slice_gl_handles, TRUE); + } +} + +static gboolean +_cogl_pixel_format_from_gl_internal (GLenum gl_int_format, + CoglPixelFormat *out_format) +{ + /* It doesn't really matter we convert to exact same + format (some have no cogl match anyway) since format + is re-matched against cogl when getting or setting + texture image data. + */ + + switch (gl_int_format) + { + case GL_ALPHA: + + *out_format = COGL_PIXEL_FORMAT_A_8; + return TRUE; + + case GL_LUMINANCE: + + *out_format = COGL_PIXEL_FORMAT_G_8; + return TRUE; + + case GL_RGB: + + *out_format = COGL_PIXEL_FORMAT_RGB_888; + return TRUE; + + case GL_RGBA: + + *out_format = COGL_PIXEL_FORMAT_RGBA_8888; + return TRUE; + } + + return FALSE; +} + +static CoglPixelFormat +_cogl_pixel_format_to_gl (CoglPixelFormat format, + GLenum *out_glintformat, + GLenum *out_glformat, + GLenum *out_gltype) +{ + CoglPixelFormat required_format = format; + GLenum glintformat = 0; + GLenum glformat = 0; + GLenum gltype = 0; + + /* No premultiplied formats accepted by GL + * (FIXME: latest hardware?) */ + + if (format & COGL_PREMULT_BIT) + format = (format & COGL_UNPREMULT_MASK); + + /* Find GL equivalents */ + switch (format) + { + case COGL_PIXEL_FORMAT_A_8: + glintformat = GL_ALPHA; + glformat = GL_ALPHA; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_G_8: + glintformat = GL_LUMINANCE; + glformat = GL_LUMINANCE; + gltype = GL_UNSIGNED_BYTE; + break; + + /* Just one 24-bit ordering supported */ + case COGL_PIXEL_FORMAT_RGB_888: + case COGL_PIXEL_FORMAT_BGR_888: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_BYTE; + required_format = COGL_PIXEL_FORMAT_RGB_888; + break; + + /* Just one 32-bit ordering supported */ + case COGL_PIXEL_FORMAT_RGBA_8888: + case COGL_PIXEL_FORMAT_BGRA_8888: + case COGL_PIXEL_FORMAT_ARGB_8888: + case COGL_PIXEL_FORMAT_ABGR_8888: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_BYTE; + required_format = COGL_PIXEL_FORMAT_RGBA_8888; + break; + + /* The following three types of channel ordering + * are always defined using system word byte + * ordering (even according to GLES spec) */ + case COGL_PIXEL_FORMAT_RGB_565: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_SHORT_5_6_5; + break; + case COGL_PIXEL_FORMAT_RGBA_4444: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case COGL_PIXEL_FORMAT_RGBA_5551: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_5_5_5_1; + break; + + /* FIXME: check extensions for YUV support */ + default: + break; + } + + if (out_glintformat != NULL) + *out_glintformat = glintformat; + if (out_glformat != NULL) + *out_glformat = glformat; + if (out_gltype != NULL) + *out_gltype = gltype; + + return required_format; +} + +static gboolean +_cogl_texture_bitmap_prepare (CoglTexture *tex, + CoglPixelFormat internal_format) +{ + CoglBitmap new_bitmap; + CoglPixelFormat new_data_format; + gboolean success; + + /* Was there any internal conversion requested? */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + internal_format = tex->bitmap.format; + + /* Find closest format accepted by GL */ + new_data_format = _cogl_pixel_format_to_gl (internal_format, + &tex->gl_intformat, + &tex->gl_format, + &tex->gl_type); + + /* Convert to internal format */ + if (new_data_format != tex->bitmap.format) + { + success = _cogl_bitmap_convert_and_premult (&tex->bitmap, + &new_bitmap, + new_data_format); + + if (!success) + return FALSE; + + /* Update texture with new data */ + _cogl_texture_bitmap_swap (tex, &new_bitmap); + } + + return TRUE; +} + +static void +_cogl_texture_free (CoglTexture *tex) +{ + /* Frees texture resources but its handle is not + released! Do that separately before this! */ + _cogl_texture_bitmap_free (tex); + _cogl_texture_slices_free (tex); + g_free (tex); +} + +CoglHandle +cogl_texture_new_with_size (guint width, + guint height, + gint max_waste, + CoglPixelFormat internal_format) +{ + CoglTexture *tex; + gint bpp; + gint rowstride; + + /* Since no data, we need some internal format */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + /* Rowstride from width */ + bpp = _cogl_get_format_bpp (internal_format); + rowstride = width * bpp; + + /* Init texture with empty bitmap */ + tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); + + tex->ref_count = 1; + + tex->is_foreign = FALSE; + + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.format = internal_format; + tex->bitmap.rowstride = rowstride; + tex->bitmap.data = NULL; + tex->bitmap_owner = FALSE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* Find closest GL format match */ + tex->bitmap.format = + _cogl_pixel_format_to_gl (internal_format, + &tex->gl_intformat, + &tex->gl_format, + &tex->gl_type); + + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_new_from_data (guint width, + guint height, + gint max_waste, + CoglPixelFormat format, + CoglPixelFormat internal_format, + guint rowstride, + const guchar *data) +{ + CoglTexture *tex; + gint bpp; + + if (format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + if (data == NULL) + return COGL_INVALID_HANDLE; + + /* Rowstride from width if not given */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = width * bpp; + + /* Create new texture and fill with given data */ + tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); + + tex->ref_count = 1; + + tex->is_foreign = FALSE; + + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.data = (guchar*)data; + tex->bitmap.format = format; + tex->bitmap.rowstride = rowstride; + tex->bitmap_owner = FALSE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* FIXME: If upload fails we should set some kind of + * error flag but still return texture handle (this + * is to keep the behavior equal to _new_from_file; + * see below) */ + + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_upload_to_gl (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + _cogl_texture_bitmap_free (tex); + + return _cogl_texture_handle_new (tex);; +} + +CoglHandle +cogl_texture_new_from_file (const gchar *filename, + gint max_waste, + CoglPixelFormat internal_format, + GError **error) +{ + CoglBitmap bmp; + CoglTexture *tex; + + g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE); + + /* Try loading with imaging backend */ + if (!_cogl_bitmap_from_file (&bmp, filename, error)) + { + /* Try fallback */ + if (!_cogl_bitmap_fallback_from_file (&bmp, filename)) + return COGL_INVALID_HANDLE; + else if (error && *error) + { + g_error_free (*error); + *error = NULL; + } + } + + /* Create new texture and fill with loaded data */ + tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture)); + + tex->ref_count = 1; + + tex->is_foreign = FALSE; + + tex->bitmap = bmp; + tex->bitmap_owner = TRUE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* FIXME: If upload fails we should set some kind of + * error flag but still return texture handle if the + * user decides to destroy another texture and upload + * this one instead (reloading from file is not needed + * in that case). As a rule then, everytime a valid + * CoglHandle is returned, it should also be destroyed + * with cogl_texture_unref at some point! */ + + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_upload_to_gl (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + _cogl_texture_bitmap_free (tex); + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_new_from_foreign (GLuint gl_handle, + GLenum gl_target, + GLuint width, + GLuint height, + GLuint x_pot_waste, + GLuint y_pot_waste, + CoglPixelFormat format) +{ + GLenum gl_error = 0; + GLboolean gl_istexture; + GLint gl_min_filter; + GLint gl_mag_filter; + guint bpp; + CoglTexture *tex; + CoglTexSliceSpan x_span; + CoglTexSliceSpan y_span; + + /* Allow 2-dimensional textures only */ + if (gl_target != GL_TEXTURE_2D) + return COGL_INVALID_HANDLE; + + /* Make sure it is a valid GL texture object */ + gl_istexture = GE( glIsTexture (gl_handle) ); + if (gl_istexture == GL_FALSE) + return COGL_INVALID_HANDLE; + + /* Make sure binding succeeds */ + gl_error = glGetError (); + glBindTexture (gl_target, gl_handle); + if (glGetError () != GL_NO_ERROR) + return COGL_INVALID_HANDLE; + + /* Obtain texture parameters + (only level 0 we are interested in) */ + + /* These are not queriable in GLES :( + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_COMPRESSED, + &gl_compressed) ); + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_INTERNAL_FORMAT, + &gl_int_format) ); + */ + GE( glGetTexParameteriv (gl_target, + GL_TEXTURE_MIN_FILTER, + &gl_min_filter)); + + GE( glGetTexParameteriv (gl_target, + GL_TEXTURE_MAG_FILTER, + &gl_mag_filter)); + + /* Validate width and height */ + if (width <= 0 || height <= 0) + return COGL_INVALID_HANDLE; + + /* Validate pot waste */ + if (x_pot_waste < 0 || x_pot_waste >= width || + y_pot_waste < 0 || y_pot_waste >= height) + return COGL_INVALID_HANDLE; + + /* Create new texture */ + tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture)); + + tex->ref_count = 1; +#if COGL_DEBUG + printf ("COGL TEX new %p %i\n", tex, tex->ref_count); +#endif + + /* Setup bitmap info */ + tex->is_foreign = TRUE; + + bpp = _cogl_get_format_bpp (format); + tex->bitmap.format = format; + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.rowstride = tex->bitmap.width * bpp; + tex->bitmap_owner = FALSE; + + tex->gl_target = gl_target; + tex->gl_intformat = 0; + tex->gl_format = 0; + tex->gl_type = 0; + + tex->min_filter = gl_min_filter; + tex->mag_filter = gl_mag_filter; + tex->max_waste = 0; + + /* Create slice arrays */ + tex->slice_x_spans = + g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), 1); + + tex->slice_y_spans = + g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), 1); + + tex->slice_gl_handles = + g_array_sized_new (FALSE, FALSE, + sizeof (GLuint), 1); + + /* Store info for a single slice */ + x_span.start = 0; + x_span.size = width + x_pot_waste; + x_span.waste = x_pot_waste; + g_array_append_val (tex->slice_x_spans, x_span); + + y_span.start = 0; + y_span.size = height + x_pot_waste; + y_span.waste = y_pot_waste; + g_array_append_val (tex->slice_y_spans, y_span); + + g_array_append_val (tex->slice_gl_handles, gl_handle); + + /* Replace mipmap min filter modes with single level ones */ + if (gl_min_filter != GL_NEAREST && gl_min_filter != GL_LINEAR) + { + if (gl_min_filter == GL_NEAREST_MIPMAP_NEAREST) + { + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST) ); + tex->min_filter = CGL_NEAREST; + } + else + { + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR) ); + tex->min_filter = CGL_LINEAR; + } + } + + /* Force appropriate wrap parameter */ + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) ); + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_ref (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return COGL_INVALID_HANDLE; + + tex = _cogl_texture_pointer_from_handle (handle); + + tex->ref_count++; + + return handle; +} + +void +cogl_texture_unref (CoglHandle handle) +{ + /* Check if valid texture handle */ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + if (--tex->ref_count < 1) + { + /* Free texture handle and resources */ + _cogl_texture_handle_release (tex); + _cogl_texture_free (tex); + } +} + +void +cogl_texture_get_properties (CoglHandle handle, + guint *out_width, + guint *out_height, + CoglPixelFormat *out_format, + guint *out_rowstride, + guint *out_max_waste) +{ + CoglTexture *tex; + + /* Check if valid texture handle */ + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Output requested properties */ + if (out_width != NULL) + *out_width = tex->bitmap.width; + + if (out_height != NULL) + *out_height = tex->bitmap.height; + + if (out_format != NULL) + *out_format = tex->bitmap.format; + + if (out_rowstride != NULL) + *out_rowstride = tex->bitmap.rowstride; + + if (out_max_waste != NULL) + *out_max_waste = tex->max_waste; +} + +guint +cogl_texture_get_width (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.width; +} + +guint +cogl_texture_get_height (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.height; +} + +CoglPixelFormat +cogl_texture_get_format (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return COGL_PIXEL_FORMAT_ANY; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.format; +} + +guint +cogl_texture_get_rowstride (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.rowstride; +} + +gint +cogl_texture_get_max_waste (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->max_waste; +} + +gboolean +cogl_texture_is_sliced (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + if (tex->slice_gl_handles == NULL) + return FALSE; + + if (tex->slice_gl_handles->len <= 1) + return FALSE; + + return TRUE; +} + +gboolean +cogl_texture_get_gl_texture (CoglHandle handle, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + if (tex->slice_gl_handles == NULL) + return FALSE; + + if (tex->slice_gl_handles->len < 1) + return FALSE; + + if (out_gl_handle != NULL) + *out_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + + if (out_gl_target != NULL) + *out_gl_target = tex->gl_target; + + return TRUE; +} + +COGLenum +cogl_texture_get_min_filter (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->min_filter; +} + +COGLenum +cogl_texture_get_mag_filter (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->mag_filter; +} + +void +cogl_texture_set_filters (CoglHandle handle, + COGLenum min_filter, + COGLenum mag_filter) +{ + CoglTexture *tex; + GLuint gl_handle; + int i; + + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Store new values */ + tex->min_filter = min_filter; + tex->mag_filter = mag_filter; + + /* Make sure slices were created */ + if (tex->slice_gl_handles == NULL) + return; + + /* Apply new filters to every slice */ + for (i=0; i<tex->slice_gl_handles->len; ++i) + { + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, i); + GE( glBindTexture (tex->gl_target, gl_handle) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) ); + } +} + +gboolean +cogl_texture_set_region (CoglHandle handle, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + guint dst_width, + guint dst_height, + gint width, + gint height, + CoglPixelFormat format, + guint rowstride, + const guchar *data) +{ + CoglTexture *tex; + gint bpp; + CoglBitmap source_bmp; + CoglBitmap temp_bmp; + gboolean source_bmp_owner = FALSE; + CoglPixelFormat closest_format; + GLenum closest_gl_format; + GLenum closest_gl_type; + gboolean success; + + /* Check if valid texture handle */ + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Check for valid format */ + if (format == COGL_PIXEL_FORMAT_ANY) + return FALSE; + + /* Init source bitmap */ + source_bmp.width = width; + source_bmp.height = height; + source_bmp.format = format; + source_bmp.data = (guchar*)data; + + /* Rowstride from width if none specified */ + bpp = _cogl_get_format_bpp (format); + source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride; + + /* Find closest format to internal that's supported by GL */ + closest_format = _cogl_pixel_format_to_gl (tex->bitmap.format, + NULL, /* don't need */ + &closest_gl_format, + &closest_gl_type); + + /* If no direct match, convert */ + if (closest_format != format) + { + /* Convert to required format */ + success = _cogl_bitmap_convert_and_premult (&source_bmp, + &temp_bmp, + closest_format); + + /* Swap bitmaps if succeeded */ + if (!success) return FALSE; + source_bmp = temp_bmp; + source_bmp_owner = TRUE; + } + + /* Send data to GL */ + _cogl_texture_upload_subregion_to_gl (tex, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + &source_bmp, + closest_gl_format, + closest_gl_type); + + /* Free data if owner */ + if (source_bmp_owner) + g_free (source_bmp.data); + + return TRUE; +} + +gint +cogl_texture_get_data (CoglHandle handle, + CoglPixelFormat format, + guint rowstride, + guchar *data) +{ + CoglTexture *tex; + gint bpp; + gint byte_size; + CoglPixelFormat closest_format; + gint closest_bpp; + GLenum closest_gl_format; + GLenum closest_gl_type; + CoglBitmap target_bmp; + CoglBitmap new_bmp; + gboolean success; + guchar *src; + guchar *dst; + gint y; + + /* Check if valid texture handle */ + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Default to internal format if none specified */ + if (format == COGL_PIXEL_FORMAT_ANY) + format = tex->bitmap.format; + + /* Rowstride from texture width if none specified */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = tex->bitmap.width * bpp; + + /* Return byte size if only that requested */ + byte_size = tex->bitmap.height * rowstride; + if (data == NULL) return byte_size; + + /* Find closest format that's supported by GL + (Can't use _cogl_pixel_format_to_gl since available formats + when reading pixels on GLES are severely limited) */ + closest_format = COGL_PIXEL_FORMAT_RGBA_8888; + closest_gl_format = GL_RGBA; + closest_gl_type = GL_UNSIGNED_BYTE; + closest_bpp = _cogl_get_format_bpp (closest_format); + + /* Is the requested format supported? */ + if (closest_format == format) + { + /* Target user data directly */ + target_bmp = tex->bitmap; + target_bmp.format = format; + target_bmp.rowstride = rowstride; + target_bmp.data = data; + } + else + { + /* Target intermediate buffer */ + target_bmp = tex->bitmap; + target_bmp.format = closest_format; + target_bmp.rowstride = target_bmp.width * closest_bpp; + target_bmp.data = (guchar*) g_malloc (target_bmp.height + * target_bmp.rowstride); + } + + /* Retrieve data from slices */ + _cogl_texture_download_from_gl (tex, &target_bmp, + closest_gl_format, + closest_gl_type); + + /* Was intermediate used? */ + if (closest_format != format) + { + /* Convert to requested format */ + success = _cogl_bitmap_convert_and_premult (&target_bmp, + &new_bmp, + format); + + /* Free intermediate data and return if failed */ + g_free (target_bmp.data); + if (!success) return 0; + + /* Copy to user buffer */ + for (y = 0; y < new_bmp.height; ++y) + { + src = new_bmp.data + y * new_bmp.rowstride; + dst = data + y * rowstride; + memcpy (dst, src, new_bmp.width); + } + + /* Free converted data */ + g_free (new_bmp.data); + } + + return byte_size; +} + +static void +_cogl_texture_quad_sw (CoglTexture *tex, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglSpanIter iter_x , iter_y; + ClutterFixed tw , th; + ClutterFixed tqx , tqy; + ClutterFixed first_tx , first_ty; + ClutterFixed first_qx , first_qy; + ClutterFixed slice_tx1 , slice_ty1; + ClutterFixed slice_tx2 , slice_ty2; + ClutterFixed slice_qx1 , slice_qy1; + ClutterFixed slice_qx2 , slice_qy2; + GLfixed tex_coords[8]; + GLfixed quad_coords[8]; + GLuint gl_handle; + +#if COGL_DEBUG + printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); +#endif + + /* Prepare GL state */ + gulong enable_flags = (COGL_ENABLE_TEXTURE_2D + | COGL_ENABLE_VERTEX_ARRAY + | COGL_ENABLE_TEXCOORD_ARRAY); + + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + { + enable_flags |= COGL_ENABLE_BLEND; + } + + cogl_enable (enable_flags); + + GE( glTexCoordPointer (2, GL_FIXED, 0, tex_coords) ); + GE( glVertexPointer (2, GL_FIXED, 0, quad_coords) ); + + /* Scale ratio from texture to quad widths */ + tw = CLUTTER_INT_TO_FIXED (tex->bitmap.width); + th = CLUTTER_INT_TO_FIXED (tex->bitmap.height); + + tqx = CFX_QDIV (x2-x1, CFX_QMUL (tw, (tx2 - tx1))); + tqy = CFX_QDIV (y2-y1, CFX_QMUL (th, (ty2 - ty1))); + + /* Integral texture coordinate for first tile */ + first_tx = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (tx1)); + first_ty = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (ty1)); + + /* Denormalize texture coordinates */ + first_tx = CFX_QMUL (first_tx, tw); + first_ty = CFX_QMUL (first_ty, th); + tx1 = CFX_QMUL (tx1, tw); + ty1 = CFX_QMUL (ty1, th); + tx2 = CFX_QMUL (tx2, tw); + ty2 = CFX_QMUL (ty2, th); + + /* Quad coordinate of the first tile */ + first_qx = x1 - CFX_QMUL (tx1 - first_tx, tqx); + first_qy = y1 - CFX_QMUL (ty1 - first_ty, tqy); + + + /* Iterate until whole quad height covered */ + for (_cogl_span_iter_begin (&iter_y, tex->slice_y_spans, + first_ty, ty1, ty2) ; + !_cogl_span_iter_end (&iter_y) ; + _cogl_span_iter_next (&iter_y) ) + { + /* Discard slices out of quad early */ + if (!iter_y.intersects) continue; + + /* Span-quad intersection in quad coordinates */ + slice_qy1 = first_qy + + CFX_QMUL (iter_y.intersect_start - first_ty, tqy); + + slice_qy2 = first_qy + + CFX_QMUL (iter_y.intersect_end - first_ty, tqy); + + /* Localize slice texture coordinates */ + slice_ty1 = iter_y.intersect_start - iter_y.pos; + slice_ty2 = iter_y.intersect_end - iter_y.pos; + + /* Normalize texture coordinates to current slice */ + slice_ty1 /= iter_y.span->size; + slice_ty2 /= iter_y.span->size; + + + /* Iterate until whole quad width covered */ + for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans, + first_tx, tx1, tx2) ; + !_cogl_span_iter_end (&iter_x) ; + _cogl_span_iter_next (&iter_x) ) + { + /* Discard slices out of quad early */ + if (!iter_x.intersects) continue; + + /* Span-quad intersection in quad coordinates */ + slice_qx1 = first_qx + + CFX_QMUL (iter_x.intersect_start - first_tx, tqx); + + slice_qx2 = first_qx + + CFX_QMUL (iter_x.intersect_end - first_tx, tqx); + + /* Localize slice texture coordinates */ + slice_tx1 = iter_x.intersect_start - iter_x.pos; + slice_tx2 = iter_x.intersect_end - iter_x.pos; + + /* Normalize texture coordinates to current slice */ + slice_tx1 /= iter_x.span->size; + slice_tx2 /= iter_x.span->size; + +#if COGL_DEBUG + printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index); + printf("qx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx1)); + printf("qy1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy1)); + printf("qx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx2)); + printf("qy2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy2)); + printf("tx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx1)); + printf("ty1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty1)); + printf("tx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx2)); + printf("ty2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty2)); +#endif + + /* Pick and bind opengl texture object */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + iter_y.index * iter_x.array->len + + iter_x.index); + + GE( glBindTexture (tex->gl_target, gl_handle) ); + + /* Draw textured quad */ + tex_coords[0] = slice_tx1; tex_coords[1] = slice_ty1; + tex_coords[2] = slice_tx2; tex_coords[3] = slice_ty1; + tex_coords[4] = slice_tx1; tex_coords[5] = slice_ty2; + tex_coords[6] = slice_tx2; tex_coords[7] = slice_ty2; + + quad_coords[0] = slice_qx1; quad_coords[1] = slice_qy1; + quad_coords[2] = slice_qx2; quad_coords[3] = slice_qy1; + quad_coords[4] = slice_qx1; quad_coords[5] = slice_qy2; + quad_coords[6] = slice_qx2; quad_coords[7] = slice_qy2; + + GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); + } + } +} + +static void +_cogl_texture_quad_hw (CoglTexture *tex, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + GLfixed tex_coords[8]; + GLfixed quad_coords[8]; + GLuint gl_handle; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + +#if COGL_DEBUG + printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); +#endif + + /* Prepare GL state */ + gulong enable_flags = (COGL_ENABLE_TEXTURE_2D + | COGL_ENABLE_VERTEX_ARRAY + | COGL_ENABLE_TEXCOORD_ARRAY); + + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + { + enable_flags |= COGL_ENABLE_BLEND; + } + + cogl_enable (enable_flags); + + GE( glTexCoordPointer (2, GL_FIXED, 0, tex_coords) ); + GE( glVertexPointer (2, GL_FIXED, 0, quad_coords) ); + + /* Pick and bind opengl texture object */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + GE( glBindTexture (tex->gl_target, gl_handle) ); + + /* Don't include the waste in the texture coordinates */ + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + tx1 = tx1 * (x_span->size - x_span->waste) / x_span->size; + tx2 = tx2 * (x_span->size - x_span->waste) / x_span->size; + ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size; + ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; + + /* Draw textured quad */ + tex_coords[0] = tx1; tex_coords[1] = ty1; + tex_coords[2] = tx2; tex_coords[3] = ty1; + tex_coords[4] = tx1; tex_coords[5] = ty2; + tex_coords[6] = tx2; tex_coords[7] = ty2; + + quad_coords[0] = x1; quad_coords[1] = y1; + quad_coords[2] = x2; quad_coords[3] = y1; + quad_coords[4] = x1; quad_coords[5] = y2; + quad_coords[6] = x2; quad_coords[7] = y2; + + GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); +} + +void +cogl_texture_rectangle (CoglHandle handle, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + CoglTexture *tex; + ClutterFixed tempx; + + /* Check if valid texture */ + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Make sure we got stuff to draw */ + if (tex->slice_gl_handles == NULL) + return; + + if (tex->slice_gl_handles->len == 0) + return; + + if (tx1 == tx2 || ty1 == ty2) + return; + + /* Fix quad coord ordering */ + if (x1 > x2) + { + tempx = x1; + x1 = x2; + x2 = tempx; + } + + if (y1 > y2) + { + tempx = y1; + y1 = y2; + y2 = tempx; + } + + /* Fix texture coord ordering */ + if (tx1 > tx2) + { + tempx = tx1; + tx1 = tx2; + tx2 = tempx; + } + + if (ty1 > ty2) + { + tempx = ty1; + ty1 = ty2; + ty2 = tempx; + } + + /* Tile textured quads */ + if (tex->slice_gl_handles->len == 1 + && tx1 >= -CFX_ONE && tx2 <= CFX_ONE + && ty1 >= -CFX_ONE && ty2 <= CFX_ONE) + { + _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + } + else + { + _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + } +} + +void +cogl_texture_polygon (CoglHandle handle, + guint n_vertices, + CoglTextureVertex *vertices, + gboolean use_color) +{ + CoglTexture *tex; + GLuint gl_handle; + gulong enable_flags; + int i; + CoglTextureGLVertex *p; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Check if valid texture */ + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* GL ES has no GL_CLAMP_TO_BORDER wrap mode so the method used to + render sliced textures in the GL backend will not work. Therefore + cogl_texture_polygon is only supported if the texture is not + sliced */ + if (tex->slice_gl_handles->len != 1) + { + static gboolean shown_warning = FALSE; + + if (!shown_warning) + { + g_warning ("cogl_texture_polygon does not work for " + "sliced textures on GL ES"); + shown_warning = TRUE; + } + return; + } + + /* Make sure there is enough space in the global texture vertex + array. This is used so we can render the polygon with a single + call to OpenGL but still support any number of vertices */ + if (ctx->texture_vertices_size < n_vertices) + { + guint nsize = ctx->texture_vertices_size; + + if (nsize == 0) + nsize = 1; + do + nsize *= 2; + while (nsize < n_vertices); + + ctx->texture_vertices_size = nsize; + + if (ctx->texture_vertices) + ctx->texture_vertices = g_realloc (ctx->texture_vertices, + nsize + * sizeof (CoglTextureGLVertex)); + else + ctx->texture_vertices = g_malloc (nsize + * sizeof (CoglTextureGLVertex)); + } + + /* Prepare GL state */ + enable_flags = (COGL_ENABLE_TEXTURE_2D + | COGL_ENABLE_VERTEX_ARRAY + | COGL_ENABLE_TEXCOORD_ARRAY); + + if ((tex->bitmap.format & COGL_A_BIT)) + enable_flags |= COGL_ENABLE_BLEND; + else if (use_color) + { + for (i = 0; i < n_vertices; i++) + if (vertices[i].color.alpha < 255) + { + enable_flags |= COGL_ENABLE_BLEND; + break; + } + } + else if (ctx->color_alpha < 255) + enable_flags |= COGL_ENABLE_BLEND; + + if (use_color) + { + enable_flags |= COGL_ENABLE_COLOR_ARRAY; + GE( glColorPointer (4, GL_FIXED, sizeof (CoglTextureGLVertex), + ctx->texture_vertices[0].c) ); + } + + GE( glVertexPointer (3, GL_FIXED, sizeof (CoglTextureGLVertex), + ctx->texture_vertices[0].v) ); + GE( glTexCoordPointer (2, GL_FIXED, sizeof (CoglTextureGLVertex), + ctx->texture_vertices[0].t) ); + + cogl_enable (enable_flags); + + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + + /* Convert the vertices into an array of GLfixeds ready to pass to + OpenGL */ + for (i = 0, p = ctx->texture_vertices; i < n_vertices; i++, p++) + { + p->v[0] = vertices[i].x; + p->v[1] = vertices[i].y; + p->v[2] = vertices[i].z; + p->t[0] = vertices[i].tx * (x_span->size - x_span->waste) / x_span->size; + p->t[1] = vertices[i].ty * (y_span->size - y_span->waste) / y_span->size; + p->c[0] = (vertices[i].color.red << 16) / 0xff; + p->c[1] = (vertices[i].color.green << 16) / 0xff; + p->c[2] = (vertices[i].color.blue << 16) / 0xff; + p->c[3] = (vertices[i].color.alpha << 16) / 0xff; + } + + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices) ); + + /* Set the last color so that the cache of the alpha value will work + properly */ + if (use_color && n_vertices > 0) + cogl_color (&vertices[n_vertices - 1].color); +} diff --git a/gles/cogl-texture.h b/gles/cogl-texture.h new file mode 100644 index 00000000..e2401be0 --- /dev/null +++ b/gles/cogl-texture.h @@ -0,0 +1,63 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_TEXTURE_H +#define __COGL_TEXTURE_H + +#include "cogl-bitmap.h" + +typedef struct _CoglTexture CoglTexture; +typedef struct _CoglTexSliceSpan CoglTexSliceSpan; +typedef struct _CoglSpanIter CoglSpanIter; + +struct _CoglTexSliceSpan +{ + gint start; + gint size; + gint waste; +}; + +struct _CoglTexture +{ + guint ref_count; + CoglBitmap bitmap; + gboolean bitmap_owner; + GLenum gl_target; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + GArray *slice_x_spans; + GArray *slice_y_spans; + GArray *slice_gl_handles; + gint max_waste; + COGLenum min_filter; + COGLenum mag_filter; + gboolean is_foreign; +}; + +CoglTexture* +_cogl_texture_pointer_from_handle (CoglHandle handle); + +#endif /* __COGL_TEXTURE_H */ diff --git a/gles/cogl-util.c b/gles/cogl-util.c new file mode 100644 index 00000000..b024e763 --- /dev/null +++ b/gles/cogl-util.c @@ -0,0 +1,51 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" + +/** + * cogl_util_next_p2: + * @a: Value to get the next power + * + * Calculates the next power greater than @a. + * + * Return value: The next power after @a. + */ +int +cogl_util_next_p2 (int a) +{ + int rval=1; + + while(rval < a) + rval <<= 1; + + return rval; +} diff --git a/gles/cogl-util.h b/gles/cogl-util.h new file mode 100644 index 00000000..759da8cc --- /dev/null +++ b/gles/cogl-util.h @@ -0,0 +1,32 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_UTIL_H +#define __COGL_UTIL_H + +int +cogl_util_next_p2 (int a); + +#endif /* __COGL_UTIL_H */ diff --git a/gles/cogl.c b/gles/cogl.c index 495702ae..e4390889 100644 --- a/gles/cogl.c +++ b/gles/cogl.c @@ -32,16 +32,12 @@ #include <GLES/gl.h> #include <string.h> -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -#define PIXEL_TYPE GL_UNSIGNED_BYTE -#else -#define PIXEL_TYPE GL_UNSIGNED_INT_8_8_8_8_REV -#endif - -static gulong __enable_flags = 0; +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-context.h" -#define COGL_DEBUG 0 +/* GL error to string conversion */ #if COGL_DEBUG struct token_string { @@ -75,20 +71,6 @@ error_string(GLenum errorCode) } #endif -#if COGL_DEBUG -#define GE(x...) { \ - GLenum err; \ - (x); \ - fprintf(stderr, "%s\n", #x); \ - while ((err = glGetError()) != GL_NO_ERROR) { \ - fprintf(stderr, "glError: %s caught at %s:%u\n", \ - (char *)error_string(err), \ - __FILE__, __LINE__); \ - } \ -} -#else -#define GE(x) (x); -#endif CoglFuncPtr cogl_get_proc_address (const gchar* name) @@ -117,9 +99,6 @@ cogl_paint_init (const ClutterColor *color) glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glDisable (GL_LIGHTING); glDisable (GL_FOG); - - cogl_enable (CGL_ENABLE_BLEND); - glTexEnvx (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } /* FIXME: inline most of these */ @@ -173,53 +152,98 @@ cogl_rotate (gint angle, gint x, gint y, gint z) CLUTTER_INT_TO_FIXED(z)) ); } -void -cogl_enable (gulong flags) +static inline gboolean +cogl_toggle_flag (CoglContext *ctx, + gulong new_flags, + gulong flag, + GLenum gl_flag) { - /* This function essentially caches glEnable state() in the - * hope of lessening number GL traffic. - */ - if (flags & CGL_ENABLE_BLEND) + /* Toggles and caches a single enable flag on or off + * by comparing to current state + */ + if (new_flags & flag) { - if (!(__enable_flags & CGL_ENABLE_BLEND)) + if (!(ctx->enable_flags & flag)) { - GE( glEnable (GL_BLEND) ); - GE( glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ); + GE( glEnable (gl_flag) ); + ctx->enable_flags |= flag; + return TRUE; } - __enable_flags |= CGL_ENABLE_BLEND; } - else if (__enable_flags & CGL_ENABLE_BLEND) + else if (ctx->enable_flags & flag) { - GE( glDisable (GL_BLEND) ); - __enable_flags &= ~CGL_ENABLE_BLEND; + GE( glDisable (gl_flag) ); + ctx->enable_flags &= ~flag; } + + return FALSE; +} - if (flags & CGL_ENABLE_TEXTURE_2D) +static inline gboolean +cogl_toggle_client_flag (CoglContext *ctx, + gulong new_flags, + gulong flag, + GLenum gl_flag) +{ + /* Toggles and caches a single client-side enable flag + * on or off by comparing to current state + */ + if (new_flags & flag) { - if (!(__enable_flags & CGL_ENABLE_TEXTURE_2D)) - GE( glEnable (GL_TEXTURE_2D) ); - __enable_flags |= CGL_ENABLE_TEXTURE_2D; + if (!(ctx->enable_flags & flag)) + { + GE( glEnableClientState (gl_flag) ); + ctx->enable_flags |= flag; + return TRUE; + } } - else if (__enable_flags & CGL_ENABLE_TEXTURE_2D) + else if (ctx->enable_flags & flag) { - GE( glDisable (GL_TEXTURE_2D) ); - __enable_flags &= ~CGL_ENABLE_TEXTURE_2D; + GE( glDisableClientState (gl_flag) ); + ctx->enable_flags &= ~flag; } + + return FALSE; +} -#if 0 - if (flags & CGL_ENABLE_TEXTURE_RECT) - { - if (!(__enable_flags & CGL_ENABLE_TEXTURE_RECT)) - glEnable (GL_TEXTURE_RECTANGLE_); - - __enable_flags |= CGL_ENABLE_TEXTURE_RECT; - } - else if (__enable_flags & CGL_ENABLE_TEXTURE_RECT) +void +cogl_enable (gulong flags) +{ + /* This function essentially caches glEnable state() in the + * hope of lessening number GL traffic. + */ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (cogl_toggle_flag (ctx, flags, + COGL_ENABLE_BLEND, + GL_BLEND)) { - glDisable (GL_TEXTURE_RECTANGLE_); - __enable_flags &= ~CGL_ENABLE_TEXTURE_RECT; + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } -#endif + + cogl_toggle_flag (ctx, flags, + COGL_ENABLE_TEXTURE_2D, + GL_TEXTURE_2D); + + cogl_toggle_client_flag (ctx, flags, + COGL_ENABLE_VERTEX_ARRAY, + GL_VERTEX_ARRAY); + + cogl_toggle_client_flag (ctx, flags, + COGL_ENABLE_TEXCOORD_ARRAY, + GL_TEXTURE_COORD_ARRAY); + + cogl_toggle_client_flag (ctx, flags, + COGL_ENABLE_COLOR_ARRAY, + GL_COLOR_ARRAY); +} + +gulong +cogl_get_enable () +{ + _COGL_GET_CONTEXT (ctx, 0); + + return ctx->enable_flags; } void @@ -242,6 +266,8 @@ cogl_enable_depth_test (gboolean setting) void cogl_color (const ClutterColor *color) { + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + #if 0 /*HAVE_GLES_COLOR4UB*/ /* NOTE: seems SDK_OGLES-1.1_LINUX_PCEMULATION_2.02.22.0756 has this call @@ -267,25 +293,9 @@ cogl_color (const ClutterColor *color) (color->blue << 16) / 0xff, (color->alpha << 16) / 0xff)); #endif -} - -static inline void -cogl_rectangle_internal (ClutterFixed x, - ClutterFixed y, - ClutterFixed width, - ClutterFixed height) -{ - GLfixed rect_verts[8] = { - x, y, - x + width, y, - x, y + height, - x + width, y + height - }; - - GE( glEnableClientState (GL_VERTEX_ARRAY) ); - GE( glVertexPointer (2, GL_FIXED, 0, rect_verts) ); - GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); - GE( glDisableClientState (GL_VERTEX_ARRAY) ); + + /* Store alpha for proper blending enables */ + ctx->color_alpha = color->alpha; } void @@ -294,199 +304,57 @@ cogl_clip_set (ClutterFixed x_offset, ClutterFixed width, ClutterFixed height) { - GE( glEnable (GL_STENCIL_TEST) ); + if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES)) + { + GLfixed eqn_left[4] = { CFX_ONE, 0, 0, -x_offset }; + GLfixed eqn_right[4] = { -CFX_ONE, 0, 0, x_offset + width }; + GLfixed eqn_top[4] = { 0, CFX_ONE, 0, -y_offset }; + GLfixed eqn_bottom[4] = { 0, -CFX_ONE, 0, y_offset + height }; + + GE( glClipPlanex (GL_CLIP_PLANE0, eqn_left) ); + GE( glClipPlanex (GL_CLIP_PLANE1, eqn_right) ); + GE( glClipPlanex (GL_CLIP_PLANE2, eqn_top) ); + GE( glClipPlanex (GL_CLIP_PLANE3, eqn_bottom) ); + GE( glEnable (GL_CLIP_PLANE0) ); + GE( glEnable (GL_CLIP_PLANE1) ); + GE( glEnable (GL_CLIP_PLANE2) ); + GE( glEnable (GL_CLIP_PLANE3) ); + } + else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) + { + GE( glEnable (GL_STENCIL_TEST) ); - GE( glClearStencil (0) ); - GE( glClear (GL_STENCIL_BUFFER_BIT) ); + GE( glClearStencil (0) ); + GE( glClear (GL_STENCIL_BUFFER_BIT) ); - GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); - GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); + GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); + GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); - GE( glColor4x (CFX_ONE, CFX_ONE, CFX_ONE, CFX_ONE ) ); + GE( glColor4x (CFX_ONE, CFX_ONE, CFX_ONE, CFX_ONE ) ); - cogl_rectangle_internal (x_offset, y_offset, width, height); + cogl_fast_fill_rectanglex (x_offset, y_offset, width, height); - GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); - GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + } } void cogl_clip_unset (void) { - GE( glDisable (GL_STENCIL_TEST) ); -} - -gboolean -cogl_texture_can_size (COGLenum target, - COGLenum pixel_format, - COGLenum pixel_type, - int width, - int height) -{ - /* FIXME: How we get this is likely GLES implementation dependant. */ - return TRUE; -} - -void -cogl_texture_quad (gint x1, - gint x2, - gint y1, - gint y2, - ClutterFixed tx1, - ClutterFixed ty1, - ClutterFixed tx2, - ClutterFixed ty2) -{ -#define FIX CLUTTER_INT_TO_FIXED - - GLfixed quadVerts[] = { - FIX(x1), FIX(y1), 0, - FIX(x2), FIX(y1), 0, - FIX(x2), FIX(y2), 0, - FIX(x2), FIX(y2), 0, - FIX(x1), FIX(y2), 0, - FIX(x1), FIX(y1), 0 - }; - - GLfixed quadTex[] = { - tx1, ty1, - tx2, ty1, - tx2, ty2, - tx2, ty2, - tx1, ty2, - tx1, ty1 - }; - -#undef FIX - - GE( glEnableClientState(GL_VERTEX_ARRAY) ); - GE( glEnableClientState(GL_TEXTURE_COORD_ARRAY) ); - GE( glVertexPointer(3, GL_FIXED, 0, quadVerts) ); - GE( glTexCoordPointer(2, GL_FIXED, 0, quadTex) ); - GE( glDrawArrays(GL_TRIANGLES, 0, 6) ); - GE( glDisableClientState(GL_TEXTURE_COORD_ARRAY) ); - GE( glDisableClientState(GL_VERTEX_ARRAY) ); - - /* Note also see glDrawTexxOES for potential optimisation */ -} - -void -cogl_textures_create (guint num, COGLuint *textures) -{ - GE( glGenTextures (num, textures) ); -} - -void -cogl_textures_destroy (guint num, const COGLuint *textures) -{ - GE( glDeleteTextures (num, textures) ); -} - -void -cogl_texture_bind (COGLenum target, COGLuint texture) -{ - GE( glBindTexture (target, texture) ); -} - -void -cogl_texture_set_alignment (COGLenum target, - guint alignment, - guint row_length) -{ - /* GE( glPixelStorei (GL_UNPACK_ROW_LENGTH, row_length) ); */ - GE( glPixelStorei (GL_UNPACK_ALIGNMENT, alignment) ); -} - -void -cogl_texture_set_filters (COGLenum target, - COGLenum min_filter, - COGLenum max_filter) -{ - GE( glTexParameteri(target, GL_TEXTURE_MAG_FILTER, max_filter) ); - GE( glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filter) ); -} - -void -cogl_texture_set_wrap (COGLenum target, - COGLenum wrap_s, - COGLenum wrap_t) -{ - GE( glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap_s) ); - GE( glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap_s) ); -} - -void -cogl_texture_image_2d (COGLenum target, - COGLint internal_format, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels) -{ - GE( glTexImage2D (target, - 0, - format, /* HACK: For gles we set the internal_format equal - * to the pixel format. This is for RGB data (i.e - * jpgs) which seem to need a matching internal - * format rather than RGBA (which is used by GL) - *. - * This fix isn't ideal.. - */ - width, - height, - 0, - format, - type, - pixels) ); -} - -void -cogl_texture_sub_image_2d (COGLenum target, - gint xoff, - gint yoff, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels) -{ - GE( glTexSubImage2D (target, - 0, - xoff, - yoff, - width, - height, - format, - type, - pixels)); -} - -void -cogl_rectangle (gint x, - gint y, - guint width, - guint height) -{ - cogl_rectangle_internal (CLUTTER_INT_TO_FIXED (x), - CLUTTER_INT_TO_FIXED (y), - CLUTTER_INT_TO_FIXED (width), - CLUTTER_INT_TO_FIXED (height)); -} - -/* FIXME: Should use ClutterReal or Fixed */ -void -cogl_trapezoid (gint y1, - gint x11, - gint x21, - gint y2, - gint x12, - gint x22) -{ - /* FIXME */ + if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES)) + { + GE( glDisable (GL_CLIP_PLANE3) ); + GE( glDisable (GL_CLIP_PLANE2) ); + GE( glDisable (GL_CLIP_PLANE1) ); + GE( glDisable (GL_CLIP_PLANE0) ); + } + else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) + { + GE( glDisable (GL_STENCIL_TEST) ); + } } - void cogl_alpha_func (COGLenum func, ClutterFixed ref) @@ -591,11 +459,49 @@ cogl_setup_viewport (guint w, GE( glTranslatex (0, -CFX_ONE * height, 0) ); } +static void +_cogl_features_init () +{ + ClutterFeatureFlags flags = 0; + int stencil_bits = 0; + int max_clip_planes = 0; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + flags = COGL_FEATURE_TEXTURE_READ_PIXELS; + + GE( glGetIntegerv (GL_STENCIL_BITS, &stencil_bits) ); + if (stencil_bits > 0) + flags |= COGL_FEATURE_STENCIL_BUFFER; + + GE( glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); + if (max_clip_planes >= 4) + flags |= COGL_FEATURE_FOUR_CLIP_PLANES; + + ctx->feature_flags = flags; + ctx->features_cached = TRUE; +} + ClutterFeatureFlags cogl_get_features () { - /* Suck */ - return 0; + _COGL_GET_CONTEXT (ctx, 0); + + if (!ctx->features_cached) + _cogl_features_init (); + + return ctx->feature_flags; +} + +gboolean +cogl_features_available (CoglFeatureFlags features) +{ + _COGL_GET_CONTEXT (ctx, 0); + + if (!ctx->features_cached) + _cogl_features_init (); + + return (ctx->feature_flags & features) == features; } void @@ -654,39 +560,6 @@ cogl_fog_set (const ClutterColor *fog_color, glFogx (GL_FOG_END, (GLfixed) z_far); } -/* Offscreen - TODO: possible support from FBO's/PBuffers - * See; - * http://www.khronos.org/message_boards/viewtopic.php?t=589 - * http://www.gamedev.net/community/forums/topic.asp?topic_id=369739 - * - * Likely requires EGL 1.3 for eglBindTexImage -*/ -COGLuint -cogl_offscreen_create (COGLuint target_texture) -{ - return 0; -} - -void -cogl_offscreen_destroy (COGLuint offscreen_handle) -{ -} - -void -cogl_offscreen_redirect_start (COGLuint offscreen_handle, - gint width, - gint height) -{ -} - -void -cogl_offscreen_redirect_end (COGLuint offscreen_handle, - gint width, - gint height) -{ -} - - /* Shaders, no support on regular OpenGL 1.1 */ COGLhandle |