From 1d87ecc6a1be73e7d72c8c5ecb2b8268d5a0cd5e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 1 Dec 2009 16:22:45 +0000 Subject: [PATCH 01/83] materials: Get the right blend function for alpha The correct blend function for the alpha channel is: GL_ONE, GL_ONE_MINUS_SRC_ALPHA As per bug 1406. This fix was dropped when the switch to premultiplied alpha was merged. --- clutter/cogl/cogl/cogl-material.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/cogl/cogl/cogl-material.c b/clutter/cogl/cogl/cogl-material.c index 1a641a776..6c292ac67 100644 --- a/clutter/cogl/cogl/cogl-material.c +++ b/clutter/cogl/cogl/cogl-material.c @@ -106,7 +106,7 @@ _cogl_material_init_default_material (void) #ifndef HAVE_COGL_GLES material->blend_equation_rgb = GL_FUNC_ADD; material->blend_equation_alpha = GL_FUNC_ADD; - material->blend_src_factor_alpha = GL_SRC_ALPHA; + material->blend_src_factor_alpha = GL_ONE; material->blend_dst_factor_alpha = GL_ONE_MINUS_SRC_ALPHA; material->blend_constant[0] = 0; material->blend_constant[1] = 0; From 2f7ff4d3e363a0568e4051ab67c0df6f745d06d2 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 1 Dec 2009 16:16:18 +0000 Subject: [PATCH 02/83] stage: Add :use-alpha property The ClutterStage:use-alpha property is used to let a stage know that it should honour the alpha component of the ClutterStage:color property. If :use-alpha is set to FALSE the stage always uses the full opacity when clearing itself before a paint(); otherwise, the alpha value is used. --- clutter/clutter-stage.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 760bec193..6e3a41088 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -96,6 +96,7 @@ struct _ClutterStagePrivate guint is_user_resizable : 1; guint use_fog : 1; guint throttle_motion_events : 1; + guint use_alpha : 1; }; enum @@ -110,7 +111,8 @@ enum PROP_TITLE, PROP_USER_RESIZE, PROP_USE_FOG, - PROP_FOG + PROP_FOG, + PROP_USE_ALPHA }; enum @@ -250,7 +252,9 @@ clutter_stage_paint (ClutterActor *self) priv->color.red, priv->color.green, priv->color.blue, - priv->color.alpha); + priv->use_alpha + ? priv->color.alpha + : 255); cogl_clear (&stage_color, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH); @@ -289,7 +293,8 @@ clutter_stage_pick (ClutterActor *self, */ clutter_container_foreach (CLUTTER_CONTAINER (self), - CLUTTER_CALLBACK (clutter_actor_paint), NULL); + CLUTTER_CALLBACK (clutter_actor_paint), + NULL); } static void @@ -660,6 +665,11 @@ clutter_stage_set_property (GObject *object, clutter_stage_set_fog (stage, g_value_get_boxed (value)); break; + case PROP_USE_ALPHA: + stage->priv->use_alpha = g_value_get_boolean (value); + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -712,6 +722,10 @@ clutter_stage_get_property (GObject *gobject, g_value_set_boxed (value, &priv->fog); break; + case PROP_USE_ALPHA: + g_value_set_boolean (value, priv->use_alpha); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -939,6 +953,24 @@ clutter_stage_class_init (ClutterStageClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_FOG, pspec); + /** + * ClutterStage:use-alpha: + * + * Whether the #ClutterStage should honour the alpha component of the + * #ClutterStage:color property when painting. If Clutter is run under + * a compositing manager this will result in the stage being blended + * with the underlying window(s) + * + * Since: 1.2 + */ + pspec = g_param_spec_boolean ("use-alpha", + "Use Alpha", + "Whether to honour the alpha component of " + "the stage color", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_USE_ALPHA, pspec); + /** * ClutterStage::fullscreen * @stage: the stage which was fullscreened From e6ca2d891a2c48c91db45a0c2f5ce84d5e6621e1 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 1 Dec 2009 16:18:39 +0000 Subject: [PATCH 03/83] glx: Always request an ARGB visual When requesting the GLXFBConfig for creating the GLX context, we should always request one that links to an ARGB visual instead of a plain RGB one. By using an ARGB visual we allow the ClutterStage:use-alpha property to work as intended when running Clutter under a compositing manager. The default behaviour of requesting an ARGB visual can be disabled by using the: CLUTTER_DISABLE_ARGB_VISUAL Environment variable. --- clutter/glx/clutter-backend-glx.c | 128 ++++++++++++++++++++++++------ clutter/glx/clutter-backend-glx.h | 5 +- clutter/glx/clutter-glx.h | 5 -- clutter/glx/clutter-stage-glx.c | 13 +-- clutter/x11/clutter-backend-x11.c | 15 +++- clutter/x11/clutter-x11.h | 2 + 6 files changed, 125 insertions(+), 43 deletions(-) diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index 8870037b3..ce1256e6c 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -56,7 +56,7 @@ G_DEFINE_TYPE (ClutterBackendGLX, clutter_backend_glx, CLUTTER_TYPE_BACKEND_X11) /* singleton object */ static ClutterBackendGLX *backend_singleton = NULL; -static gchar *clutter_vblank_name = NULL; +static gchar *clutter_vblank_name = NULL; #ifdef __linux__ #define DRM_VBLANK_RELATIVE 0x1; @@ -354,6 +354,20 @@ clutter_backend_glx_get_features (ClutterBackend *backend) return flags; } +enum +{ + DRAWABLE_TYPE = 0, + RENDER_TYPE = 2, + DOUBLE_BUFFER = 4, + RED_SIZE = 6, + GREEN_SIZE = 8, + BLUE_SIZE = 10, + ALPHA_SIZE = 12, + DEPTH_SIZE = 14, + STENCIL_SIZE = 16, + TRANSPARENT_TYPE = 18 +}; + /* It seems the GLX spec never defined an invalid GLXFBConfig that * we could overload as an indication of error, so we have to return * an explicit boolean status. */ @@ -362,30 +376,43 @@ _clutter_backend_glx_get_fbconfig (ClutterBackendGLX *backend_glx, GLXFBConfig *config) { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend_glx); - int attributes[] = { - GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, - GLX_RENDER_TYPE, GLX_RGBA_BIT, - GLX_DOUBLEBUFFER, GL_TRUE, - GLX_RED_SIZE, 1, - GLX_GREEN_SIZE, 1, - GLX_BLUE_SIZE, 1, - GLX_ALPHA_SIZE, 1, - GLX_DEPTH_SIZE, 1, - GLX_STENCIL_SIZE, 1, + GLXFBConfig *configs = NULL; + gboolean retval = FALSE; + gboolean use_argb = clutter_x11_has_argb_visuals (); + int n_configs, i; + static int attributes[] = { + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DOUBLEBUFFER, GL_TRUE, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_ALPHA_SIZE, 1, + GLX_DEPTH_SIZE, 1, + GLX_STENCIL_SIZE, 1, + GLX_TRANSPARENT_TYPE, GLX_NONE, None }; - GLXFBConfig *configs = NULL; - int n_configs; if (backend_x11->xdpy == None || backend_x11->xscreen == None) return FALSE; - if (backend_glx->found_fbconfig) + if (backend_glx->found_fbconfig > 0) { - *config = backend_glx->fbconfig; + if (use_argb && backend_glx->found_fbconfig == 2) + *config = backend_glx->fbconfig_rgba; + else + *config = backend_glx->fbconfig_rgb; + return TRUE; } + if (use_argb) + { + attributes[ALPHA_SIZE] = 8; + attributes[TRANSPARENT_TYPE] = GLX_TRANSPARENT_RGB; + } + CLUTTER_NOTE (BACKEND, "Retrieving GL fbconfig, dpy: %p, xscreen; %p (%d)", backend_x11->xdpy, @@ -396,16 +423,64 @@ _clutter_backend_glx_get_fbconfig (ClutterBackendGLX *backend_glx, backend_x11->xscreen_num, attributes, &n_configs); - if (configs) + if (!configs) + return FALSE; + + if (!use_argb) { *config = configs[0]; - backend_glx->found_fbconfig = TRUE; - backend_glx->fbconfig = configs[0]; - XFree (configs); - return TRUE; + + backend_glx->found_fbconfig = 1; + backend_glx->fbconfig_rgb = configs[0]; + + retval = TRUE; + + goto out; } - else - return FALSE; + + for (i = 0; i < n_configs; i++) + { + XVisualInfo *vinfo; + + vinfo = glXGetVisualFromFBConfig (backend_x11->xdpy, configs[i]); + if (vinfo == None) + continue; + + if (vinfo->depth == 32 && + (vinfo->red_mask == 0xff0000 && + vinfo->green_mask == 0x00ff00 && + vinfo->blue_mask == 0x0000ff)) + { + CLUTTER_NOTE (BACKEND, "Found GLX visual ARGB [index:%d]", i); + + *config = configs[i]; + + backend_glx->found_fbconfig = 2; + backend_glx->fbconfig_rgba = configs[i]; + + retval = TRUE; + + goto out; + } + } + + /* XXX - we might add a warning here */ + if (use_argb && !backend_glx->found_fbconfig != 2) + { + CLUTTER_NOTE (BACKEND, "ARGB visual requested, but none found"); + + *config = configs[0]; + + backend_glx->found_fbconfig = 1; + backend_glx->fbconfig_rgb = configs[0]; + + retval = TRUE; + } + +out: + XFree (configs); + + return retval; } static XVisualInfo * @@ -436,12 +511,14 @@ clutter_backend_glx_create_context (ClutterBackend *backend, { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, - "Unable to find suitable fbconfig for GL context"); + "Unable to find a suitable GLXFBConfig for " + "the GLX context"); return FALSE; } - CLUTTER_NOTE (GL, "Creating GL Context (display: %p)", + CLUTTER_NOTE (GL, "Creating GLX Context (display: %p)", backend_x11->xdpy); + backend_glx->gl_context = glXCreateNewContext (backend_x11->xdpy, config, @@ -460,7 +537,8 @@ clutter_backend_glx_create_context (ClutterBackend *backend, is_direct = glXIsDirect (backend_x11->xdpy, backend_glx->gl_context); - CLUTTER_NOTE (GL, "Setting %s context", + CLUTTER_NOTE (GL, + "Setting %s context", is_direct ? "direct" : "indirect"); _cogl_set_indirect_context (!is_direct); } diff --git a/clutter/glx/clutter-backend-glx.h b/clutter/glx/clutter-backend-glx.h index 2bd380813..e7d6d9836 100644 --- a/clutter/glx/clutter-backend-glx.h +++ b/clutter/glx/clutter-backend-glx.h @@ -63,8 +63,9 @@ struct _ClutterBackendGLX ClutterBackendX11 parent_instance; /* Single context for all wins */ - gboolean found_fbconfig; - GLXFBConfig fbconfig; + gint found_fbconfig; + GLXFBConfig fbconfig_rgb; + GLXFBConfig fbconfig_rgba; GLXContext gl_context; /* Vblank stuff */ diff --git a/clutter/glx/clutter-glx.h b/clutter/glx/clutter-glx.h index bb2455b8a..967cfec90 100644 --- a/clutter/glx/clutter-glx.h +++ b/clutter/glx/clutter-glx.h @@ -43,9 +43,4 @@ #include #include -G_BEGIN_DECLS - - -G_END_DECLS - #endif /* __CLUTTER_GLX_H__ */ diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index 9c1105b2e..f770c88d3 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -105,18 +105,11 @@ clutter_stage_glx_realize (ClutterStageWindow *stage_window) { XSetWindowAttributes xattr; unsigned long mask; - GLXFBConfig config; XVisualInfo *xvisinfo; CLUTTER_NOTE (MISC, "Creating stage X window"); - if (!_clutter_backend_glx_get_fbconfig (backend_glx, &config)) - { - g_critical ("Unable to find suitable FBConfig to realize stage."); - return FALSE; - } - - xvisinfo = glXGetVisualFromFBConfig (backend_x11->xdpy, config); + xvisinfo = clutter_backend_x11_get_visual_info (backend_x11); if (xvisinfo == NULL) { g_critical ("Unable to find suitable GL visual."); @@ -145,9 +138,9 @@ clutter_stage_glx_realize (ClutterStageWindow *stage_window) XFree (xvisinfo); } - if (clutter_x11_has_event_retrieval()) + if (clutter_x11_has_event_retrieval ()) { - if (clutter_x11_has_xinput()) + if (clutter_x11_has_xinput ()) { XSelectInput (backend_x11->xdpy, stage_x11->xwin, StructureNotifyMask | diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index ae6ff824c..ec9afebea 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -97,6 +97,7 @@ static ClutterBackendX11 *backend_singleton = NULL; /* various flags corresponding to pre init setup calls */ static gboolean _no_xevent_retrieval = FALSE; static gboolean clutter_enable_xinput = FALSE; +static gboolean clutter_enable_argb = TRUE; static Display *_foreign_dpy = NULL; /* options */ @@ -124,6 +125,13 @@ clutter_backend_x11_pre_parse (ClutterBackend *backend, env_string = NULL; } + env_string = g_getenv ("CLUTTER_DISABLE_ARGB_VISUAL"); + if (env_string) + { + clutter_enable_argb = FALSE; + env_string = NULL; + } + return TRUE; } @@ -980,6 +988,12 @@ clutter_x11_has_composite_extension (void) return have_composite; } +gboolean +clutter_x11_has_argb_visuals (void) +{ + return clutter_enable_argb; +} + XVisualInfo * clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11) { @@ -993,4 +1007,3 @@ clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11) return NULL; } - diff --git a/clutter/x11/clutter-x11.h b/clutter/x11/clutter-x11.h index 58fc7a63e..2b960578c 100644 --- a/clutter/x11/clutter-x11.h +++ b/clutter/x11/clutter-x11.h @@ -124,6 +124,8 @@ gboolean clutter_x11_has_xinput (void); gboolean clutter_x11_has_composite_extension (void); +gboolean clutter_x11_has_argb_visuals (void); + Time clutter_x11_get_current_event_time (void); G_END_DECLS From 18860eb16f36eb62b94aee353b4e2ace76e2fffa Mon Sep 17 00:00:00 2001 From: Bastian Winkler Date: Thu, 3 Dec 2009 15:38:05 +0000 Subject: [PATCH 04/83] flow-layout: Use the natural size Fix a copy-and-paste thinko where the cell size was computed using the minimum size instead of the natural size. For actors with a minimum size of zero, like Textures, this implied always a zero allocation. Signed-off-by: Emmanuele Bassi --- clutter/clutter-flow-layout.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-flow-layout.c b/clutter/clutter-flow-layout.c index 611a46b96..4e649e6d7 100644 --- a/clutter/clutter-flow-layout.c +++ b/clutter/clutter-flow-layout.c @@ -554,7 +554,7 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, clutter_actor_get_preferred_width (child, item_height, &child_min, &child_natural); - item_width = MIN (item_width, child_min); + item_width = MIN (item_width, child_natural); clutter_actor_get_preferred_height (child, item_width, &child_min, @@ -593,7 +593,7 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, clutter_actor_get_preferred_width (child, item_height, &child_min, &child_natural); - item_width = MIN (item_width, child_min); + item_width = MIN (item_width, child_natural); clutter_actor_get_preferred_height (child, item_width, &child_min, From c9b7efec96636e9368a8dfb76440d1b89c618c99 Mon Sep 17 00:00:00 2001 From: Johan Bilien Date: Wed, 11 Nov 2009 20:40:57 -0500 Subject: [PATCH 05/83] Add a cache of size requests clutter_actor_get_preferred_width/height currently caches only one size requests, for a given height / width. It's common for a layout manager to call get_preferred_width with 2 different heights during the same allocation cycle. Typically once in the size request, once in the allocation. If clutter_actor_get_preferred_width is called alternatively with 2 different for_height, the cache is totally inefficient, and we end up always querying the actor size even when the actor does not need a re-allocation. http://bugzilla.openedhand.com/show_bug.cgi?id=1876 Signed-off-by: Emmanuele Bassi --- clutter/clutter-actor.c | 137 ++++++++++++++++++++++++++++++++++------ 1 file changed, 117 insertions(+), 20 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index fcab0b6a1..b81a26e47 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -234,6 +234,18 @@ struct _AnchorCoord } v; }; +/* 3 entries should be a good compromise, few layout managers + * will ask for 3 different preferred size in each allocation cycle */ +#define N_CACHED_SIZE_REQUESTS 3 +typedef struct _SizeRequest SizeRequest; +struct _SizeRequest +{ + guint age; + gfloat for_size; + gfloat min_size; + gfloat natural_size; +}; + /* Internal enum used to control mapped state update. This is a hint * which indicates when to do something other than just enforce * invariants. @@ -262,14 +274,17 @@ struct _ClutterActorPrivate /* request mode */ ClutterRequestMode request_mode; - /* our cached request width is for this height */ - gfloat request_width_for_height; - gfloat request_min_width; - gfloat request_natural_width; + /* our cached size requests for different width / height */ + SizeRequest width_requests[N_CACHED_SIZE_REQUESTS]; + SizeRequest height_requests[N_CACHED_SIZE_REQUESTS]; - /* our cached request height is for this width */ - gfloat request_height_for_width; + /* An age of 0 means the entry is not set */ + guint cached_height_age; + guint cached_width_age; + + gfloat request_min_width; gfloat request_min_height; + gfloat request_natural_width; gfloat request_natural_height; ClutterActorBox allocation; @@ -1663,6 +1678,12 @@ clutter_actor_real_queue_relayout (ClutterActor *self) priv->needs_height_request = TRUE; priv->needs_allocation = TRUE; + /* reset the cached size requests */ + memset (priv->width_requests, 0, + N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest)); + memset (priv->height_requests, 0, + N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest)); + /* always repaint also (no-op if not mapped) */ clutter_actor_queue_redraw (self); @@ -4392,6 +4413,9 @@ clutter_actor_init (ClutterActor *self) priv->needs_height_request = TRUE; priv->needs_allocation = TRUE; + priv->cached_width_age = 1; + priv->cached_height_age = 1; + priv->opacity_parent = NULL; priv->enable_model_view_transform = TRUE; @@ -4586,6 +4610,40 @@ clutter_actor_get_preferred_size (ClutterActor *self, *natural_height_p = natural_height; } +/* looks for a cached size request for this for_size. If not + * found, returns the oldest entry so it can be overwritten */ +static gboolean +_clutter_actor_get_cached_size_request (gfloat for_size, + SizeRequest *cached_size_requests, + SizeRequest **result) +{ + gboolean found_free_cache; + guint i; + + found_free_cache = FALSE; + *result = &cached_size_requests[0]; + + for (i = 0; i < N_CACHED_SIZE_REQUESTS; i++) + { + SizeRequest *sr; + + sr = &cached_size_requests[i]; + + if (sr->age > 0 && + sr->for_size == for_size) + { + *result = sr; + return TRUE; + } + else if (sr->age < (*result)->age) + { + *result = sr; + } + } + + return FALSE; +} + /** * clutter_actor_get_preferred_width: * @self: A #ClutterActor @@ -4616,14 +4674,23 @@ clutter_actor_get_preferred_width (ClutterActor *self, { ClutterActorClass *klass; ClutterActorPrivate *priv; + gboolean found_in_cache; + SizeRequest *cached_size_request; g_return_if_fail (CLUTTER_IS_ACTOR (self)); klass = CLUTTER_ACTOR_GET_CLASS (self); priv = self->priv; - if (priv->needs_width_request || - priv->request_width_for_height != for_height) + found_in_cache = FALSE; + cached_size_request = &priv->width_requests[0]; + + if (!priv->needs_width_request) + found_in_cache = _clutter_actor_get_cached_size_request (for_height, + priv->width_requests, + &cached_size_request); + + if (!found_in_cache) { gfloat min_width, natural_width; @@ -4641,16 +4708,21 @@ clutter_actor_get_preferred_width (ClutterActor *self, if (natural_width < min_width) natural_width = min_width; - if (!priv->min_width_set) - priv->request_min_width = min_width; + cached_size_request->min_size = min_width; + cached_size_request->natural_size = natural_width; + cached_size_request->for_size = for_height; + cached_size_request->age = priv->cached_width_age; - if (!priv->natural_width_set) - priv->request_natural_width = natural_width; - - priv->request_width_for_height = for_height; + priv->cached_width_age ++; priv->needs_width_request = FALSE; } + if (!priv->min_width_set) + priv->request_min_width = cached_size_request->min_size; + + if (!priv->natural_width_set) + priv->request_natural_width = cached_size_request->natural_size; + if (min_width_p) *min_width_p = priv->request_min_width; @@ -4687,20 +4759,29 @@ clutter_actor_get_preferred_height (ClutterActor *self, { ClutterActorClass *klass; ClutterActorPrivate *priv; + gboolean found_in_cache; + SizeRequest *cached_size_request; g_return_if_fail (CLUTTER_IS_ACTOR (self)); klass = CLUTTER_ACTOR_GET_CLASS (self); priv = self->priv; - if (priv->needs_height_request || - priv->request_height_for_width != for_width) + found_in_cache = FALSE; + cached_size_request = &priv->height_requests[0]; + + if (!priv->needs_height_request) + found_in_cache = _clutter_actor_get_cached_size_request (for_width, + priv->height_requests, + &cached_size_request); + + if (!found_in_cache) { gfloat min_height, natural_height; min_height = natural_height = 0; - CLUTTER_NOTE (LAYOUT, "Width request for %.2f px", for_width); + CLUTTER_NOTE (LAYOUT, "Height request for %.2f px", for_width); klass->get_preferred_height (self, for_width, &min_height, @@ -4713,15 +4794,31 @@ clutter_actor_get_preferred_height (ClutterActor *self, natural_height = min_height; if (!priv->min_height_set) - priv->request_min_height = min_height; + { + priv->request_min_height = min_height; + } if (!priv->natural_height_set) - priv->request_natural_height = natural_height; + { + priv->request_natural_height = natural_height; + } + + cached_size_request->min_size = min_height; + cached_size_request->natural_size = natural_height; + cached_size_request->for_size = for_width; + cached_size_request->age = priv->cached_height_age; + + priv->cached_height_age ++; - priv->request_height_for_width = for_width; priv->needs_height_request = FALSE; } + if (!priv->min_height_set) + priv->request_min_height = cached_size_request->min_size; + + if (!priv->natural_height_set) + priv->request_natural_height = cached_size_request->natural_size; + if (min_height_p) *min_height_p = priv->request_min_height; From 9ccdf2eb02a0d0155e27d5f9270bca6b72c08359 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 4 Dec 2009 16:54:22 +0000 Subject: [PATCH 06/83] box: Add relative packing methods ClutterBox should provide some convenience functions to pack a new child at a given position, either an absolute index or relative to a sibling. --- clutter/clutter-box.c | 221 +++++++++++++++++---- clutter/clutter-box.h | 16 ++ doc/reference/clutter/clutter-sections.txt | 3 + tests/interactive/test-bin-layout.c | 1 - 4 files changed, 203 insertions(+), 38 deletions(-) diff --git a/clutter/clutter-box.c b/clutter/clutter-box.c index f9a09d19b..67c52d096 100644 --- a/clutter/clutter-box.c +++ b/clutter/clutter-box.c @@ -661,48 +661,21 @@ clutter_box_packv (ClutterBox *box, } } -/** - * clutter_box_pack: - * @box: a #ClutterBox - * @actor: a #ClutterActor - * @first_property: the name of the first property to set, or %NULL - * @Varargs: a list of property name and value pairs, terminated by %NULL - * - * Adds @actor to @box and sets layout properties at the same time, - * if the #ClutterLayoutManager used by @box has them - * - * This function is a wrapper around clutter_container_add_actor() - * and clutter_layout_manager_child_set() - * - * Language bindings should use the vector-based clutter_box_addv() - * variant instead - * - * Since: 1.2 - */ -void -clutter_box_pack (ClutterBox *box, - ClutterActor *actor, - const gchar *first_property, - ...) +static inline void +clutter_box_set_property_valist (ClutterBox *box, + ClutterActor *actor, + const gchar *first_property, + va_list var_args) { - ClutterBoxPrivate *priv; - ClutterContainer *container; + ClutterContainer *container = CLUTTER_CONTAINER (box); + ClutterBoxPrivate *priv = box->priv; ClutterLayoutMeta *meta; GObjectClass *klass; const gchar *pname; - va_list var_args; - g_return_if_fail (CLUTTER_IS_BOX (box)); - g_return_if_fail (CLUTTER_IS_ACTOR (actor)); - - container = CLUTTER_CONTAINER (box); - clutter_container_add_actor (container, actor); - - if (first_property == NULL || *first_property == '\0') + if (priv->manager == NULL) return; - priv = box->priv; - meta = clutter_layout_manager_get_child_meta (priv->manager, container, actor); @@ -712,8 +685,6 @@ clutter_box_pack (ClutterBox *box, klass = G_OBJECT_GET_CLASS (meta); - va_start (var_args, first_property); - pname = first_property; while (pname) { @@ -761,7 +732,183 @@ clutter_box_pack (ClutterBox *box, pname = va_arg (var_args, gchar*); } +} +/** + * clutter_box_pack: + * @box: a #ClutterBox + * @actor: a #ClutterActor + * @first_property: the name of the first property to set, or %NULL + * @Varargs: a list of property name and value pairs, terminated by %NULL + * + * Adds @actor to @box and sets layout properties at the same time, + * if the #ClutterLayoutManager used by @box has them + * + * This function is a wrapper around clutter_container_add_actor() + * and clutter_layout_manager_child_set() + * + * Language bindings should use the vector-based clutter_box_addv() + * variant instead + * + * Since: 1.2 + */ +void +clutter_box_pack (ClutterBox *box, + ClutterActor *actor, + const gchar *first_property, + ...) +{ + va_list var_args; + + g_return_if_fail (CLUTTER_IS_BOX (box)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + clutter_container_add_actor (CLUTTER_CONTAINER (box), actor); + + if (first_property == NULL || *first_property == '\0') + return; + + va_start (var_args, first_property); + clutter_box_set_property_valist (box, actor, first_property, var_args); + va_end (var_args); +} + +/** + * clutter_box_pack_after: + * @box: a #ClutterBox + * @actor: a #ClutterActor + * @sibling: (allow none): a #ClutterActor or %NULL + * @first_property: the name of the first property to set, or %NULL + * @Varargs: a list of property name and value pairs, terminated by %NULL + * + * Adds @actor to @box, placing it after @sibling, and sets layout + * properties at the same time, if the #ClutterLayoutManager used by + * @box supports them + * + * If @sibling is %NULL then @actor is placed at the end of the + * list of children, to be allocated and painted after every other child + * + * This function is a wrapper around clutter_container_add_actor(), + * clutter_container_raise_child() and clutter_layout_manager_child_set() + * + * Since: 1.2 + */ +void +clutter_box_pack_after (ClutterBox *box, + ClutterActor *actor, + ClutterActor *sibling, + const gchar *first_property, + ...) +{ + va_list var_args; + + g_return_if_fail (CLUTTER_IS_BOX (box)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling)); + + clutter_container_add_actor (CLUTTER_CONTAINER (box), actor); + clutter_container_raise_child (CLUTTER_CONTAINER (box), actor, sibling); + + if (first_property == NULL || *first_property == '\0') + return; + + va_start (var_args, first_property); + clutter_box_set_property_valist (box, actor, first_property, var_args); + va_end (var_args); +} + +/** + * clutter_box_pack_before: + * @box: a #ClutterBox + * @actor: a #ClutterActor + * @sibling: (allow none): a #ClutterActor or %NULL + * @first_property: the name of the first property to set, or %NULL + * @Varargs: a list of property name and value pairs, terminated by %NULL + * + * Adds @actor to @box, placing it before @sibling, and sets layout + * properties at the same time, if the #ClutterLayoutManager used by + * @box supports them + * + * If @sibling is %NULL then @actor is placed at the beginning of the + * list of children, to be allocated and painted below every other child + * + * This function is a wrapper around clutter_container_add_actor(), + * clutter_container_lower_child() and clutter_layout_manager_child_set() + * + * Since: 1.2 + */ +void +clutter_box_pack_before (ClutterBox *box, + ClutterActor *actor, + ClutterActor *sibling, + const gchar *first_property, + ...) +{ + va_list var_args; + + g_return_if_fail (CLUTTER_IS_BOX (box)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling)); + + clutter_container_add_actor (CLUTTER_CONTAINER (box), actor); + clutter_container_lower_child (CLUTTER_CONTAINER (box), actor, sibling); + + if (first_property == NULL || *first_property == '\0') + return; + + va_start (var_args, first_property); + clutter_box_set_property_valist (box, actor, first_property, var_args); + va_end (var_args); +} + +/** + * clutter_box_pack_at: + * @box: a #ClutterBox + * @actor: a #ClutterActor + * @position: the position to insert the @actor at + * @first_property: the name of the first property to set, or %NULL + * @Varargs: a list of property name and value pairs, terminated by %NULL + * + * Adds @actor to @box, placing it at @position, and sets layout + * properties at the same time, if the #ClutterLayoutManager used by + * @box supports them + * + * If @position is a negative number, or is larger than the number of + * children of @box, the new child is added at the end of the list of + * children + * + * Since: 1.2 + */ +void +clutter_box_pack_at (ClutterBox *box, + ClutterActor *actor, + gint position, + const gchar *first_property, + ...) +{ + ClutterBoxPrivate *priv; + va_list var_args; + + g_return_if_fail (CLUTTER_IS_BOX (box)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + priv = box->priv; + + /* this is really clutter_box_add() with a different insert() */ + priv->children = g_list_insert (priv->children, + actor, + position); + + clutter_actor_set_parent (actor, CLUTTER_ACTOR (box)); + clutter_actor_queue_relayout (actor); + + g_signal_emit_by_name (box, "actor-added", actor); + + if (first_property == NULL || *first_property == '\0') + return; + + va_start (var_args, first_property); + clutter_box_set_property_valist (box, actor, first_property, var_args); va_end (var_args); } diff --git a/clutter/clutter-box.h b/clutter/clutter-box.h index a44c274c1..584ea44c5 100644 --- a/clutter/clutter-box.h +++ b/clutter/clutter-box.h @@ -57,6 +57,22 @@ void clutter_box_packv (ClutterBox *box, const gchar * const properties[], const GValue *values); +void clutter_box_pack_after (ClutterBox *box, + ClutterActor *actor, + ClutterActor *sibling, + const gchar *first_property, + ...) G_GNUC_NULL_TERMINATED; +void clutter_box_pack_before (ClutterBox *box, + ClutterActor *actor, + ClutterActor *sibling, + const gchar *first_property, + ...) G_GNUC_NULL_TERMINATED; +void clutter_box_pack_at (ClutterBox *box, + ClutterActor *actor, + gint position, + const gchar *first_property, + ...) G_GNUC_NULL_TERMINATED; + G_END_DECLS #endif /* __CLUTTER_BOX_H__ */ diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index d96a6c07d..b4c84fb53 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1836,6 +1836,9 @@ clutter_box_get_color clutter_box_pack clutter_box_packv +clutter_box_pack_after +clutter_box_pack_before +clutter_box_pack_at CLUTTER_TYPE_BOX diff --git a/tests/interactive/test-bin-layout.c b/tests/interactive/test-bin-layout.c index 84fda7b54..d61796e64 100644 --- a/tests/interactive/test-bin-layout.c +++ b/tests/interactive/test-bin-layout.c @@ -127,7 +127,6 @@ test_bin_layout_main (int argc, char *argv[]) clutter_actor_lower_bottom (rect); clutter_actor_set_name (rect, "background"); - { ClutterActor *tex; GError *error; From cf62b8fe4ab0d7cca360077843a9e213540e2c4d Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 4 Dec 2009 17:38:26 +0000 Subject: [PATCH 07/83] actor: Add debugging notes for size cache Add a note for cache hits, and another one for cache misses. --- clutter/clutter-actor.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index b81a26e47..9e7fee728 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -4632,6 +4632,7 @@ _clutter_actor_get_cached_size_request (gfloat for_size, if (sr->age > 0 && sr->for_size == for_size) { + CLUTTER_NOTE (LAYOUT, "Size cache hit for size: %.2f", for_size); *result = sr; return TRUE; } @@ -4641,6 +4642,8 @@ _clutter_actor_get_cached_size_request (gfloat for_size, } } + CLUTTER_NOTE (LAYOUT, "Size cache miss for size: %.2f", for_size); + return FALSE; } From b33b6287a1d3d3b07bbb845e79dfc4e2162c5c77 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 4 Dec 2009 17:39:04 +0000 Subject: [PATCH 08/83] tests: Clean up the BoxLayout interactive test --- tests/interactive/test-box-layout.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/interactive/test-box-layout.c b/tests/interactive/test-box-layout.c index e45631880..a031e269d 100644 --- a/tests/interactive/test-box-layout.c +++ b/tests/interactive/test-box-layout.c @@ -32,6 +32,8 @@ "Press q\t\342\236\236\tQuit" static ClutterActor *hover_actor = NULL; +static ClutterActor *box = NULL; +static ClutterActor *label = NULL; static guint last_index = 0; static void @@ -88,7 +90,7 @@ leave_event (ClutterActor *actor, static gboolean button_release_event (ClutterActor *actor, ClutterEvent *event, - ClutterBoxLayout *box) + ClutterBoxLayout *layout) { gboolean xfill, yfill; ClutterBoxAlignment xalign, yalign; @@ -98,14 +100,14 @@ button_release_event (ClutterActor *actor, if (button == 1) { - clutter_box_layout_get_fill (box, actor, &xfill, &yfill); - clutter_box_layout_set_fill (box, actor, + clutter_box_layout_get_fill (layout, actor, &xfill, &yfill); + clutter_box_layout_set_fill (layout, actor, xfill ? FALSE : TRUE, yfill ? FALSE : TRUE); } else { - clutter_box_layout_get_alignment (box, actor, &xalign, &yalign); + clutter_box_layout_get_alignment (layout, actor, &xalign, &yalign); if (xalign < 2) xalign += 1; @@ -117,14 +119,14 @@ button_release_event (ClutterActor *actor, else yalign = 0; - clutter_box_layout_set_alignment (box, actor, xalign, yalign); + clutter_box_layout_set_alignment (layout, actor, xalign, yalign); } return TRUE; } static void -add_actor (ClutterBoxLayout *box, +add_actor (ClutterBoxLayout *layout, guint index_) { ClutterActor *rect; @@ -138,7 +140,7 @@ add_actor (ClutterBoxLayout *box, rect = clutter_rectangle_new_with_color (&color); clutter_actor_set_size (rect, 32, 64); - clutter_box_layout_pack (box, rect, expand, + clutter_box_layout_pack (layout, rect, expand, FALSE, /* x-fill */ FALSE, /* y-fill */ CLUTTER_BOX_ALIGNMENT_CENTER, @@ -152,7 +154,7 @@ add_actor (ClutterBoxLayout *box, g_signal_connect (rect, "leave-event", G_CALLBACK (leave_event), NULL); g_signal_connect (rect, "button-release-event", G_CALLBACK (button_release_event), - box); + layout); expand = !expand; } @@ -207,18 +209,21 @@ static void stage_size_changed_cb (ClutterActor *stage, const ClutterActorBox *allocation, ClutterAllocationFlags flags, - ClutterActor *box) + gpointer dummy G_GNUC_UNUSED) { gfloat width, height; clutter_actor_box_get_size (allocation, &width, &height); clutter_actor_set_size (box, width - 100, height - 100); + + clutter_actor_set_y (label, + height - clutter_actor_get_height (label) - 8); } G_MODULE_EXPORT int test_box_layout_main (int argc, char *argv[]) { - ClutterActor *stage, *box, *label; + ClutterActor *stage; ClutterLayoutManager *layout; gint i; @@ -242,7 +247,7 @@ test_box_layout_main (int argc, char *argv[]) layout); g_signal_connect (stage, "allocation-changed", G_CALLBACK (stage_size_changed_cb), - box); + NULL); label = clutter_text_new_with_text ("Sans 12px", INSTRUCTIONS); clutter_container_add_actor (CLUTTER_CONTAINER (stage), label); From c4f27b1556b65464fd1f53749d32db9adab97942 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Oct 2009 16:41:25 +0100 Subject: [PATCH 09/83] actor: Add a NO_LAYOUT flag for actors Some actor implementation might avoid imposing any layout on their children. The Actor base class usually assumes some sort of layout management is in place, so it will queue relayouts when, for instance, an actor is shown or is hidden. If the parent of the actor does not impose any layout, though, showing or hiding one of its children will not affect the layout of the others. An example of this kind of container is ClutterGroup. By adding a new Actor flag, CLUTTER_ACTOR_NO_LAYOUT, and by making the Group actor set it on itself, the Actor base class can now decide whether or not to queue a relayout. The flag is not meant to be used by application code, and should only be set when implementing a new container. http://bugzilla.openedhand.com/show_bug.cgi?id=1838 --- clutter/clutter-actor.c | 37 +++++++++++++++++++++++++++---------- clutter/clutter-actor.h | 15 ++++++++++----- clutter/clutter-group.c | 6 ++++++ 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index fcab0b6a1..bfb4d8496 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -74,8 +74,8 @@ * clutter_actor_set_width(); or it can have a preferred width and * height, which then allows a layout manager to implicitly size and * position it by "allocating" an area for an actor. This allows for - * actors to be manipulate in both a fixed or static parent container - * (i.e. children of #ClutterGroup) and a more automatic or dynamic + * actors to be manipulated in both a fixed (or static) parent container + * (i.e. children of #ClutterGroup) and a more automatic (or dynamic) * layout based parent container. * * When accessing the position and size of an actor, the simple accessors @@ -880,6 +880,7 @@ clutter_actor_real_map (ClutterActor *self) g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_MAPPED); + /* notify on parent mapped before potentially mapping * children, so apps see a top-down notification. */ @@ -993,20 +994,29 @@ clutter_actor_real_show (ClutterActor *self) ClutterActorPrivate *priv = self->priv; CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_VISIBLE); + /* we notify on the "visible" flag in the clutter_actor_show() * wrapper so the entire show signal emission completes first * (?) */ clutter_actor_update_map_state (self, MAP_STATE_CHECK); - /* While an actor is hidden the parent may not have allocated/requested - * so we need to start from scratch and avoid the short-circuiting - * in clutter_actor_queue_relayout(). + /* we queue a relayout unless the actor is inside a + * container that explicitly told us not to */ - priv->needs_width_request = FALSE; - priv->needs_height_request = FALSE; - priv->needs_allocation = FALSE; - clutter_actor_queue_relayout (self); + if (priv->parent_actor && + (!(priv->parent_actor->flags & CLUTTER_ACTOR_NO_LAYOUT))) + { + /* While an actor is hidden the parent may not have + * allocated/requested so we need to start from scratch + * and avoid the short-circuiting in + * clutter_actor_queue_relayout(). + */ + priv->needs_width_request = FALSE; + priv->needs_height_request = FALSE; + priv->needs_allocation = FALSE; + clutter_actor_queue_relayout (self); + } } } @@ -1081,6 +1091,8 @@ clutter_actor_real_hide (ClutterActor *self) { if (CLUTTER_ACTOR_IS_VISIBLE (self)) { + ClutterActorPrivate *priv = self->priv; + CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_VISIBLE); /* we notify on the "visible" flag in the clutter_actor_hide() @@ -1089,7 +1101,12 @@ clutter_actor_real_hide (ClutterActor *self) */ clutter_actor_update_map_state (self, MAP_STATE_CHECK); - clutter_actor_queue_relayout (self); + /* we queue a relayout unless the actor is inside a + * container that explicitly told us not to + */ + if (priv->parent_actor && + (!(priv->parent_actor->flags & CLUTTER_ACTOR_NO_LAYOUT))) + clutter_actor_queue_relayout (priv->parent_actor); } } diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 2f190a5ed..e1622dd85 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -93,21 +93,26 @@ typedef void (*ClutterCallback) (ClutterActor *actor, /** * ClutterActorFlags: - * @CLUTTER_ACTOR_MAPPED: the actor will be painted (is visible, and inside a toplevel, and all parents visible) + * @CLUTTER_ACTOR_MAPPED: the actor will be painted (is visible, and inside + * a toplevel, and all parents visible) * @CLUTTER_ACTOR_REALIZED: the resources associated to the actor have been * allocated * @CLUTTER_ACTOR_REACTIVE: the actor 'reacts' to mouse events emmitting event * signals * @CLUTTER_ACTOR_VISIBLE: the actor has been shown by the application program + * @CLUTTER_ACTOR_NO_LAYOUT: the actor provides an explicit layout management + * policy for its children; this flag will prevent Clutter from automatic + * queueing of relayout and will defer all layouting to the actor itself * * Flags used to signal the state of an actor. */ typedef enum { - CLUTTER_ACTOR_MAPPED = 1 << 1, - CLUTTER_ACTOR_REALIZED = 1 << 2, - CLUTTER_ACTOR_REACTIVE = 1 << 3, - CLUTTER_ACTOR_VISIBLE = 1 << 4 + CLUTTER_ACTOR_MAPPED = 1 << 1, + CLUTTER_ACTOR_REALIZED = 1 << 2, + CLUTTER_ACTOR_REACTIVE = 1 << 3, + CLUTTER_ACTOR_VISIBLE = 1 << 4, + CLUTTER_ACTOR_NO_LAYOUT = 1 << 5 } ClutterActorFlags; /** diff --git a/clutter/clutter-group.c b/clutter/clutter-group.c index ef3a5a22c..a0ef32c05 100644 --- a/clutter/clutter-group.c +++ b/clutter/clutter-group.c @@ -441,6 +441,12 @@ clutter_group_init (ClutterGroup *self) self->priv->layout = clutter_fixed_layout_new (); g_object_ref_sink (self->priv->layout); + + /* signal Clutter that we don't impose any layout on + * our children, so we can shave off some relayout + * operations + */ + CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_NO_LAYOUT); } /** From 6a3f6a460e730b0067adfa1eac455246c8d6e8d8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 7 Dec 2009 17:35:17 +0000 Subject: [PATCH 10/83] stage: Composite the opacity with the alpha channel The Stage's opacity should be composited with the alpha component of the Stage's background color. --- clutter/clutter-stage.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 6e3a41088..4fac83d84 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -245,16 +245,24 @@ clutter_stage_paint (ClutterActor *self) { ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv; CoglColor stage_color; + guint8 real_alpha; CLUTTER_NOTE (PAINT, "Initializing stage paint"); + /* composite the opacity to the stage color */ + real_alpha = clutter_actor_get_opacity (self) + * priv->color.alpha + / 255; + + /* we use the real alpha to clear the stage if :use-alpha is + * set; the effect depends entirely on how the Clutter backend + */ cogl_color_set_from_4ub (&stage_color, priv->color.red, priv->color.green, priv->color.blue, - priv->use_alpha - ? priv->color.alpha - : 255); + priv->use_alpha ? real_alpha + : 255); cogl_clear (&stage_color, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH); @@ -274,11 +282,6 @@ clutter_stage_paint (ClutterActor *self) else cogl_disable_fog (); -#if 0 - CLUTTER_NOTE (PAINT, "Proxying the paint to the stage implementation"); - _clutter_stage_window_paint (priv->impl); -#endif - /* this will take care of painting every child */ CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->paint (self); } @@ -291,7 +294,6 @@ clutter_stage_pick (ClutterActor *self, * emitted for the stage itself. The stage's pick id is effectively handled * by the call to cogl_clear done in clutter-main.c:_clutter_do_pick_async() */ - clutter_container_foreach (CLUTTER_CONTAINER (self), CLUTTER_CALLBACK (clutter_actor_paint), NULL); From 3b9575baf8d254b7ba8b5faf56b14fbc6c3fde74 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 7 Dec 2009 18:41:15 +0000 Subject: [PATCH 11/83] Queue a relayout when the default text direction changes We need a relayout cycle if the default direction for the text changes while the main loop is running. --- clutter/clutter-main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 90452482d..81756347f 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -3093,13 +3093,19 @@ clutter_check_version (guint major, void clutter_set_default_text_direction (ClutterTextDirection text_dir) { + ClutterStageManager *manager; + const GSList *stages, *l; + if (text_dir == CLUTTER_TEXT_DIRECTION_DEFAULT) text_dir = clutter_get_text_direction (); if (text_dir != clutter_text_direction) clutter_text_direction = text_dir; - /* FIXME - queue a relayout on all stages */ + manager = clutter_stage_manager_get_default (); + stages = clutter_stage_manager_peek_stages (manager); + for (l = stages; l != NULL; l = l->next) + clutter_actor_queue_relayout (l->data); } ClutterTextDirection From 28cb2cdab588ac361544538d4bb70a3e9bf24c81 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 7 Dec 2009 19:00:55 +0000 Subject: [PATCH 12/83] Remove clutter_set_default_text_direction() Setting the default text direction programmatically is wrong: it is a value dependent on the locale or by the environment. --- clutter/clutter-actor.c | 3 +- clutter/clutter-main.c | 32 ++++++++++------------ clutter/clutter-main.h | 1 - doc/reference/clutter/clutter-sections.txt | 1 - 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index fcab0b6a1..f676d5e3b 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -9448,7 +9448,8 @@ clutter_actor_set_text_direction (ClutterActor *self, * Retrieves the value set using clutter_actor_set_text_direction() * * If no text direction has been previously set, the default text - * direction will be returned + * direction, as returned by clutter_get_default_text_direction(), will + * be returned instead * * Return value: the #ClutterTextDirection for the actor * diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 81756347f..642085774 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -3090,24 +3090,20 @@ clutter_check_version (guint major, clutter_micro_version >= micro)); } -void -clutter_set_default_text_direction (ClutterTextDirection text_dir) -{ - ClutterStageManager *manager; - const GSList *stages, *l; - - if (text_dir == CLUTTER_TEXT_DIRECTION_DEFAULT) - text_dir = clutter_get_text_direction (); - - if (text_dir != clutter_text_direction) - clutter_text_direction = text_dir; - - manager = clutter_stage_manager_get_default (); - stages = clutter_stage_manager_peek_stages (manager); - for (l = stages; l != NULL; l = l->next) - clutter_actor_queue_relayout (l->data); -} - +/** + * clutter_get_default_text_direction: + * + * Retrieves the default direction for the text. The text direction is + * determined by the locale and/or by the %CLUTTER_TEXT_DIRECTION environment + * variable + * + * The default text direction can be overridden on a per-actor basis by using + * clutter_actor_set_text_direction() + * + * Return value: the default text direction + * + * Since: 1.2 + */ ClutterTextDirection clutter_get_default_text_direction (void) { diff --git a/clutter/clutter-main.h b/clutter/clutter-main.h index c5f456219..067cef086 100644 --- a/clutter/clutter-main.h +++ b/clutter/clutter-main.h @@ -164,7 +164,6 @@ void clutter_ungrab_pointer_for_device (gint id); PangoFontMap * clutter_get_font_map (void); -void clutter_set_default_text_direction (ClutterTextDirection text_dir); ClutterTextDirection clutter_get_default_text_direction (void); G_END_DECLS diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index b4c84fb53..e7dafd772 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1017,7 +1017,6 @@ clutter_set_font_flags clutter_get_font_flags clutter_get_font_map ClutterTextDirection -clutter_set_default_text_direction clutter_get_default_text_direction From e957e277b8a4893ce8c99e94402036d42a8b3748 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 8 Dec 2009 18:24:30 +0000 Subject: [PATCH 13/83] x11: Do not manage a foreign window If a Stage has been set to use a foreign Window then Clutter should not be managing it; calling XWithdrawWindow and XMapWindow should be reserved to the windows we manage ourselves. --- clutter/x11/clutter-stage-x11.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index 678bbde2f..6dbc29050 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -525,16 +525,21 @@ clutter_stage_x11_set_user_resizable (ClutterStageWindow *stage_window, static void update_wm_hints (ClutterStageX11 *stage_x11) { - ClutterBackend *backend = clutter_get_default_backend (); + ClutterBackend *backend; ClutterBackendX11 *backend_x11; XWMHints wm_hints; - g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend)); - backend_x11 = CLUTTER_BACKEND_X11 (backend); - if (stage_x11->wm_state & STAGE_X11_WITHDRAWN) return; + if (stage_x11->is_foreign_xwin) + return; + + backend = clutter_get_default_backend (); + + g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend)); + backend_x11 = CLUTTER_BACKEND_X11 (backend); + wm_hints.flags = StateHint; wm_hints.initial_state = NormalState; @@ -573,7 +578,7 @@ clutter_stage_x11_show (ClutterStageWindow *stage_window, if (stage_x11->xwin != None) { - if (do_raise) + if (do_raise && !stage_x11->is_foreign_xwin) { CLUTTER_NOTE (BACKEND, "Raising stage[%lu]", (unsigned long) stage_x11->xwin); @@ -599,7 +604,8 @@ clutter_stage_x11_show (ClutterStageWindow *stage_window, clutter_actor_map (CLUTTER_ACTOR (stage_x11->wrapper)); - XMapWindow (backend_x11->xdpy, stage_x11->xwin); + if (!stage_x11->is_foreign_xwin) + XMapWindow (backend_x11->xdpy, stage_x11->xwin); } } @@ -622,9 +628,8 @@ clutter_stage_x11_hide (ClutterStageWindow *stage_window) clutter_actor_unmap (CLUTTER_ACTOR (stage_x11->wrapper)); - XWithdrawWindow (backend_x11->xdpy, - stage_x11->xwin, - 0); + if (!stage_x11->is_foreign_xwin) + XWithdrawWindow (backend_x11->xdpy, stage_x11->xwin, 0); } } From 3a3c815286daa55761787a49d7a33c6eb8c3d74d Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 9 Dec 2009 01:46:09 +0000 Subject: [PATCH 14/83] stage: Premultiply the stage color In case we are using Stage:use-alpha then we need to premultiply the stage color to get the correct blending from the compositor. --- clutter/clutter-stage.c | 1 + 1 file changed, 1 insertion(+) diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 4fac83d84..313cc276d 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -263,6 +263,7 @@ clutter_stage_paint (ClutterActor *self) priv->color.blue, priv->use_alpha ? real_alpha : 255); + cogl_color_premultiply (&stage_color); cogl_clear (&stage_color, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH); From 3b075b6dc4197ed78f6386ee7e9a6e307839590f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 9 Dec 2009 01:46:52 +0000 Subject: [PATCH 15/83] tests: Allow setting the stage opacity in test-paint-wrapper This allows testing the Stage:use-alpha property and the blending done by a compositor. --- tests/interactive/test-paint-wrapper.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/interactive/test-paint-wrapper.c b/tests/interactive/test-paint-wrapper.c index 7b14cef42..46e01daa0 100644 --- a/tests/interactive/test-paint-wrapper.c +++ b/tests/interactive/test-paint-wrapper.c @@ -31,6 +31,7 @@ typedef struct SuperOH } SuperOH; static gint n_hands = NHANDS; +static gint use_alpha = 255; static GOptionEntry super_oh_entries[] = { { @@ -39,6 +40,12 @@ static GOptionEntry super_oh_entries[] = { G_OPTION_ARG_INT, &n_hands, "Number of hands", "HANDS" }, + { + "use-alpha", 'a', + 0, + G_OPTION_ARG_INT, &use_alpha, + "Stage opacity", "VALUE" + }, { NULL } }; @@ -205,6 +212,14 @@ test_paint_wrapper_main (int argc, char *argv[]) stage = clutter_stage_get_default (); clutter_actor_set_size (stage, 800, 600); + if (use_alpha != 255) + { + g_object_set (G_OBJECT (stage), + "use-alpha", TRUE, + "opacity", use_alpha, + NULL); + } + clutter_stage_set_title (CLUTTER_STAGE (stage), "Paint Test"); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); From 1208e47198ea981b644bca2ce9beab3b59f05d0b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 9 Dec 2009 02:04:56 +0000 Subject: [PATCH 16/83] stage: Add accessors for :use-alpha Use real accessors for the :use-alpha property. --- clutter/clutter-stage.c | 53 +++++++++++++++++++++- clutter/clutter-stage.h | 4 ++ doc/reference/clutter/clutter-sections.txt | 2 + 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 313cc276d..32eac253f 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -669,8 +669,7 @@ clutter_stage_set_property (GObject *object, break; case PROP_USE_ALPHA: - stage->priv->use_alpha = g_value_get_boolean (value); - clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + clutter_stage_set_use_alpha (stage, g_value_get_boolean (value)); break; default: @@ -2164,3 +2163,53 @@ clutter_stage_get_throttle_motion_events (ClutterStage *stage) return stage->priv->throttle_motion_events; } + +/** + * clutter_stage_set_use_alpha: + * @stage: a #ClutterStage + * @use_alpha: whether the stage should honour the opacity or the + * alpha channel of the stage color + * + * Sets whether the @stage should honour the #ClutterActor:opacity and + * the alpha channel of the #ClutterStage:color + * + * Since: 1.2 + */ +void +clutter_stage_set_use_alpha (ClutterStage *stage, + gboolean use_alpha) +{ + ClutterStagePrivate *priv; + + g_return_if_fail (CLUTTER_IS_STAGE (stage)); + + priv = stage->priv; + + if (priv->use_alpha != use_alpha) + { + priv->use_alpha = use_alpha; + + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + g_object_notify (G_OBJECT (stage), "use-alpha"); + } +} + +/** + * clutter_stage_get_use_alpha: + * @stage: a #ClutterStage + * + * Retrieves the value set using clutter_stage_set_use_alpha() + * + * Return value: %TRUE if the stage should honour the opacity and the + * alpha channel of the stage color + * + * Since: 1.2 + */ +gboolean +clutter_stage_get_use_alpha (ClutterStage *stage) +{ + g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); + + return stage->priv->use_alpha; +} diff --git a/clutter/clutter-stage.h b/clutter/clutter-stage.h index 59baf5684..095e1f8c9 100644 --- a/clutter/clutter-stage.h +++ b/clutter/clutter-stage.h @@ -247,6 +247,10 @@ void clutter_stage_set_throttle_motion_events (ClutterStage *stage, gboolean throttle); gboolean clutter_stage_get_throttle_motion_events (ClutterStage *stage); +void clutter_stage_set_use_alpha (ClutterStage *stage, + gboolean use_alpha); +gboolean clutter_stage_get_use_alpha (ClutterStage *stage); + /* Commodity macro, for mallum only */ #define clutter_stage_add(stage,actor) G_STMT_START { \ if (CLUTTER_IS_STAGE ((stage)) && CLUTTER_IS_ACTOR ((actor))) \ diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index d96a6c07d..cb3b06732 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -522,6 +522,8 @@ clutter_stage_get_key_focus clutter_stage_read_pixels clutter_stage_set_throttle_motion_events clutter_stage_get_throttle_motion_events +clutter_stage_set_use_alpha +clutter_stage_get_use_alpha ClutterPerspective From 34913378fb7aa84874bfda63648e9e0e02e40d8c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 9 Dec 2009 17:17:33 +0000 Subject: [PATCH 17/83] tests: Use accessor methods for :use-alpha Instead of using g_object_set(). --- tests/interactive/test-paint-wrapper.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/interactive/test-paint-wrapper.c b/tests/interactive/test-paint-wrapper.c index 46e01daa0..3a654e4f5 100644 --- a/tests/interactive/test-paint-wrapper.c +++ b/tests/interactive/test-paint-wrapper.c @@ -214,10 +214,8 @@ test_paint_wrapper_main (int argc, char *argv[]) if (use_alpha != 255) { - g_object_set (G_OBJECT (stage), - "use-alpha", TRUE, - "opacity", use_alpha, - NULL); + clutter_stage_set_use_alpha (CLUTTER_STAGE (stage), TRUE); + clutter_actor_set_opacity (stage, use_alpha); } clutter_stage_set_title (CLUTTER_STAGE (stage), "Paint Test"); From 1ffd2ccb87a570733647e655e5e608ee05661c74 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 9 Dec 2009 01:52:09 +0000 Subject: [PATCH 18/83] stage: Deprecate default stage size macros The macros for getting the size of the default stage are a relic of the past and they should be deprecated. --- clutter/clutter-stage.h | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/clutter/clutter-stage.h b/clutter/clutter-stage.h index 59baf5684..fc2e4e855 100644 --- a/clutter/clutter-stage.h +++ b/clutter/clutter-stage.h @@ -39,25 +39,13 @@ G_BEGIN_DECLS #define CLUTTER_TYPE_FOG (clutter_fog_get_type ()) #define CLUTTER_TYPE_STAGE (clutter_stage_get_type()) -#define CLUTTER_STAGE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_STAGE, ClutterStage)) +#define CLUTTER_STAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE, ClutterStage)) +#define CLUTTER_STAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE, ClutterStageClass)) +#define CLUTTER_IS_STAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE)) +#define CLUTTER_IS_STAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE)) +#define CLUTTER_STAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE, ClutterStageClass)) -#define CLUTTER_STAGE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_STAGE, ClutterStageClass)) - -#define CLUTTER_IS_STAGE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_STAGE)) - -#define CLUTTER_IS_STAGE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_STAGE)) - -#define CLUTTER_STAGE_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_STAGE, ClutterStageClass)) +#ifndef CLUTTER_DISABLE_DEPRECATED /** * CLUTTER_STAGE_WIDTH: @@ -65,9 +53,10 @@ G_BEGIN_DECLS * Macro that evaluates to the width of the default stage * * Since: 0.2 + * + * Deprecated: 1.2: Use clutter_actor_get_width() instead */ -#define CLUTTER_STAGE_WIDTH() \ - (clutter_actor_get_width (clutter_stage_get_default ())) +#define CLUTTER_STAGE_WIDTH() (clutter_actor_get_width (clutter_stage_get_default ())) /** * CLUTTER_STAGE_HEIGHT: @@ -75,9 +64,12 @@ G_BEGIN_DECLS * Macro that evaluates to the height of the default stage * * Since: 0.2 + * + * Deprecated: 1.2: use clutter_actor_get_height() instead */ -#define CLUTTER_STAGE_HEIGHT() \ - (clutter_actor_get_height (clutter_stage_get_default ())) +#define CLUTTER_STAGE_HEIGHT() (clutter_actor_get_height (clutter_stage_get_default ())) + +#endif /* !CLUTTER_DISABLE_DEPRECATED */ /** * ClutterPickMode: From 9242d6b934710ca4001e8dc2a0f1f4bc4a738a63 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 9 Dec 2009 18:10:52 +0000 Subject: [PATCH 19/83] docs: Move RELEASING under docs/ --- RELEASING => doc/RELEASING | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename RELEASING => doc/RELEASING (100%) diff --git a/RELEASING b/doc/RELEASING similarity index 100% rename from RELEASING rename to doc/RELEASING From 86ce92eec14aa1aaf7c9ab14a520b66690488213 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 9 Dec 2009 18:46:25 +0000 Subject: [PATCH 20/83] x11: Emulate XKB's detectable key auto-repeat Old-style X11 terminals require that even modern X11 send KeyPress and KeyRelease pairs when auto-repeating. For this reason modern(-ish) API like XKB has a way to detect auto-repeat and do a single KeyRelease at the end of a KeyPress sequence. The newly added check emulates XKB's detectable auto-repeat by peeking the next event after a KeyRelease and checking if it's a KeyPress for the same key and timestamp - and then ignoring the KeyRelease if it matches. --- clutter/x11/clutter-event-x11.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index b40c5b5a4..6b3ea67ce 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -627,6 +627,32 @@ event_translate (ClutterBackend *backend, break; case KeyRelease: + /* old-style X11 terminals require that even modern X11 send + * KeyPress/KeyRelease pairs when auto-repeating. for this + * reason modern(-ish) API like XKB has a way to detect + * auto-repeat and do a single KeyRelease at the end of a + * KeyPress sequence. + * + * this check emulates XKB's detectable auto-repeat; we peek + * the next event and check if it's a KeyPress for the same key + * and timestamp - and then ignore it if it matches the + * KeyRelease + */ + if (XPending (xevent->xkey.display)) + { + XEvent next_event; + + XPeekEvent (xevent->xkey.display, &next_event); + + if (next_event.type == KeyPress && + next_event.xkey.keycode == xevent->xkey.keycode && + next_event.xkey.time == xevent->xkey.time) + { + res = FALSE; + break; + } + } + event->key.type = event->type = CLUTTER_KEY_RELEASE; translate_key_event (backend, event, xevent); break; From 1374b5aac9398852cf3f3482fdf925b52aa31338 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 12 Nov 2009 17:33:15 -0500 Subject: [PATCH 21/83] Add clutter_get_current_event When getting signals from higher level toolkits, occasionally one wants access to the underlying event; say for a Button widget's "clicked" signal, to get the keyboard state. Rather than having all of the highlevel widgets emit ClutterEvent just for the more unusual use cases, add a global function to access the event state. http://bugzilla.openedhand.com/show_bug.cgi?id=1888 Signed-off-by: Emmanuele Bassi --- clutter/clutter-event.c | 23 ++++++++ clutter/clutter-event.h | 1 + clutter/clutter-main.c | 62 ++++++++++++---------- clutter/clutter-private.h | 1 + doc/reference/clutter/clutter-sections.txt | 1 + 5 files changed, 61 insertions(+), 27 deletions(-) diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index a74338ecd..2f9cf178a 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -765,6 +765,29 @@ clutter_get_current_event_time (void) return CLUTTER_CURRENT_TIME; } +/** + * clutter_get_current_event: + * + * If an event is currently being processed, return that event. + * This function is intended to be used to access event state + * that might not be exposed by higher-level widgets. For + * example, to get the key modifier state from a Button 'clicked' + * event. + * + * Return value: (transfer none): The current ClutterEvent, or %NULL if none + * + * Since: 1.2 + */ +G_CONST_RETURN ClutterEvent * +clutter_get_current_event (void) +{ + ClutterMainContext *context = _clutter_context_get_default (); + + g_return_val_if_fail (context != NULL, NULL); + + return context->current_event; +} + /** * clutter_input_device_get_device_type: * @device: a #ClutterInputDevice diff --git a/clutter/clutter-event.h b/clutter/clutter-event.h index 9ce23455b..03d91549c 100644 --- a/clutter/clutter-event.h +++ b/clutter/clutter-event.h @@ -507,6 +507,7 @@ ClutterScrollDirection clutter_event_get_scroll_direction (ClutterEvent *e guint32 clutter_keysym_to_unicode (guint keyval); guint32 clutter_get_current_event_time (void); +G_CONST_RETURN ClutterEvent *clutter_get_current_event (void); ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device); gint clutter_input_device_get_device_id (ClutterInputDevice *device); diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 642085774..3b0f3997e 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2166,36 +2166,13 @@ clutter_do_event (ClutterEvent *event) _clutter_stage_queue_event (event->any.stage, event); } -/** - * _clutter_process_event - * @event: a #ClutterEvent. - * - * Does the actual work of processing an event that was queued earlier - * out of clutter_do_event(). - */ -void -_clutter_process_event (ClutterEvent *event) +static void +_clutter_process_event_details (ClutterActor *stage, + ClutterMainContext *context, + ClutterEvent *event) { - /* FIXME: This should probably be clutter_cook_event() - it would - * take a raw event from the backend and 'cook' it so its more tasty. - * - */ - ClutterMainContext *context; - ClutterBackend *backend; - ClutterActor *stage; ClutterInputDevice *device = NULL; - context = _clutter_context_get_default (); - backend = context->backend; - stage = CLUTTER_ACTOR(event->any.stage); - - if (!stage) - return; - - CLUTTER_TIMESTAMP (EVENT, "Event received"); - - context->last_event_time = clutter_event_get_time (event); - switch (event->type) { case CLUTTER_NOTHING: @@ -2393,6 +2370,37 @@ _clutter_process_event (ClutterEvent *event) } } +/** + * _clutter_process_event + * @event: a #ClutterEvent. + * + * Does the actual work of processing an event that was queued earlier + * out of clutter_do_event(). + */ +void +_clutter_process_event (ClutterEvent *event) +{ + ClutterMainContext *context; + ClutterBackend *backend; + ClutterActor *stage; + + context = _clutter_context_get_default (); + backend = context->backend; + stage = CLUTTER_ACTOR(event->any.stage); + + if (!stage) + return; + + CLUTTER_TIMESTAMP (EVENT, "Event received"); + + context->last_event_time = clutter_event_get_time (event); + + context->current_event = event; + _clutter_process_event_details (stage, context, event); + context->current_event = NULL; +} + + /** * clutter_get_actor_by_gid * @id: a #ClutterActor ID. diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index db9a6d2d0..651fbea16 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -128,6 +128,7 @@ struct _ClutterMainContext GSList *input_devices; /* For extra input devices, i.e MultiTouch */ + ClutterEvent *current_event; guint32 last_event_time; gulong redraw_count; diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index e7dafd772..c2c432bce 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -977,6 +977,7 @@ clutter_input_device_get_device_type clutter_get_current_event_time +clutter_get_current_event CLUTTER_TYPE_EVENT From d2ea7cd6a8a1eb4ff04f11cb53aa387458a8e128 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 11 Dec 2009 20:17:58 +0000 Subject: [PATCH 22/83] Clean up whitespace, indentation and comments --- clutter/clutter-main.c | 70 ++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 642085774..cc2addcc0 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -354,19 +354,6 @@ clutter_get_motion_events_enabled (void) guint _clutter_pix_to_id (guchar pixel[4]); -static inline void init_bits (void) -{ - ClutterMainContext *ctx; - - static gboolean done = FALSE; - if (G_LIKELY (done)) - return; - - ctx = _clutter_context_get_default (); - - done = TRUE; -} - void _clutter_id_to_color (guint id, ClutterColor *col) { @@ -377,9 +364,11 @@ _clutter_id_to_color (guint id, ClutterColor *col) /* compute the numbers we'll store in the components */ red = (id >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used)) - & (0xff >> (8-ctx->fb_r_mask_used)); - green = (id >> ctx->fb_b_mask_used) & (0xff >> (8-ctx->fb_g_mask_used)); - blue = (id) & (0xff >> (8-ctx->fb_b_mask_used)); + & (0xff >> (8-ctx->fb_r_mask_used)); + green = (id >> ctx->fb_b_mask_used) + & (0xff >> (8-ctx->fb_g_mask_used)); + blue = (id) + & (0xff >> (8-ctx->fb_b_mask_used)); /* shift left bits a bit and add one, this circumvents * at least some potential rounding errors in GL/GLES @@ -409,9 +398,9 @@ _clutter_id_to_color (guint id, ClutterColor *col) */ if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) { - col->red = (col->red << 4) | (col->red >> 4); + col->red = (col->red << 4) | (col->red >> 4); col->green = (col->green << 4) | (col->green >> 4); - col->blue = (col->blue << 4) | (col->blue >> 4); + col->blue = (col->blue << 4) | (col->blue >> 4); } } @@ -456,8 +445,9 @@ _clutter_pixel_to_id (guchar pixel[4]) blue = blue >> (ctx->fb_b_mask - ctx->fb_b_mask_used); /* combine the correct per component values into the final id */ - id = blue + (green << ctx->fb_b_mask_used) - + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used)); + id = blue + + (green << ctx->fb_b_mask_used) + + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used)); return id; } @@ -498,28 +488,34 @@ read_pixels_to_file (char *filename_stem, NULL); /* callback data */ if (pixbuf) { - char *filename = - g_strdup_printf ("%s-%05d.png", filename_stem, read_count); + char *filename = g_strdup_printf ("%s-%05d.png", + filename_stem, + read_count); GError *error = NULL; + if (!gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL)) { g_warning ("Failed to save pick buffer to file %s: %s", filename, error->message); g_error_free (error); } + g_free (filename); g_object_unref (pixbuf); read_count++; } -#else - static gboolean seen = FALSE; - if (!seen) - { - g_warning ("dumping buffers to an image isn't supported on platforms " - "without gdk pixbuf support\n"); - seen = TRUE; - } -#endif +#else /* !USE_GDKPIXBUF */ + { + static gboolean seen = FALSE; + + if (!seen) + { + g_warning ("dumping buffers to an image isn't supported on platforms " + "without gdk pixbuf support\n"); + seen = TRUE; + } + } +#endif /* USE_GDKPIXBUF */ } ClutterActor * @@ -780,14 +776,14 @@ clutter_main (void) static void clutter_threads_impl_lock (void) { - if (clutter_threads_mutex) + if (G_LIKELY (clutter_threads_mutex != NULL)) g_mutex_lock (clutter_threads_mutex); } static void clutter_threads_impl_unlock (void) { - if (clutter_threads_mutex) + if (G_LIKELY (clutter_threads_mutex != NULL)) g_mutex_unlock (clutter_threads_mutex); } @@ -802,6 +798,8 @@ clutter_threads_impl_unlock (void) * * This function must be called before clutter_init(). * + * It is safe to call this function multiple times. + * * Since: 0.4 */ void @@ -810,6 +808,9 @@ clutter_threads_init (void) if (!g_thread_supported ()) g_error ("g_thread_init() must be called before clutter_threads_init()"); + if (clutter_threads_mutex != NULL) + return; + clutter_threads_mutex = g_mutex_new (); if (!clutter_threads_lock) @@ -1253,6 +1254,7 @@ _clutter_context_get_default (void) ClutterCntx = ctx = g_new0 (ClutterMainContext, 1); + /* create the default backend */ ctx->backend = g_object_new (_clutter_backend_impl_get_type (), NULL); ctx->is_initialized = FALSE; @@ -1414,7 +1416,7 @@ clutter_init_real (GError **error) resolution = clutter_backend_get_resolution (ctx->backend); cogl_pango_font_map_set_resolution (ctx->font_map, resolution); - if (G_LIKELY (!clutter_disable_mipmap_text)) + if (!clutter_disable_mipmap_text) cogl_pango_font_map_set_use_mipmapping (ctx->font_map, TRUE); clutter_text_direction = clutter_get_text_direction (); From 999359d64ccfbdbcc952662c14fa47441d6a5225 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 11 Dec 2009 23:48:58 +0000 Subject: [PATCH 23/83] Add ActorBox animation methods ClutterActorBox should have an interpolate() method that allows to compute the intermediate values between two states, given a progress value, e.g.: clutter_actor_box_interpolate (start, end, alpha, &result); Another utility method, useful for layout managers, is a modifier that clamps the members of the actor box to the nearest integer value. --- clutter/clutter-actor.c | 47 ++++++++++++++++++++++ clutter/clutter-types.h | 5 +++ doc/reference/clutter/clutter-sections.txt | 2 + 3 files changed, 54 insertions(+) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index f676d5e3b..c439437d0 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -8317,6 +8317,53 @@ clutter_actor_box_from_vertices (ClutterActorBox *box, box->y2 = y_2; } +/** + * clutter_actor_box_interpolate: + * @initial: the initial #ClutterActorBox + * @final: the final #ClutterActorBox + * @progress: the interpolation progress + * @result: (out): return location for the interpolation + * + * Interpolates between @initial and @final #ClutterActorBoxes + * using @progress + * + * Since: 1.2 + */ +void +clutter_actor_box_interpolate (const ClutterActorBox *initial, + const ClutterActorBox *final, + gdouble progress, + ClutterActorBox *result) +{ + g_return_if_fail (initial != NULL); + g_return_if_fail (final != NULL); + g_return_if_fail (result != NULL); + + result->x1 = initial->x1 + (final->x1 - initial->x1) * progress; + result->y1 = initial->y1 + (final->y1 - initial->y1) * progress; + result->x2 = initial->x2 + (final->x2 - initial->x2) * progress; + result->y2 = initial->y2 + (final->y2 - initial->y2) * progress; +} + +/** + * clutter_actor_box_clamp_to_pixel: + * @box: (inout): the #ClutterActorBox to clamp + * + * Clamps the components of @box to the nearest integer + * + * Since: 1.2 + */ +void +clutter_actor_box_clamp_to_pixel (ClutterActorBox *box) +{ + g_return_if_fail (box != NULL); + + box->x1 = floorf (box->x1 + 0.5); + box->y1 = floorf (box->y1 + 0.5); + box->x2 = floorf (box->x2 + 0.5); + box->y2 = floorf (box->y2 + 0.5); +} + /******************************************************************************/ struct _ShaderData diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h index ec7502310..b13a4dfed 100644 --- a/clutter/clutter-types.h +++ b/clutter/clutter-types.h @@ -153,6 +153,11 @@ gboolean clutter_actor_box_contains (const ClutterActorBox *box, gfloat y); void clutter_actor_box_from_vertices (ClutterActorBox *box, const ClutterVertex verts[]); +void clutter_actor_box_interpolate (const ClutterActorBox *initial, + const ClutterActorBox *final, + gdouble progress, + ClutterActorBox *result); +void clutter_actor_box_clamp_to_pixel (ClutterActorBox *box); /** * ClutterGeometry: diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index e7dafd772..d90358739 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -414,6 +414,8 @@ clutter_actor_box_get_size clutter_actor_box_get_area clutter_actor_box_contains clutter_actor_box_from_vertices +clutter_actor_box_clamp_to_pixel +clutter_actor_box_interpolate ClutterVertex From f94a903d9ebdbc055010bb4a4c6fe7e76f418bd8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sat, 12 Dec 2009 00:02:43 +0000 Subject: [PATCH 24/83] layout: Add animation support to LayoutManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to animate a fluid layout we cannot use the common animation code paths as they will override the size request and allocation paths that are handled by the layout manager itself. One way to introduce animations in the allocation sequence is to use a Timeline and an Alpha to compute a progress value and then use that value to interpolate an ActorBox between the initial and final states of the animation - with the initial state being the last allocation of the child prior to the animation start, and the final state the allocation of the child at the end; for every frame of the Timeline we then queue a relayout on the layout manager's container, which will result in an animation. ClutterLayoutManager is the most likely place to add a generic API for beginning and ending an animation, as well as the place to provide a default code path to create the ancillary Timeline and Alpha instances needed to drive the animation. A LayoutManager sub-class will need to: • call clutter_layout_manager_begin_animation() whenever it should animate between two states, for instance: whenever a layout property changes value; • eventually override begin_animation() and end_animation() in case further state needs to be set up, and then chain up to the default implementation provided by LayoutManager; • if a completely different implementation is required, the layout manager sub-class should override begin_animation(), end_animation() and get_animation_progress(). Inside the allocate() implementation the sub-class should also interpolate between the last known allocation of a child and the newly computed allocation. --- clutter/clutter-layout-manager.c | 159 ++++++++++++++++++++- clutter/clutter-layout-manager.h | 62 +++++--- doc/reference/clutter/clutter-sections.txt | 5 + 3 files changed, 204 insertions(+), 22 deletions(-) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 119539b6f..251f3e647 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -107,6 +107,11 @@ * child of the #ClutterContainer. * * + * + * Animating a ClutterLayoutManager + * ... + * + * * #ClutterLayoutManager is available since Clutter 1.2 */ @@ -117,11 +122,13 @@ #include #include +#include "clutter-alpha.h" #include "clutter-debug.h" #include "clutter-layout-manager.h" #include "clutter-layout-meta.h" #include "clutter-marshal.h" #include "clutter-private.h" +#include "clutter-timeline.h" #define LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED(m,method) G_STMT_START { \ GObject *_obj = G_OBJECT (m); \ @@ -141,7 +148,9 @@ G_DEFINE_ABSTRACT_TYPE (ClutterLayoutManager, clutter_layout_manager, G_TYPE_INITIALLY_UNOWNED); -static GQuark quark_layout_meta = 0; +static GQuark quark_layout_meta = 0; +static GQuark quark_layout_alpha = 0; + static guint manager_signals[LAST_SIGNAL] = { 0, }; static void @@ -217,17 +226,92 @@ layout_manager_real_get_child_meta_type (ClutterLayoutManager *manager) return G_TYPE_INVALID; } +static void +layout_manager_real_begin_animation (ClutterLayoutManager *manager, + guint duration, + gulong mode) +{ + ClutterTimeline *timeline; + ClutterAlpha *alpha; + + alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); + if (alpha != NULL) + return; + + timeline = clutter_timeline_new (duration); + alpha = clutter_alpha_new_full (timeline, mode); + g_object_unref (timeline); + + g_signal_connect_swapped (timeline, "completed", + G_CALLBACK (clutter_layout_manager_end_animation), + manager); + g_signal_connect_swapped (timeline, "new-frame", + G_CALLBACK (clutter_layout_manager_layout_changed), + manager); + + g_object_set_qdata_full (G_OBJECT (manager), + quark_layout_alpha, alpha, + (GDestroyNotify) g_object_unref); + + clutter_timeline_start (timeline); +} + +static gdouble +layout_manager_real_get_animation_progress (ClutterLayoutManager *manager) +{ + ClutterAlpha *alpha; + + alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); + if (alpha == NULL) + return 1.0; + + return clutter_alpha_get_alpha (alpha); +} + +static void +layout_manager_real_end_animation (ClutterLayoutManager *manager) +{ + ClutterTimeline *timeline; + ClutterAlpha *alpha; + + alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); + if (alpha == NULL) + return; + + timeline = clutter_alpha_get_timeline (alpha); + g_assert (timeline != NULL); + + if (clutter_timeline_is_playing (timeline)) + clutter_timeline_stop (timeline); + + g_signal_handlers_disconnect_by_func (timeline, + G_CALLBACK (clutter_layout_manager_end_animation), + manager); + g_signal_handlers_disconnect_by_func (timeline, + G_CALLBACK (clutter_layout_manager_layout_changed), + manager); + + g_object_set_qdata (G_OBJECT (manager), quark_layout_alpha, NULL); + + clutter_layout_manager_layout_changed (manager); +} + static void clutter_layout_manager_class_init (ClutterLayoutManagerClass *klass) { quark_layout_meta = g_quark_from_static_string ("clutter-layout-manager-child-meta"); + quark_layout_alpha = + g_quark_from_static_string ("clutter-layout-manager-alpha"); klass->get_preferred_width = layout_manager_real_get_preferred_width; klass->get_preferred_height = layout_manager_real_get_preferred_height; klass->allocate = layout_manager_real_allocate; klass->create_child_meta = layout_manager_real_create_child_meta; klass->get_child_meta_type = layout_manager_real_get_child_meta_type; + klass->begin_animation = layout_manager_real_begin_animation; + klass->get_animation_progress = layout_manager_real_get_animation_progress; + klass->end_animation = layout_manager_real_end_animation; /** * ClutterLayoutManager::layout-changed: @@ -905,3 +989,76 @@ clutter_layout_manager_list_child_properties (ClutterLayoutManager *manager, return pspecs; } + +/** + * clutter_layout_manager_begin_animation: + * @manager: a #ClutterLayoutManager + * @duration: the duration of the animation, in milliseconds + * @mode: the easing mode of the animation + * + * Begins an animation of @duration milliseconds, using the provided + * easing @mode + * + * The easing mode can be specified either as a #ClutterAnimationMode + * or as a logical id returned by clutter_alpha_register_func() + * + * The result of this function depends on the @manager implementation + * + * Since: 1.2 + */ +void +clutter_layout_manager_begin_animation (ClutterLayoutManager *manager, + guint duration, + gulong mode) +{ + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + g_return_if_fail (duration > 0 && duration < 1000); + + CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager)->begin_animation (manager, + duration, + mode); +} + +/** + * clutter_layout_manager_end_animation: + * @manager: a #ClutterLayoutManager + * + * Ends an animation started by clutter_layout_manager_begin_animation() + * + * The result of this call depends on the @manager implementation + * + * Since: 1.2 + */ +void +clutter_layout_manager_end_animation (ClutterLayoutManager *manager) +{ + g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); + + CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager)->end_animation (manager); +} + +/** + * clutter_layout_manager_get_animation_progress: + * @manager: a #ClutterLayoutManager + * + * Retrieves the progress of the animation, if one has been started by + * clutter_layout_manager_begin_animation() + * + * The returned value has the same semantics of the #ClutterAlpha:alpha + * value + * + * Return value: the progress of the animation + * + * Since: 1.2 + */ +gdouble +clutter_layout_manager_get_animation_progress (ClutterLayoutManager *manager) +{ + ClutterLayoutManagerClass *klass; + + g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), 1.0); + + klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); + + return klass->get_animation_progress (manager); +} diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h index 676837dea..1271a3db4 100644 --- a/clutter/clutter-layout-manager.h +++ b/clutter/clutter-layout-manager.h @@ -82,6 +82,12 @@ struct _ClutterLayoutManager * @create_child_meta: virtual function; override to create a * #ClutterLayoutMeta instance associated to a #ClutterContainer and a * child #ClutterActor, used to maintain layout manager specific properties + * @begin_animation: virtual function; override to control the animation + * of a #ClutterLayoutManager with the given duration and easing mode + * @end_animation: virtual function; override to end an animation started + * by clutter_layout_manager_begin_animation() + * @get_animation_progress: virtual function; override to control the + * progress of the animation of a #ClutterLayoutManager * @layout_changed: class handler for the #ClutterLayoutManager::layout-changed * signal * @@ -96,30 +102,38 @@ struct _ClutterLayoutManagerClass GInitiallyUnownedClass parent_class; /*< public >*/ - void (* get_preferred_width) (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_height, - gfloat *minimum_width_p, - gfloat *natural_width_p); - void (* get_preferred_height) (ClutterLayoutManager *manager, - ClutterContainer *container, - gfloat for_width, - gfloat *minimum_height_p, - gfloat *natural_height_p); - void (* allocate) (ClutterLayoutManager *manager, - ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags); + /* vfuncs, not signals */ + void (* get_preferred_width) (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *minimum_width_p, + gfloat *natural_width_p); + void (* get_preferred_height) (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *minimum_height_p, + gfloat *natural_height_p); + void (* allocate) (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags); - void (* set_container) (ClutterLayoutManager *manager, - ClutterContainer *container); + void (* set_container) (ClutterLayoutManager *manager, + ClutterContainer *container); - GType (* get_child_meta_type) (ClutterLayoutManager *manager); - ClutterLayoutMeta *(* create_child_meta) (ClutterLayoutManager *manager, - ClutterContainer *container, - ClutterActor *actor); + GType (* get_child_meta_type) (ClutterLayoutManager *manager); + ClutterLayoutMeta *(* create_child_meta) (ClutterLayoutManager *manager, + ClutterContainer *container, + ClutterActor *actor); - void (* layout_changed) (ClutterLayoutManager *manager); + void (* begin_animation) (ClutterLayoutManager *manager, + guint duration, + gulong mode); + gdouble (* get_animation_progress) (ClutterLayoutManager *manager); + void (* end_animation) (ClutterLayoutManager *manager); + + /* signals */ + void (* layout_changed) (ClutterLayoutManager *manager); /*< private >*/ /* padding for future expansion */ @@ -184,6 +198,12 @@ void clutter_layout_manager_child_get_property (ClutterLayoutMa const gchar *property_name, GValue *value); +void clutter_layout_manager_begin_animation (ClutterLayoutManager *manager, + guint duration, + gulong mode); +void clutter_layout_manager_end_animation (ClutterLayoutManager *manager); +gdouble clutter_layout_manager_get_animation_progress (ClutterLayoutManager *manager); + G_END_DECLS #endif /* __CLUTTER_LAYOUT_MANAGER_H__ */ diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index d90358739..2bd5f55eb 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1768,6 +1768,11 @@ clutter_layout_manager_child_get_property clutter_layout_manager_find_child_property clutter_layout_manager_list_child_properties + +clutter_layout_manager_begin_animation +clutter_layout_manager_end_animation +clutter_layout_manager_get_animation_progress + CLUTTER_TYPE_LAYOUT_MANAGER CLUTTER_LAYOUT_MANAGER From 3c2e91aef586de5521aa9674b80d449cf96da0e1 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sat, 12 Dec 2009 00:13:05 +0000 Subject: [PATCH 25/83] box-layout: Animate layout properties Use the newly added animation support inside LayoutManager to animate between state changes of the BoxLayout properties. The implementation is based on equivalent code from Mx, written by: Thomas Wood --- clutter/clutter-box-layout.c | 134 ++++++++++++++++++++++++++++++----- 1 file changed, 115 insertions(+), 19 deletions(-) diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c index 6d30bfb93..49b31a686 100644 --- a/clutter/clutter-box-layout.c +++ b/clutter/clutter-box-layout.c @@ -94,8 +94,11 @@ struct _ClutterBoxLayoutPrivate guint spacing; + GHashTable *allocations; + guint is_vertical : 1; guint is_pack_start : 1; + guint is_animating : 1; }; struct _ClutterBoxChild @@ -169,6 +172,7 @@ box_child_set_align (ClutterBoxChild *self, ClutterLayoutManager *layout; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); + clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); clutter_layout_manager_layout_changed (layout); if (x_changed) @@ -205,6 +209,7 @@ box_child_set_fill (ClutterBoxChild *self, ClutterLayoutManager *layout; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); + clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); clutter_layout_manager_layout_changed (layout); if (x_changed) @@ -226,6 +231,7 @@ box_child_set_expand (ClutterBoxChild *self, self->expand = expand; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); + clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); clutter_layout_manager_layout_changed (layout); g_object_notify (G_OBJECT (self), "expand"); @@ -698,14 +704,6 @@ allocate_box_child (ClutterBoxLayout *self, child_box.x1 = 0; child_box.x2 = floorf (avail_width + 0.5); - - allocate_fill (child, &child_box, box_child); - clutter_actor_allocate (child, &child_box, flags); - - if (box_child->expand) - *position += (child_nat + priv->spacing + extra_space); - else - *position += (child_nat + priv->spacing); } else { @@ -721,15 +719,64 @@ allocate_box_child (ClutterBoxLayout *self, child_box.y1 = 0; child_box.y2 = floorf (avail_height + 0.5); - - allocate_fill (child, &child_box, box_child); - clutter_actor_allocate (child, &child_box, flags); - - if (box_child->expand) - *position += (child_nat + priv->spacing + extra_space); - else - *position += (child_nat + priv->spacing); } + + allocate_fill (child, &child_box, box_child); + + if (priv->is_animating) + { + ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self); + ClutterActorBox *start = NULL; + ClutterActorBox end = { 0, }; + gdouble p; + + p = clutter_layout_manager_get_animation_progress (manager); + + start = g_hash_table_lookup (priv->allocations, child); + if (start == NULL) + { + /* if there is no allocation available then the child has just + * been added to the container; we put it in the final state + * and store its allocation for later + */ + start = clutter_actor_box_copy (&child_box); + g_hash_table_insert (priv->allocations, child, start); + + goto do_allocate; + } + + end = child_box; + + /* interpolate between the initial and final values */ + clutter_actor_box_interpolate (start, &end, p, &child_box); + + CLUTTER_NOTE (ANIMATION, + "Animate { %.1f, %.1f, %.1f, %.1f }\t" + "%.3f * { %.1f, %.1f, %.1f, %.1f }\t" + "-> { %.1f, %.1f, %.1f, %.1f }", + start->x1, start->y1, + start->x2, start->y2, + p, + child_box.x1, child_box.y1, + child_box.x2, child_box.y2, + end.x1, end.y1, + end.x2, end.y2); + } + else + { + ClutterActorBox *start = clutter_actor_box_copy (&child_box); + + /* store the allocation for later animations */ + g_hash_table_replace (priv->allocations, child, start); + } + +do_allocate: + clutter_actor_allocate (child, &child_box, flags); + + if (box_child->expand) + *position += (child_nat + priv->spacing + extra_space); + else + *position += (child_nat + priv->spacing); } static void @@ -887,6 +934,37 @@ clutter_box_layout_allocate (ClutterLayoutManager *layout, g_list_free (children); } +static void +clutter_box_layout_begin_animation (ClutterLayoutManager *manager, + guint duration, + gulong easing) +{ + ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (manager)->priv; + ClutterLayoutManagerClass *parent_class; + + if (priv->is_animating) + return; + + priv->is_animating = TRUE; + + /* we want the default implementation */ + parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_box_layout_parent_class); + parent_class->begin_animation (manager, duration, easing); +} + +static void +clutter_box_layout_end_animation (ClutterLayoutManager *manager) +{ + ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (manager)->priv; + ClutterLayoutManagerClass *parent_class; + + priv->is_animating = FALSE; + + /* we want the default implementation */ + parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_box_layout_parent_class); + parent_class->end_animation (manager); +} + static void clutter_box_layout_set_property (GObject *gobject, guint prop_id, @@ -943,6 +1021,16 @@ clutter_box_layout_get_property (GObject *gobject, } } +static void +clutter_box_layout_finalize (GObject *gobject) +{ + ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (gobject)->priv; + + g_hash_table_destroy (priv->allocations); + + G_OBJECT_CLASS (clutter_box_layout_parent_class)->finalize (gobject); +} + static void clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) { @@ -954,6 +1042,7 @@ clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) gobject_class->set_property = clutter_box_layout_set_property; gobject_class->get_property = clutter_box_layout_get_property; + gobject_class->finalize = clutter_box_layout_finalize; layout_class->get_preferred_width = clutter_box_layout_get_preferred_width; @@ -963,6 +1052,8 @@ clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) layout_class->set_container = clutter_box_layout_set_container; layout_class->get_child_meta_type = clutter_box_layout_get_child_meta_type; + layout_class->begin_animation = clutter_box_layout_begin_animation; + layout_class->end_animation = clutter_box_layout_end_animation; g_type_class_add_private (klass, sizeof (ClutterBoxLayoutPrivate)); @@ -1022,6 +1113,11 @@ clutter_box_layout_init (ClutterBoxLayout *layout) priv->is_vertical = FALSE; priv->is_pack_start = FALSE; priv->spacing = 0; + + priv->allocations = + g_hash_table_new_full (NULL, NULL, + NULL, + (GDestroyNotify) clutter_actor_box_free); } /** @@ -1065,7 +1161,7 @@ clutter_box_layout_set_spacing (ClutterBoxLayout *layout, priv->spacing = spacing; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_layout_changed (manager); + clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); g_object_notify (G_OBJECT (layout), "spacing"); } @@ -1116,7 +1212,7 @@ clutter_box_layout_set_vertical (ClutterBoxLayout *layout, priv->is_vertical = vertical ? TRUE : FALSE; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_layout_changed (manager); + clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); g_object_notify (G_OBJECT (layout), "vertical"); } @@ -1170,7 +1266,7 @@ clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, priv->is_pack_start = pack_start ? TRUE : FALSE; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_layout_changed (manager); + clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); g_object_notify (G_OBJECT (layout), "pack-start"); } From 2e6397c39132007e79f5f87743fe9378fd8d5387 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sun, 13 Dec 2009 01:07:44 +0000 Subject: [PATCH 26/83] box-layout: Add knobs for controlling animations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClutterLayoutManager does not have any state associated with it, and defers all the state to its sub-classes. The BoxLayout is thus in charge of controlling: • whether or not animations should be used • the duration of the animation • the easing mode of the animation By adding three new properties: • ClutterBoxLayout:use-animations • ClutterBoxLayout:easing-duration • ClutterBoxLayout:easing-mode And their relative accessors pairs we can make BoxLayout decide whether or not, and with which parameters, call the begin_animation() method of ClutterLayoutManager. The test-box-layout has been modified to reflect this new functionality, by checking the key-press event for the 'a' key symbol to toggle the use of animations. --- clutter/clutter-box-layout.c | 320 ++++++++++++++++++++- clutter/clutter-box-layout.h | 86 +++--- doc/reference/clutter/clutter-sections.txt | 8 + tests/interactive/test-box-layout.c | 6 + 4 files changed, 368 insertions(+), 52 deletions(-) diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c index 49b31a686..00b16256a 100644 --- a/clutter/clutter-box-layout.c +++ b/clutter/clutter-box-layout.c @@ -94,11 +94,14 @@ struct _ClutterBoxLayoutPrivate guint spacing; + gulong easing_mode; + guint easing_duration; GHashTable *allocations; - guint is_vertical : 1; - guint is_pack_start : 1; - guint is_animating : 1; + guint is_vertical : 1; + guint is_pack_start : 1; + guint is_animating : 1; + guint use_animations : 1; }; struct _ClutterBoxChild @@ -131,7 +134,10 @@ enum PROP_SPACING, PROP_VERTICAL, - PROP_PACK_START + PROP_PACK_START, + PROP_USE_ANIMATIONS, + PROP_EASING_MODE, + PROP_EASING_DURATION }; G_DEFINE_TYPE (ClutterBoxChild, @@ -170,10 +176,19 @@ box_child_set_align (ClutterBoxChild *self, if (x_changed || y_changed) { ClutterLayoutManager *layout; + ClutterBoxLayout *box; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); - clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); - clutter_layout_manager_layout_changed (layout); + box = CLUTTER_BOX_LAYOUT (layout); + + if (box->priv->use_animations) + { + clutter_layout_manager_begin_animation (layout, + box->priv->easing_duration, + box->priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (layout); if (x_changed) g_object_notify (G_OBJECT (self), "x-align"); @@ -207,10 +222,19 @@ box_child_set_fill (ClutterBoxChild *self, if (x_changed || y_changed) { ClutterLayoutManager *layout; + ClutterBoxLayout *box; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); - clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); - clutter_layout_manager_layout_changed (layout); + box = CLUTTER_BOX_LAYOUT (layout); + + if (box->priv->use_animations) + { + clutter_layout_manager_begin_animation (layout, + box->priv->easing_duration, + box->priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (layout); if (x_changed) g_object_notify (G_OBJECT (self), "x-fill"); @@ -227,12 +251,21 @@ box_child_set_expand (ClutterBoxChild *self, if (self->expand != expand) { ClutterLayoutManager *layout; + ClutterBoxLayout *box; self->expand = expand; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); - clutter_layout_manager_begin_animation (layout, 500, CLUTTER_EASE_OUT_CUBIC); - clutter_layout_manager_layout_changed (layout); + box = CLUTTER_BOX_LAYOUT (layout); + + if (box->priv->use_animations) + { + clutter_layout_manager_begin_animation (layout, + box->priv->easing_duration, + box->priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (layout); g_object_notify (G_OBJECT (self), "expand"); } @@ -723,7 +756,7 @@ allocate_box_child (ClutterBoxLayout *self, allocate_fill (child, &child_box, box_child); - if (priv->is_animating) + if (priv->use_animations && priv->is_animating) { ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self); ClutterActorBox *start = NULL; @@ -987,6 +1020,18 @@ clutter_box_layout_set_property (GObject *gobject, clutter_box_layout_set_pack_start (self, g_value_get_boolean (value)); break; + case PROP_USE_ANIMATIONS: + clutter_box_layout_set_use_animations (self, g_value_get_boolean (value)); + break; + + case PROP_EASING_MODE: + clutter_box_layout_set_easing_mode (self, g_value_get_ulong (value)); + break; + + case PROP_EASING_DURATION: + clutter_box_layout_set_easing_duration (self, g_value_get_uint (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -1015,6 +1060,18 @@ clutter_box_layout_get_property (GObject *gobject, g_value_set_boolean (value, priv->is_pack_start); break; + case PROP_USE_ANIMATIONS: + g_value_set_boolean (value, priv->use_animations); + break; + + case PROP_EASING_MODE: + g_value_set_ulong (value, priv->easing_mode); + break; + + case PROP_EASING_DURATION: + g_value_set_uint (value, priv->easing_duration); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -1101,6 +1158,62 @@ clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) 0, G_MAXUINT, 0, CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_SPACING, pspec); + + /** + * ClutterBoxLayout:use-animations: + * + * Whether the #ClutterBoxLayout should animate changes in the + * layout properties + * + * Since: 1.2 + */ + pspec = g_param_spec_boolean ("use-animations", + "Use Animations", + "Whether layout changes should be animated", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_USE_ANIMATIONS, pspec); + + /** + * ClutterBoxLayout:easing-mode: + * + * The easing mode for the animations, in case + * #ClutterBoxLayout:use-animations is set to %TRUE + * + * The easing mode has the same semantics of #ClutterAnimation:mode: it can + * either be a value from the #ClutterAnimationMode enumeration, like + * %CLUTTER_EASE_OUT_CUBIC, or a logical id as returned by + * clutter_alpha_register_func() + * + * The default value is %CLUTTER_EASE_OUT_CUBIC + * + * Since: 1.2 + */ + pspec = g_param_spec_ulong ("easing-mode", + "Easing Mode", + "The easing mode of the animations", + 0, G_MAXULONG, + CLUTTER_EASE_OUT_CUBIC, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_EASING_MODE, pspec); + + /** + * ClutterBoxLayout:easing-duration: + * + * The duration of the animations, in case #ClutterBoxLayout:use-animations + * is set to %TRUE + * + * The duration is expressed in milliseconds + * + * Since: 1.2 + */ + pspec = g_param_spec_uint ("easing-duration", + "Easing Duration", + "The duration of the animations", + 0, G_MAXUINT, + 500, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_EASING_DURATION, pspec); } static void @@ -1114,6 +1227,10 @@ clutter_box_layout_init (ClutterBoxLayout *layout) priv->is_pack_start = FALSE; priv->spacing = 0; + priv->use_animations = FALSE; + priv->easing_mode = CLUTTER_EASE_OUT_CUBIC; + priv->easing_duration = 500; + priv->allocations = g_hash_table_new_full (NULL, NULL, NULL, @@ -1161,7 +1278,15 @@ clutter_box_layout_set_spacing (ClutterBoxLayout *layout, priv->spacing = spacing; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); + + if (priv->use_animations) + { + clutter_layout_manager_begin_animation (manager, + priv->easing_duration, + priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (manager); g_object_notify (G_OBJECT (layout), "spacing"); } @@ -1212,7 +1337,15 @@ clutter_box_layout_set_vertical (ClutterBoxLayout *layout, priv->is_vertical = vertical ? TRUE : FALSE; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); + + if (priv->use_animations) + { + clutter_layout_manager_begin_animation (manager, + priv->easing_duration, + priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (manager); g_object_notify (G_OBJECT (layout), "vertical"); } @@ -1266,7 +1399,15 @@ clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, priv->is_pack_start = pack_start ? TRUE : FALSE; manager = CLUTTER_LAYOUT_MANAGER (layout); - clutter_layout_manager_begin_animation (manager, 500, CLUTTER_EASE_OUT_CUBIC); + + if (priv->use_animations) + { + clutter_layout_manager_begin_animation (manager, + priv->easing_duration, + priv->easing_mode); + } + else + clutter_layout_manager_layout_changed (manager); g_object_notify (G_OBJECT (layout), "pack-start"); } @@ -1670,3 +1811,154 @@ clutter_box_layout_get_expand (ClutterBoxLayout *layout, return CLUTTER_BOX_CHILD (meta)->expand; } + +/** + * clutter_box_layout_set_use_animations: + * @layout: a #ClutterBoxLayout + * @animate: %TRUE if the @layout should use animations + * + * Sets whether @layout should animate changes in the layout properties + * + * The duration of the animations is controlled by + * clutter_box_layout_set_easing_duration(); the easing mode to be used + * by the animations is controlled by clutter_box_layout_set_easing_mode() + * + * Since: 1.2 + */ +void +clutter_box_layout_set_use_animations (ClutterBoxLayout *layout, + gboolean animate) +{ + ClutterBoxLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->use_animations != animate) + { + priv->use_animations = animate; + + g_object_notify (G_OBJECT (layout), "use-animations"); + } +} + +/** + * clutter_box_layout_get_use_animations: + * @layout: a #ClutterBoxLayout + * + * Retrieves whether @layout should animate changes in the layout properties + * + * Since clutter_box_layout_set_use_animations() + * + * Return value: %TRUE if the animations should be used, %FALSE otherwise + * + * Since: 1.2 + */ +gboolean +clutter_box_layout_get_use_animations (ClutterBoxLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE); + + return layout->priv->use_animations; +} + +/** + * clutter_box_layout_set_easing_mode: + * @layout: a #ClutterBoxLayout + * @mode: an easing mode, either from #ClutterAnimationMode or a logical id + * from clutter_alpha_register_func() + * + * Sets the easing mode to be used by @layout when animating changes in layout + * properties + * + * Use clutter_box_layout_set_use_animations() to enable and disable the + * animations + * + * Since: 1.2 + */ +void +clutter_box_layout_set_easing_mode (ClutterBoxLayout *layout, + gulong mode) +{ + ClutterBoxLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->easing_mode != mode) + { + priv->easing_mode = mode; + + g_object_notify (G_OBJECT (layout), "easing-mode"); + } +} + +/** + * clutter_box_layout_get_easing_mode: + * @layout: a #ClutterBoxLayout + * + * Retrieves the easing mode set using clutter_box_layout_set_easing_mode() + * + * Return value: an easing mode + * + * Since: 1.2 + */ +gulong +clutter_box_layout_get_easing_mode (ClutterBoxLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), + CLUTTER_EASE_OUT_CUBIC); + + return layout->priv->easing_mode; +} + +/** + * clutter_box_layout_set_easing_duration: + * @layout: a #ClutterBoxLayout + * @msecs: the duration of the animations, in milliseconds + * + * Sets the duration of the animations used by @layout when animating changes + * in the layout properties + * + * Use clutter_box_layout_set_use_animations() to enable and disable the + * animations + * + * Since: 1.2 + */ +void +clutter_box_layout_set_easing_duration (ClutterBoxLayout *layout, + guint msecs) +{ + ClutterBoxLayoutPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); + + priv = layout->priv; + + if (priv->easing_duration != msecs) + { + priv->easing_duration = msecs; + + g_object_notify (G_OBJECT (layout), "easing-duration"); + } +} + +/** + * clutter_box_layout_get_easing_duration: + * @layout: a #ClutterBoxLayout + * + * Retrieves the duration set using clutter_box_layout_set_easing_duration() + * + * Return value: the duration of the animations, in milliseconds + * + * Since: 1.2 + */ +guint +clutter_box_layout_get_easing_duration (ClutterBoxLayout *layout) +{ + g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), 500); + + return layout->priv->easing_duration; +} diff --git a/clutter/clutter-box-layout.h b/clutter/clutter-box-layout.h index be2be9130..035368ac2 100644 --- a/clutter/clutter-box-layout.h +++ b/clutter/clutter-box-layout.h @@ -97,46 +97,56 @@ struct _ClutterBoxLayoutClass GType clutter_box_layout_get_type (void) G_GNUC_CONST; -ClutterLayoutManager *clutter_box_layout_new (void); +ClutterLayoutManager *clutter_box_layout_new (void); -void clutter_box_layout_set_spacing (ClutterBoxLayout *layout, - guint spacing); -guint clutter_box_layout_get_spacing (ClutterBoxLayout *layout); -void clutter_box_layout_set_vertical (ClutterBoxLayout *layout, - gboolean vertical); -gboolean clutter_box_layout_get_vertical (ClutterBoxLayout *layout); -void clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, - gboolean pack_start); -gboolean clutter_box_layout_get_pack_start (ClutterBoxLayout *layout); +void clutter_box_layout_set_spacing (ClutterBoxLayout *layout, + guint spacing); +guint clutter_box_layout_get_spacing (ClutterBoxLayout *layout); +void clutter_box_layout_set_vertical (ClutterBoxLayout *layout, + gboolean vertical); +gboolean clutter_box_layout_get_vertical (ClutterBoxLayout *layout); +void clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, + gboolean pack_start); +gboolean clutter_box_layout_get_pack_start (ClutterBoxLayout *layout); -void clutter_box_layout_pack (ClutterBoxLayout *layout, - ClutterActor *actor, - gboolean expand, - gboolean x_fill, - gboolean y_fill, - ClutterBoxAlignment x_align, - ClutterBoxAlignment y_align); -void clutter_box_layout_set_alignment (ClutterBoxLayout *layout, - ClutterActor *actor, - ClutterBoxAlignment x_align, - ClutterBoxAlignment y_align); -void clutter_box_layout_get_alignment (ClutterBoxLayout *layout, - ClutterActor *actor, - ClutterBoxAlignment *x_align, - ClutterBoxAlignment *y_align); -void clutter_box_layout_set_fill (ClutterBoxLayout *layout, - ClutterActor *actor, - gboolean x_fill, - gboolean y_fill); -void clutter_box_layout_get_fill (ClutterBoxLayout *layout, - ClutterActor *actor, - gboolean *x_fill, - gboolean *y_fill); -void clutter_box_layout_set_expand (ClutterBoxLayout *layout, - ClutterActor *actor, - gboolean expand); -gboolean clutter_box_layout_get_expand (ClutterBoxLayout *layout, - ClutterActor *actor); +void clutter_box_layout_pack (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean expand, + gboolean x_fill, + gboolean y_fill, + ClutterBoxAlignment x_align, + ClutterBoxAlignment y_align); +void clutter_box_layout_set_alignment (ClutterBoxLayout *layout, + ClutterActor *actor, + ClutterBoxAlignment x_align, + ClutterBoxAlignment y_align); +void clutter_box_layout_get_alignment (ClutterBoxLayout *layout, + ClutterActor *actor, + ClutterBoxAlignment *x_align, + ClutterBoxAlignment *y_align); +void clutter_box_layout_set_fill (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean x_fill, + gboolean y_fill); +void clutter_box_layout_get_fill (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean *x_fill, + gboolean *y_fill); +void clutter_box_layout_set_expand (ClutterBoxLayout *layout, + ClutterActor *actor, + gboolean expand); +gboolean clutter_box_layout_get_expand (ClutterBoxLayout *layout, + ClutterActor *actor); + +void clutter_box_layout_set_use_animations (ClutterBoxLayout *layout, + gboolean animate); +gboolean clutter_box_layout_get_use_animations (ClutterBoxLayout *layout); +void clutter_box_layout_set_easing_mode (ClutterBoxLayout *layout, + gulong mode); +gulong clutter_box_layout_get_easing_mode (ClutterBoxLayout *layout); +void clutter_box_layout_set_easing_duration (ClutterBoxLayout *layout, + guint msecs); +guint clutter_box_layout_get_easing_duration (ClutterBoxLayout *layout); G_END_DECLS diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 2bd5f55eb..8ad28f785 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1937,6 +1937,14 @@ clutter_box_layout_get_expand clutter_box_layout_set_fill clutter_box_layout_get_fill + +clutter_box_layout_set_use_animations +clutter_box_layout_get_use_animations +clutter_box_layout_set_easing_duration +clutter_box_layout_get_easing_duration +clutter_box_layout_set_easing_mode +clutter_box_layout_get_easing_mode + CLUTTER_TYPE_BOX_LAYOUT CLUTTER_BOX_LAYOUT diff --git a/tests/interactive/test-box-layout.c b/tests/interactive/test-box-layout.c index e45631880..a8b72453a 100644 --- a/tests/interactive/test-box-layout.c +++ b/tests/interactive/test-box-layout.c @@ -29,6 +29,7 @@ "Press v\t\342\236\236\tSwitch horizontal/vertical\n" \ "Press p\t\342\236\236\tSwitch pack start/end\n" \ "Press s\t\342\236\236\tIncrement spacing (up to 12px)\n" \ + "Press a\t\342\236\236\tSwitch animations on/off\n" \ "Press q\t\342\236\236\tQuit" static ClutterActor *hover_actor = NULL; @@ -167,6 +168,11 @@ key_release_cb (ClutterActor *actor, switch (clutter_event_get_key_symbol (event)) { + case CLUTTER_a: + toggle = clutter_box_layout_get_use_animations (layout); + clutter_box_layout_set_use_animations (layout, !toggle); + break; + case CLUTTER_v: toggle = clutter_box_layout_get_vertical (layout); clutter_box_layout_set_vertical (layout, !toggle); From 4a21425f4837798d01f4435b68582faf9029c61c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sun, 13 Dec 2009 01:23:54 +0000 Subject: [PATCH 27/83] layout: Let begin_animation() return the Alpha When beginning a new animation for a LayoutManager, the implementation should return the ClutterAlpha used. This allows controlling the timeline and/or modifying the animation parameters on the fly. --- clutter/clutter-box-layout.c | 5 +++-- clutter/clutter-layout-manager.c | 30 ++++++++++++++++++++++-------- clutter/clutter-layout-manager.h | 5 +++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c index 00b16256a..f9a7dbb0d 100644 --- a/clutter/clutter-box-layout.c +++ b/clutter/clutter-box-layout.c @@ -967,7 +967,7 @@ clutter_box_layout_allocate (ClutterLayoutManager *layout, g_list_free (children); } -static void +static ClutterAlpha * clutter_box_layout_begin_animation (ClutterLayoutManager *manager, guint duration, gulong easing) @@ -982,7 +982,8 @@ clutter_box_layout_begin_animation (ClutterLayoutManager *manager, /* we want the default implementation */ parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_box_layout_parent_class); - parent_class->begin_animation (manager, duration, easing); + + return parent_class->begin_animation (manager, duration, easing); } static void diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 251f3e647..35b624f1d 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -226,7 +226,7 @@ layout_manager_real_get_child_meta_type (ClutterLayoutManager *manager) return G_TYPE_INVALID; } -static void +static ClutterAlpha * layout_manager_real_begin_animation (ClutterLayoutManager *manager, guint duration, gulong mode) @@ -236,7 +236,14 @@ layout_manager_real_begin_animation (ClutterLayoutManager *manager, alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); if (alpha != NULL) - return; + { + timeline = clutter_alpha_get_timeline (alpha); + clutter_timeline_set_duration (timeline, duration); + + clutter_alpha_set_mode (alpha, mode); + + return alpha; + }; timeline = clutter_timeline_new (duration); alpha = clutter_alpha_new_full (timeline, mode); @@ -254,6 +261,8 @@ layout_manager_real_begin_animation (ClutterLayoutManager *manager, (GDestroyNotify) g_object_unref); clutter_timeline_start (timeline); + + return alpha; } static gdouble @@ -1004,19 +1013,24 @@ clutter_layout_manager_list_child_properties (ClutterLayoutManager *manager, * * The result of this function depends on the @manager implementation * + * Return value: (transfer none): The #ClutterAlpha created by the + * layout manager; the returned instance is owned by the layout + * manager and should not be unreferenced + * * Since: 1.2 */ -void +ClutterAlpha * clutter_layout_manager_begin_animation (ClutterLayoutManager *manager, guint duration, gulong mode) { - g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); - g_return_if_fail (duration > 0 && duration < 1000); + ClutterLayoutManagerClass *klass; - CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager)->begin_animation (manager, - duration, - mode); + g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), NULL); + + klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); + + return klass->begin_animation (manager, duration, mode); } /** diff --git a/clutter/clutter-layout-manager.h b/clutter/clutter-layout-manager.h index 1271a3db4..e249b6b1a 100644 --- a/clutter/clutter-layout-manager.h +++ b/clutter/clutter-layout-manager.h @@ -30,6 +30,7 @@ #define __CLUTTER_LAYOUT_MANAGER_H__ #include +#include #include #include @@ -126,7 +127,7 @@ struct _ClutterLayoutManagerClass ClutterContainer *container, ClutterActor *actor); - void (* begin_animation) (ClutterLayoutManager *manager, + ClutterAlpha * (* begin_animation) (ClutterLayoutManager *manager, guint duration, gulong mode); gdouble (* get_animation_progress) (ClutterLayoutManager *manager); @@ -198,7 +199,7 @@ void clutter_layout_manager_child_get_property (ClutterLayoutMa const gchar *property_name, GValue *value); -void clutter_layout_manager_begin_animation (ClutterLayoutManager *manager, +ClutterAlpha * clutter_layout_manager_begin_animation (ClutterLayoutManager *manager, guint duration, gulong mode); void clutter_layout_manager_end_animation (ClutterLayoutManager *manager); From 1520ba6190165dc8902b622833efbccf23716751 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 18 Dec 2009 23:20:04 +0000 Subject: [PATCH 28/83] actor: Add internal child flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClutterActor checks, when destroying and reparenting, if the parent actor implements the Container interface, and automatically calls the remove() method to perform a clean removal. Actors implementing Container, though, might have internal children; that is, children that are not added through the Container API. It is already possible to iterate through them using the Container API to avoid breaking invariants - but calling clutter_actor_destroy() on these children (even from the Container implementation, and thus outside of Clutter's control) will either lead to leaks or to segmentation faults. Clutter needs a way to distinguish a clutter_actor_set_parent() done on an internal child from one done on a "public" child; for this reason, a push/pop pair of functions should be available to Actor implementations to mark the section where they wish to add internal children: ➔ clutter_actor_push_internal (); ... clutter_actor_set_parent (child1, parent); clutter_actor_set_parent (child2, parent); ... ➔ clutter_actor_pop_internal (); The set_parent() call will automatically set the newly added INTERNAL_CHILD private flag on each child, and both clutter_actor_destroy() and clutter_actor_unparent() will check for the flag before deciding whether to call the Container's remove method. --- clutter/clutter-actor.c | 110 ++++++++++++++++++++- clutter/clutter-actor.h | 3 + clutter/clutter-private.h | 29 ++++-- doc/reference/clutter/clutter-sections.txt | 2 + 4 files changed, 132 insertions(+), 12 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index f676d5e3b..b291cd2b8 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -3001,8 +3001,14 @@ clutter_actor_dispose (GObject *object) { ClutterActor *parent = priv->parent_actor; - if (CLUTTER_IS_CONTAINER (parent)) - clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self); + /* go through the Container implementation unless this + * is an internal child and has been marked as such + */ + if (CLUTTER_IS_CONTAINER (parent) && + !(CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_INTERNAL_CHILD)) + { + clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self); + } else priv->parent_actor = NULL; } @@ -6619,6 +6625,7 @@ clutter_actor_set_parent (ClutterActor *self, { ClutterActorPrivate *priv; ClutterTextDirection text_dir; + ClutterMainContext *ctx; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (parent)); @@ -6648,6 +6655,14 @@ clutter_actor_set_parent (ClutterActor *self, g_object_ref_sink (self); priv->parent_actor = parent; + ctx = _clutter_context_get_default (); + + /* if push_internal() has been called then we automatically set + * the flag on the actor + */ + if (ctx->internal_child) + CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_INTERNAL_CHILD); + /* clutter_actor_reparent() will emit ::parent-set for us */ if (!(CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_IN_REPARENT)) g_signal_emit (self, actor_signals[PARENT_SET], 0, NULL); @@ -6825,17 +6840,22 @@ clutter_actor_reparent (ClutterActor *self, g_object_ref (self); - if (CLUTTER_IS_CONTAINER (priv->parent_actor)) + /* go through the Container implementation if this is a regular + * child and not an internal one + */ + if (CLUTTER_IS_CONTAINER (priv->parent_actor) && + !(CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_INTERNAL_CHILD)) { ClutterContainer *parent = CLUTTER_CONTAINER (priv->parent_actor); - /* Note, will call unparent() */ + + /* this will have to call unparent() */ clutter_container_remove_actor (parent, self); } else clutter_actor_unparent (self); - if (CLUTTER_IS_CONTAINER (new_parent)) /* Note, will call parent() */ + if (CLUTTER_IS_CONTAINER (new_parent)) clutter_container_add_actor (CLUTTER_CONTAINER (new_parent), self); else clutter_actor_set_parent (self, new_parent); @@ -9471,3 +9491,83 @@ clutter_actor_get_text_direction (ClutterActor *self) return priv->text_direction; } + +/** + * clutter_actor_push_internal: + * + * Should be used by actors implementing the #ClutterContainer and with + * internal children added through clutter_actor_set_parent(), for instance: + * + * |[ + * static void + * my_actor_init (MyActor *self) + * { + * self->priv = SELF_ACTOR_GET_PRIVATE (self); + * + * clutter_actor_push_internal (); + * + * /* calling clutter_actor_set_parent() now will result in + * * the internal flag being set on a child of MyActor + * */ + * + * /* internal child: a background texture */ + * self->priv->background_tex = clutter_texture_new (); + * clutter_actor_set_parent (self->priv->background_tex, + * CLUTTER_ACTOR (self)); + * + * /* internal child: a label */ + * self->priv->label = clutter_text_new (); + * clutter_actor_set_parent (self->priv->label, + * CLUTTER_ACTOR (self)); + * + * clutter_actor_pop_internal (); + * + * /* calling clutter_actor_set_parent() now will not result in + * * the internal flag being set on a child of MyActor + * */ + * } + * ]| + * + * This function will be used by Clutter to toggle an "internal child" + * flag whenever clutter_actor_set_parent() is called; internal children + * are handled differently by Clutter, specifically when destroying their + * parent. + * + * Call clutter_actor_pop_internal() when you finished adding internal + * children. + * + * Nested calls to clutter_actor_push_internal() are allowed, but each + * one must by followed by a clutter_actor_pop_internal() call. + * + * Since: 1.2 + */ +void +clutter_actor_push_internal (void) +{ + ClutterMainContext *ctx = _clutter_context_get_default (); + + ctx->internal_child += 1; +} + +/** + * clutter_actor_pop_internal: + * + * Disables the effects of clutter_actor_pop_internal() + * + * Since: 1.2 + */ +void +clutter_actor_pop_internal (void) +{ + ClutterMainContext *ctx = _clutter_context_get_default (); + + if (ctx->internal_child == 0) + { + g_warning ("Mismatched %s: you need to call " + "clutter_actor_push_composite() at least once before " + "calling this function", G_STRFUNC); + return; + } + + ctx->internal_child -= 1; +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 2f190a5ed..33c3ae773 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -532,6 +532,9 @@ void clutter_actor_set_text_direction (ClutterActor *sel ClutterTextDirection text_dir); ClutterTextDirection clutter_actor_get_text_direction (ClutterActor *self); +void clutter_actor_push_internal (void); +void clutter_actor_pop_internal (void); + G_END_DECLS #endif /* __CLUTTER_ACTOR_H__ */ diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index db9a6d2d0..d272ca9a4 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -58,13 +58,26 @@ typedef enum { CLUTTER_ACTOR_IN_DESTRUCTION = 1 << 0, CLUTTER_ACTOR_IS_TOPLEVEL = 1 << 1, CLUTTER_ACTOR_IN_REPARENT = 1 << 2, - CLUTTER_ACTOR_SYNC_MATRICES = 1 << 3, /* Used by stage to indicate GL - * viewport / perspective etc - * needs (re)setting. - */ - CLUTTER_ACTOR_IN_PAINT = 1 << 4, /* Used to avoid recursion */ - CLUTTER_ACTOR_IN_RELAYOUT = 1 << 5, /* Used to avoid recursion */ - CLUTTER_STAGE_IN_RESIZE = 1 << 6 + + /* Used by the stage to indicate GL viewport / perspective etc needs + * (re)setting. + */ + CLUTTER_ACTOR_SYNC_MATRICES = 1 << 3, + + /* Used to avoid recursion */ + CLUTTER_ACTOR_IN_PAINT = 1 << 4, + + /* Used to avoid recursion */ + CLUTTER_ACTOR_IN_RELAYOUT = 1 << 5, + + /* Used by the stage if resizing is an asynchronous operation (like on + * X11) to delay queueing relayouts until we got a notification from the + * event handling + */ + CLUTTER_STAGE_IN_RESIZE = 1 << 6, + + /* a flag for internal children of Containers */ + CLUTTER_ACTOR_INTERNAL_CHILD = 1 << 7 } ClutterPrivateFlags; struct _ClutterInputDevice @@ -133,6 +146,8 @@ struct _ClutterMainContext gulong redraw_count; GList *repaint_funcs; + + gint internal_child; }; #define CLUTTER_CONTEXT() (_clutter_context_get_default ()) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index e7dafd772..ef125b011 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -352,6 +352,8 @@ clutter_actor_lower clutter_actor_raise_top clutter_actor_lower_bottom clutter_actor_get_stage +clutter_actor_push_internal +clutter_actor_pop_internal clutter_actor_set_depth From c7e8ddb7a60dd76d5f62027383693575f30e2473 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 18 Dec 2009 23:29:11 +0000 Subject: [PATCH 29/83] conform: Add test unit for the destruction of Containers Verify that internal children do not go through the remove() implementation, and that non-internal children do. --- .gitignore | 1 + tests/conform/Makefile.am | 1 + tests/conform/test-actor-destroy.c | 168 +++++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 1 + 4 files changed, 171 insertions(+) create mode 100644 tests/conform/test-actor-destroy.c diff --git a/.gitignore b/.gitignore index 70aa83036..bdcab8ce2 100644 --- a/.gitignore +++ b/.gitignore @@ -236,6 +236,7 @@ TAGS /tests/conform/test-script-object-property /tests/conform/test-script-animation /tests/conform/test-script-named-object +/tests/conform/test-actor-destruction /tests/micro-bench/test-text-perf /tests/micro-bench/test-text /tests/micro-bench/test-picking diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index b1bb81ec3..a131ee2ee 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -40,6 +40,7 @@ test_conformance_SOURCES = \ test-actor-size.c \ test-texture-fbo.c \ test-script-parser.c \ + test-actor-destroy.c \ $(NULL) # For convenience, this provides a way to easily run individual unit tests: diff --git a/tests/conform/test-actor-destroy.c b/tests/conform/test-actor-destroy.c new file mode 100644 index 000000000..8cc0d48f4 --- /dev/null +++ b/tests/conform/test-actor-destroy.c @@ -0,0 +1,168 @@ +#include +#include "test-conform-common.h" + +#define TEST_TYPE_DESTROY (test_destroy_get_type ()) +#define TEST_DESTROY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_DESTROY, TestDestroy)) +#define TEST_IS_DESTROY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_DESTROY)) + +typedef struct _TestDestroy TestDestroy; +typedef struct _TestDestroyClass TestDestroyClass; + +struct _TestDestroy +{ + ClutterActor parent_instance; + + ClutterActor *bg; + ClutterActor *label; + ClutterActor *tex; + + GList *children; +}; + +struct _TestDestroyClass +{ + ClutterActorClass parent_class; +}; + +static void clutter_container_init (ClutterContainerIface *iface); + +G_DEFINE_TYPE_WITH_CODE (TestDestroy, test_destroy, CLUTTER_TYPE_ACTOR, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, + clutter_container_init)); + +static void +test_destroy_add (ClutterContainer *container, + ClutterActor *actor) +{ + TestDestroy *self = TEST_DESTROY (container); + + if (g_test_verbose ()) + g_print ("Adding '%s' (type:%s)\n", + clutter_actor_get_name (actor), + G_OBJECT_TYPE_NAME (actor)); + + self->children = g_list_prepend (self->children, actor); + clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); +} + +static void +test_destroy_remove (ClutterContainer *container, + ClutterActor *actor) +{ + TestDestroy *self = TEST_DESTROY (container); + + if (g_test_verbose ()) + g_print ("Removing '%s' (type:%s)\n", + clutter_actor_get_name (actor), + G_OBJECT_TYPE_NAME (actor)); + + g_assert (actor != self->bg); + g_assert (actor != self->label); + + if (!g_list_find (self->children, actor)) + g_assert (actor == self->tex); + else + self->children = g_list_remove (self->children, actor); + + clutter_actor_unparent (actor); +} + +static void +clutter_container_init (ClutterContainerIface *iface) +{ + iface->add = test_destroy_add; + iface->remove = test_destroy_remove; +} + +static void +test_destroy_destroy (ClutterActor *self) +{ + TestDestroy *test = TEST_DESTROY (self); + + if (test->bg != NULL) + { + if (g_test_verbose ()) + g_print ("Destroying '%s' (type:%s)\n", + clutter_actor_get_name (test->bg), + G_OBJECT_TYPE_NAME (test->bg)); + + clutter_actor_destroy (test->bg); + test->bg = NULL; + } + + if (test->label != NULL) + { + if (g_test_verbose ()) + g_print ("Destroying '%s' (type:%s)\n", + clutter_actor_get_name (test->label), + G_OBJECT_TYPE_NAME (test->label)); + + clutter_actor_destroy (test->label); + test->label = NULL; + } + + if (test->tex != NULL) + { + if (g_test_verbose ()) + g_print ("Destroying '%s' (type:%s)\n", + clutter_actor_get_name (test->tex), + G_OBJECT_TYPE_NAME (test->tex)); + + clutter_actor_destroy (test->tex); + test->tex = NULL; + } + + g_list_foreach (test->children, (GFunc) clutter_actor_destroy, NULL); + g_list_free (test->children); + test->children = NULL; + + if (CLUTTER_ACTOR_CLASS (test_destroy_parent_class)->destroy) + CLUTTER_ACTOR_CLASS (test_destroy_parent_class)->destroy (self); +} + +static void +test_destroy_class_init (TestDestroyClass *klass) +{ + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + actor_class->destroy = test_destroy_destroy; +} + +static void +test_destroy_init (TestDestroy *self) +{ + clutter_actor_push_internal (); + + if (g_test_verbose ()) + g_print ("Adding internal children...\n"); + + self->bg = clutter_rectangle_new (); + clutter_actor_set_parent (self->bg, CLUTTER_ACTOR (self)); + clutter_actor_set_name (self->bg, "Background"); + + self->label = clutter_text_new (); + clutter_actor_set_parent (self->label, CLUTTER_ACTOR (self)); + clutter_actor_set_name (self->label, "Label"); + + clutter_actor_pop_internal (); + + self->tex = clutter_texture_new (); + clutter_actor_set_parent (self->tex, CLUTTER_ACTOR (self)); + clutter_actor_set_name (self->tex, "Texture"); +} + +void +test_actor_destruction (TestConformSimpleFixture *fixture, + gconstpointer dummy) +{ + ClutterActor *test = g_object_new (TEST_TYPE_DESTROY, NULL); + ClutterActor *child = clutter_rectangle_new (); + + if (g_test_verbose ()) + g_print ("Adding external child...\n"); + + clutter_actor_set_name (child, "Child"); + clutter_container_add_actor (CLUTTER_CONTAINER (test), child); + + clutter_actor_destroy (test); +} diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index be3929b33..6b2ca0239 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -151,6 +151,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/binding-pool", test_binding_pool); TEST_CONFORM_SIMPLE ("/actor", test_anchors); + TEST_CONFORM_SIMPLE ("/actor", test_actor_destruction); TEST_CONFORM_SIMPLE ("/model", test_list_model_populate); TEST_CONFORM_SIMPLE ("/model", test_list_model_iterate); From c8c5e0ee39626d434c2d3e5486c6520eff17f519 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 22 Dec 2009 18:10:39 +0000 Subject: [PATCH 30/83] docs: Clean up the README file --- README | 137 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 54 deletions(-) diff --git a/README b/README index bab7a3941..7f38af7f7 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ -Clutter 1.2 README -================== +Clutter - README +=============================================================================== Clutter is an open source software library for creating fast, visually rich and animated graphical user interfaces. @@ -22,7 +22,6 @@ If you are building the Introspection data you will also need: • GObject-Introspection >= 0.6.4 - The official website is: http://www.clutter-project.org @@ -42,85 +41,115 @@ Clutter is licensed under the terms of the GNU Lesser General Public License, version 2.1 or (at your option) later. INSTALLATION -============ +=============================================================================== See the INSTALL file. Info on specific Clutter options; --enable-debug=[no/minimum/yes] - Controls Clutter debugging level (default=yes): - yes: All glib asserts, checks and runtime clutter verbose messages. - minimum: Just glib cast checks and runtime clutter verbose messagaes. - no: No glib asserts or checks and no runtime clutter verbose messages - (Only really of use in extreme performance cases) + Controls Clutter debugging level: + + yes: + All GLib asserts, checks and support for runtime Clutter + debugging notes through CLUTTER_DEBUG. This is the default + value for snapshots. + + minimum: + Just GType cast checks and support for runtime Clutter + debugging notes through CLUTTER_DEBUG. This is the default + for stable releases. + + no: + No GLib asserts or checks and no support for runtime Clutter + debugging notes. Only use in extreme performance and/or size + optimization cases. --enable-cogl-debug=[no/minimum/yes] Controls COGL debugging level (default=minimum): - yes: All runtime verbose messages and error checking for each GL - primitive - minimum: All runtime verbose messages - no: No error checking and no messages + + yes: + Support for COGL debugging notes through COGL_DEBUG and + error checking for each GL primitive. + + minimum: + Support for COGL debugging notes through COGL_DEBUG. This is + the default for snapshots. + + no: + Disable support for COGL runtime debugging notes. This is + the default for stable releases. --enable-maintainer-flags=[no/yes] - Use strict compiler flags (default=no) + Use strict compiler flags. This defaults to 'yes' for snapshots and + to 'no' for stable releases. --enable-gtk-doc use gtk-doc to build API documentation (default=no). Requires gtk-doc - present on system + present on the target system. - --enable-manual=[no/yes] - Build application developers manual. Requires jw and xmlto binaries. - Presently incomplete. + --enable-docs=[no/yes] + Build additional documentation. Requires xsltproc for DocBook + conversion, and optionally jw for PDF generation. - --with-flavour=[glx/eglx/eglnative/sdl/osx/win32/fruity] + --with-flavour=[glx/eglx/eglnative/sdl/osx/win32/fruity] Select the Clutter backend: (default=glx) - glx: Fully featured GLX backend. Using Open GL. + glx: + Fully featured GLX backend. Using Open GL. - eglx: EGL/Open GL ES backend for EGL on X windows implementations + eglx: + EGL/Open GL ES backend for EGL on X windows implementations - eglnative: + eglnative: EGL/Open GL ES backend on 'native windowing system' - i.e raw framebuffer. Expects the EGL implementation to provide a createNativeWindow() call. Also it optionally supports tslib for touchscreen events. - sdl: Basic SDL backend, using Open GL. Should provide portability + sdl: + Basic SDL backend, using Open GL. Should provide portability to Windows and possibly other OS's. (DEPRECATED) - osx: OS X backend. (EXPERIMENTAL) + osx: + OS X backend. (EXPERIMENTAL) - win32: + win32: Microsoft Windows(tm) WGL backend - fruity: + fruity: Apple iPod Touch(tm)/iPhone(tm) backend (EXPERIMENTAL) - --with-imagebackend=[gdk-pixbuf/quartz/internal] + --with-imagebackend=[gdk-pixbuf/quartz/internal] Select the image loading backend used by COGL - gdk-pixbuf: Depend on gdk-pixbuf-2.0 (default for the glx, eglx, - eglnative, sdl, win32 flavours and recommended) + gdk-pixbuf: + Depend on gdk-pixbuf-2.0 (default for the glx, eglx, + eglnative, sdl, win32 flavours and recommended) - quartz: Depend on CoreGraphics (default for the osx flavour) + quartz: + Depend on CoreGraphics (default for the osx flavour) - internal: Internal JPEG and PNG loader. Should only be used - for testing on new platforms + internal: + Internal JPEG and PNG loader. Should only be used + for testing on new platforms - --with-gles=[1.1/2.0] + --with-gles=[1.1/2.0] Select the GLES version (for EGL backends) (default=1.1) - --with-json=[internal/check/system] + --with-json=[internal/check/system] Select the JSON-GLib copy to use (default=check) - internal: Use the internal copy of JSON-GLib for ClutterScript + internal: + Use the internal copy of JSON-GLib for ClutterScript - check: Check for the existence of a system copy of JSON-GLib - and if it is available, make Clutter depend on it + check: + Check for the existence of a system copy of JSON-GLib + and if it is available, make Clutter depend on it - system: Only use the system copy of JSON-GLib + system: + Only use the system copy of JSON-GLib VERSIONING -========== +=============================================================================== Clutter uses the common "Linux kernel" versioning system, where even-numbered minor versions are stable and odd-numbered minor @@ -137,7 +166,7 @@ numbers are only used for released archives; odd micro numbers are only used on the SVN repository. HACKING -======= +=============================================================================== If you want to hack on and improve Clutter, check the contained TODO file for pending tasks, the HACKING file for general implementation guidelines, @@ -147,7 +176,7 @@ used throughout Clutter. Remember: the coding style is mandatory; patches not conforming to it will be rejected. BUGS -==== +=============================================================================== Bugs should be reported to the OpenedHand Bugzilla at: @@ -171,8 +200,8 @@ behaviour. If the bug exposes a crash, the exact text printed out and a stack trace obtained using gdb are greatly appreciated. -PATCHES -======= +CONTRIBUTING +=============================================================================== Patches should be submitted using Bugzilla. Patches fixing a bug should be attached to the bug report; patches for new features or for fixing bugs not @@ -199,13 +228,13 @@ If you do not intend to waive your copyright you should contact the Clutter development team to arrange a suitable solution. RELEASE NOTES -============= +=============================================================================== Relevant information for developers with existing Clutter applications wanting to port to newer releases (See NEWS for general new feature info). Release Notes for Clutter 1.2 -------------------------------- +------------------------------------------------------------------------------- * ClutterStageManager is now publicly available and documented API. @@ -213,7 +242,7 @@ Release Notes for Clutter 1.2 back to the internal copy only if JSON-GLib is not installed. Cogl API changes for Clutter 1.2 --------------------------------- +------------------------------------------------------------------------------- * cogl_viewport is now deprecated in favour of cogl_set_viewport which accepts a viewport offset. @@ -250,7 +279,7 @@ Cogl API changes for Clutter 1.2 Release Notes for Clutter 1.0 -------------------------------- +------------------------------------------------------------------------------- * The clutter_actor_set_shader_param() function now takes a GValue, which can be set using the clutter_value_set_shader() @@ -365,8 +394,8 @@ Release Notes for Clutter 1.0 takes the duration of the timeline in milliseconds, and thus it replaces the clutter_timeline_new_for_duration() variant. - Cogl API changes for Clutter 1.0 - -------------------------------- +Cogl API changes for Clutter 1.0 +------------------------------------------------------------------------------- * All drawing functions now use a source material to determine how geometry is filled. The source material is set via cogl_set_source. Or the convenience @@ -473,7 +502,7 @@ Release Notes for Clutter 1.0 a corresponding cogl_get_depth_test_enabled function has been added. Release Notes for Clutter 0.8 -------------------------------- +------------------------------------------------------------------------------- * The COGL GL wrapper API has been completely overhauled and now contains many new features including new greatly improved texture @@ -639,7 +668,7 @@ Release Notes for Clutter 0.8 * ClutterContainer can have per child custom properties via ClutterChildMeta. Release Notes for Clutter 0.6 -------------------------------- +------------------------------------------------------------------------------- * Now that every actor has events, the class signal handlers have been removed from ClutterStageClass and moved into ClutterActorClass. @@ -728,7 +757,7 @@ Release Notes for Clutter 0.6 respectively. Release Notes for Clutter 0.4.0 -------------------------------- +------------------------------------------------------------------------------- * clutter_actor_show_all does not recurse for groups at least (this is to match the original group_show_all behaviour). This is like 0.3 but was @@ -747,7 +776,7 @@ Release Notes for Clutter 0.4.0 overhauled. Release Notes for Clutter 0.3.1 -------------------------------- +------------------------------------------------------------------------------- * clutter_actor_apply_transform_to_point() parameters changed to use ClutterVertices. @@ -758,7 +787,7 @@ Release Notes for Clutter 0.3.1 * Exisiting X11 based egl backend public API calls now prefixed eglx. Release Notes for Clutter 0.3 ------------------------------ +------------------------------------------------------------------------------- * ClutterTexture changes: + clutter_texture_set_pixbuf() now takes a GError paremeter. From b9c1de0ec7001ce6a6b62dbd8d1fd9ee15539093 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 23 Dec 2009 08:34:08 +0000 Subject: [PATCH 31/83] box-layout: Remove the allocations hash table The BoxLayout uses a HashTable to map the latest stable allocation of each child, in order to use that as the initial value during an animation; this in spite of already having a perfectly valid per-child storage as part of the layout manager: ClutterBoxChild. The last stable allocation should be stored inside the ClutterBoxChild instead of having it in the private data for ClutterBoxLayout. The access remains O(1), since there is a 1:1 mapping between child and BoxChild instances, but we save a little bit of memory and we avoid keeping aroud allocations for old children. --- clutter/clutter-box-layout.c | 47 ++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/clutter/clutter-box-layout.c b/clutter/clutter-box-layout.c index f9a7dbb0d..90aad9837 100644 --- a/clutter/clutter-box-layout.c +++ b/clutter/clutter-box-layout.c @@ -96,7 +96,6 @@ struct _ClutterBoxLayoutPrivate gulong easing_mode; guint easing_duration; - GHashTable *allocations; guint is_vertical : 1; guint is_pack_start : 1; @@ -115,6 +114,11 @@ struct _ClutterBoxChild guint y_fill : 1; guint expand : 1; + + /* the last stable allocation before an animation; it is + * used as the initial ActorBox when interpolating + */ + ClutterActorBox *last_allocation; }; enum @@ -351,6 +355,16 @@ clutter_box_child_get_property (GObject *gobject, } } +static void +clutter_box_child_finalize (GObject *gobject) +{ + ClutterBoxChild *self = CLUTTER_BOX_CHILD (gobject); + + clutter_actor_box_free (self->last_allocation); + + G_OBJECT_CLASS (clutter_box_child_parent_class)->finalize (gobject); +} + static void clutter_box_child_class_init (ClutterBoxChildClass *klass) { @@ -359,6 +373,7 @@ clutter_box_child_class_init (ClutterBoxChildClass *klass) gobject_class->set_property = clutter_box_child_set_property; gobject_class->get_property = clutter_box_child_get_property; + gobject_class->finalize = clutter_box_child_finalize; pspec = g_param_spec_boolean ("expand", "Expand", @@ -413,6 +428,8 @@ clutter_box_child_init (ClutterBoxChild *self) self->x_fill = self->y_fill = FALSE; self->expand = FALSE; + + self->last_allocation = NULL; } static inline void @@ -765,15 +782,14 @@ allocate_box_child (ClutterBoxLayout *self, p = clutter_layout_manager_get_animation_progress (manager); - start = g_hash_table_lookup (priv->allocations, child); + start = box_child->last_allocation; if (start == NULL) { /* if there is no allocation available then the child has just * been added to the container; we put it in the final state * and store its allocation for later */ - start = clutter_actor_box_copy (&child_box); - g_hash_table_insert (priv->allocations, child, start); + box_child->last_allocation = clutter_actor_box_copy (&child_box); goto do_allocate; } @@ -797,10 +813,8 @@ allocate_box_child (ClutterBoxLayout *self, } else { - ClutterActorBox *start = clutter_actor_box_copy (&child_box); - /* store the allocation for later animations */ - g_hash_table_replace (priv->allocations, child, start); + box_child->last_allocation = clutter_actor_box_copy (&child_box); } do_allocate: @@ -975,9 +989,6 @@ clutter_box_layout_begin_animation (ClutterLayoutManager *manager, ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (manager)->priv; ClutterLayoutManagerClass *parent_class; - if (priv->is_animating) - return; - priv->is_animating = TRUE; /* we want the default implementation */ @@ -1079,16 +1090,6 @@ clutter_box_layout_get_property (GObject *gobject, } } -static void -clutter_box_layout_finalize (GObject *gobject) -{ - ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (gobject)->priv; - - g_hash_table_destroy (priv->allocations); - - G_OBJECT_CLASS (clutter_box_layout_parent_class)->finalize (gobject); -} - static void clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) { @@ -1100,7 +1101,6 @@ clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) gobject_class->set_property = clutter_box_layout_set_property; gobject_class->get_property = clutter_box_layout_get_property; - gobject_class->finalize = clutter_box_layout_finalize; layout_class->get_preferred_width = clutter_box_layout_get_preferred_width; @@ -1231,11 +1231,6 @@ clutter_box_layout_init (ClutterBoxLayout *layout) priv->use_animations = FALSE; priv->easing_mode = CLUTTER_EASE_OUT_CUBIC; priv->easing_duration = 500; - - priv->allocations = - g_hash_table_new_full (NULL, NULL, - NULL, - (GDestroyNotify) clutter_actor_box_free); } /** From 713c2952410e99e681825dac51da3e0b575ef2cf Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 23 Dec 2009 08:39:16 +0000 Subject: [PATCH 32/83] layout-manager: Rewind the timeline in begin_animation() If the default implementation begin_animation() is called twice then we should rewind the timeline, as well as updating its duration and the easing mode of the alpha. --- clutter/clutter-layout-manager.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 35b624f1d..77efdd96c 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -237,16 +237,20 @@ layout_manager_real_begin_animation (ClutterLayoutManager *manager, alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); if (alpha != NULL) { + clutter_alpha_set_mode (alpha, mode); + timeline = clutter_alpha_get_timeline (alpha); clutter_timeline_set_duration (timeline, duration); - - clutter_alpha_set_mode (alpha, mode); + clutter_timeline_rewind (timeline); return alpha; }; timeline = clutter_timeline_new (duration); + alpha = clutter_alpha_new_full (timeline, mode); + + /* let the alpha take ownership of the timeline */ g_object_unref (timeline); g_signal_connect_swapped (timeline, "completed", From 934eee17ae34098773ff5876747061b5a1d1f1a5 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 23 Dec 2009 10:35:16 +0000 Subject: [PATCH 33/83] layout-manager: Document the animation support Add a section inside the LayoutManager class API reference documenting, with examples, how to implement animation support inside a layout manager sub-class. --- clutter/clutter-layout-manager.c | 213 +++++++++++++++++++++++++++---- 1 file changed, 190 insertions(+), 23 deletions(-) diff --git a/clutter/clutter-layout-manager.c b/clutter/clutter-layout-manager.c index 77efdd96c..850c3916a 100644 --- a/clutter/clutter-layout-manager.c +++ b/clutter/clutter-layout-manager.c @@ -36,10 +36,10 @@ * a generic container using #ClutterLayoutManager called #ClutterBox. * * Clutter provides some simple #ClutterLayoutManager sub-classes, like - * #ClutterFixedLayout and #ClutterBinLayout. + * #ClutterFlowLayout and #ClutterBinLayout. * * - * Using ClutterLayoutManager inside an Actor + * Using a Layout Manager inside an Actor * In order to use a #ClutterLayoutManager inside a #ClutterActor * sub-class you should invoke clutter_layout_manager_get_preferred_width() * inside the ClutterActor::get_preferred_width() @@ -56,10 +56,10 @@ * does not need to perform specific operations whenever a layout * manager changes: * - * g_signal_connect_swapped (layout_manager, - * "layout-changed", - * G_CALLBACK (clutter_actor_queue_relayout), - * actor); + * g_signal_connect_swapped (layout_manager, + * "layout-changed", + * G_CALLBACK (clutter_actor_queue_relayout), + * actor); * * * @@ -70,18 +70,20 @@ * #ClutterActor, so you should read the relative documentation * for subclassing * ClutterActor. - * The layout manager implementation can hold a back reference - * to the #ClutterContainer by implementing the set_container() - * virtual function. The layout manager should not hold a reference - * on the container actor, to avoid reference cycles. + * The layout manager implementation can hold a back pointer + * to the #ClutterContainer by implementing the + * set_container() virtual function. The layout manager + * should not hold a real reference (i.e. call g_object_ref()) on the + * container actor, to avoid reference cycles. * If the layout manager has properties affecting the layout * policies then it should emit the #ClutterLayoutManager::layout-changed * signal on itself by using the clutter_layout_manager_layout_changed() - * function. + * function whenever one of these properties changes. * If the layout manager has layout properties, that is properties that * should exist only as the result of the presence of a specific (layout - * manager, container actor, child actor) combination, then it should - * override the ClutterLayoutManager::get_child_meta_type() + * manager, container actor, child actor) combination, and it wishes to store + * those properties inside a #ClutterLayoutMeta then it should override the + * ClutterLayoutManager::get_child_meta_type() * virtual function to return the #GType of the #ClutterLayoutMeta sub-class * used to store the layout properties; optionally, the #ClutterLayoutManager * sub-class might also override the @@ -89,17 +91,17 @@ * function to control how the #ClutterLayoutMeta instance is created, * otherwise the default implementation will be equivalent to: * - * ClutterLayoutManagerClass *klass; - * GType meta_type; + * ClutterLayoutManagerClass *klass; + * GType meta_type; * - * klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); - * meta_type = klass->get_child_meta_type (manager); + * klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); + * meta_type = klass->get_child_meta_type (manager); * - * return g_object_new (meta_type, - * "manager", manager, - * "container", container, - * "actor", actor, - * NULL); + * return g_object_new (meta_type, + * "manager", manager, + * "container", container, + * "actor", actor, + * NULL); * * Where manager is the #ClutterLayoutManager, * container is the #ClutterContainer using the @@ -109,7 +111,172 @@ * * * Animating a ClutterLayoutManager - * ... + * A layout manager is used to let a #ClutterContainer take complete + * ownership over the layout (that is: the position and sizing) of its + * children; this means that using the Clutter animation API, like + * clutter_actor_animate(), to animate the position and sizing of a child of + * a layout manager it is not going to work properly, as the animation will + * automatically override any setting done by the layout manager + * itself. + * It is possible for a #ClutterLayoutManager sub-class to animate its + * children layout by using the base class animation support. The + * #ClutterLayoutManager animation support consists of three virtual + * functions: begin_animation(), + * get_animation_progress() and + * end_animation(). + * + * + * begin_animation (duration, easing) + * This virtual function is invoked when the layout + * manager should begin an animation. The implementation should set up + * the state for the animation and create the ancillary objects for + * animating the layout. The default implementation creates a + * #ClutterTimeline for the given duration and a #ClutterAlpha binding + * the timeline to the given easing mode. This function returns a + * #ClutterAlpha which should be used to control the animation from + * the caller perspective. + * + * + * get_animation_progress() + * This virtual function should be invoked when animating + * a layout manager. It returns the progress of the animation, using the + * same semantics as the #ClutterAlpha:alpha value. + * + * + * end_animation() + * This virtual function is invoked when the animation of + * a layout manager ends, and it is meant to be used for bookkeeping the + * objects created in the begin_animation() + * function. The default implementation will call it implicitly when the + * timeline is complete. + * + * + * The simplest way to animate a layout is to create a #ClutterTimeline + * inside the begin_animation() virtual function, along + * with a #ClutterAlpha, and for each #ClutterTimeline::new-frame signal + * emission call clutter_layout_manager_layout_changed(), which will cause a + * relayout. The #ClutterTimeline::completed signal emission should cause + * clutter_layout_manager_end_animation() to be called. The default + * implementation provided internally by #ClutterLayoutManager does exactly + * this, so most sub-classes should either not override any animation-related + * virtual function or simply override begin_animation() + * and end_animation() to set up ad hoc state, and then + * chain up to the parent's implementation. + * + * Animation of a Layout Manager + * The code below shows how a #ClutterLayoutManager sub-class should + * provide animating the allocation of its children from within the + * allocate() virtual function implementation. The + * animation is computed between the last stable allocation performed + * before the animation started and the desired final allocation. + * The is_animating variable is stored inside the + * #ClutterLayoutManager sub-class and it is updated by overriding the + * begin_animation() and + * end_animation() virtual functions and chaining up + * to the base class implementation. + * The last stable allocation is stored within a #ClutterLayoutMeta + * sub-class used by the implementation. + * + * static void + * my_layout_manager_allocate (ClutterLayoutManager *manager, + * ClutterContainer *container, + * const ClutterActorBox *allocation, + * ClutterAllocationFlags flags) + * { + * MyLayoutManager *self = MY_LAYOUT_MANAGER (manager); + * GList *children, *l; + * + * children = clutter_container_get_children (container); + * + * for (l = children; l != NULL; l = l->next) + * { + * ClutterActor *child = l->data; + * ClutterLayoutMeta *meta; + * MyLayoutMeta *my_meta; + * + * /* retrieve the layout meta-object */ + * meta = clutter_layout_manager_get_child_meta (manager, + * container, + * child); + * my_meta = MY_LAYOUT_META (meta); + * + * /* compute the desired allocation for the child */ + * compute_allocation (self, my_meta, child, + * allocation, flags, + * &child_box); + * + * /* this is the additional code that deals with the animation + * * of the layout manager + * */ + * if (!self->is_animating) + * { + * /* store the last stable allocation for later use */ + * my_meta->last_alloc = clutter_actor_box_copy (&child_box); + * } + * else + * { + * ClutterActorBox end = { 0, }; + * gdouble p; + * + * /* get the progress of the animation */ + * p = clutter_layout_manager_get_animation_progress (manager); + * + * if (my_meta->last_alloc != NULL) + * { + * /* copy the desired allocation as the final state */ + * end = child_box; + * + * /* then interpolate the initial and final state + * * depending on the progress of the animation, + * * and put the result inside the box we will use + * * to allocate the child + * */ + * clutter_actor_box_interpolate (my_meta->last_alloc, + * &end, + * p, + * &child_box); + * } + * else + * { + * /* if there is no stable allocation then the child was + * * added while animating; one possible course of action + * * is to just bail out and fall through to the allocation + * * to position the child directly at its final state + * */ + * my_meta->last_alloc = + * clutter_actor_box_copy (&child_box); + * } + * } + * + * /* allocate the child */ + * clutter_actor_allocate (child, &child_box, flags); + * } + * + * g_list_free (children); + * } + * + * + * Sub-classes of #ClutterLayoutManager that support animations of the + * layout changes should call clutter_layout_manager_begin_animation() + * whenever a layout property changes value, e.g.: + * + * + * if (self->orientation != new_orientation) + * { + * ClutterLayoutManager *manager; + * + * self->orientation = new_orientation; + * + * manager = CLUTTER_LAYOUT_MANAGER (self); + * clutter_layout_manager_layout_changed (manager); + * clutter_layout_manager_begin_animation (manager, 500, CLUTTER_LINEAR); + * + * g_object_notify (G_OBJECT (self), "orientation"); + * } + * + * + * The code above will animate a change in the + * orientation layout property of a layout manager. * * * #ClutterLayoutManager is available since Clutter 1.2 From 8d40cb947031f90a7b8cf459720278c6a6fe295d Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sun, 3 Jan 2010 20:12:49 +0000 Subject: [PATCH 34/83] Disable g_set_prgname() Apparently, calling g_set_prgname() multiple times is not allowed anymore, and hence clutter_init_* calls should not do that. Though this is really GLib's fault - and a massive nuisance for us - we should prolly comply to avoid the test suite dying on us. --- clutter/clutter-main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 4b749f27e..84f5ed696 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -1725,8 +1725,10 @@ clutter_init_with_args (int *argc, if (!ctx->defer_display_setup) { +#if 0 if (argc && *argc > 0 && *argv) g_set_prgname ((*argv)[0]); +#endif context = g_option_context_new (parameter_string); @@ -1831,8 +1833,10 @@ clutter_init (int *argc, if (!ctx->defer_display_setup) { +#if 0 if (argc && *argc > 0 && *argv) g_set_prgname ((*argv)[0]); +#endif /* parse_args will trigger backend creation and things like * DISPLAY connection etc. From 3355c2ab2518711d7ab79f3ffe1f4a8ce105af84 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sun, 3 Jan 2010 20:24:39 +0000 Subject: [PATCH 35/83] build: Remove dist rule for non-existing image files --- doc/cookbook/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cookbook/Makefile.am b/doc/cookbook/Makefile.am index 4a1c1356a..7759c797b 100644 --- a/doc/cookbook/Makefile.am +++ b/doc/cookbook/Makefile.am @@ -4,7 +4,7 @@ TARGET_DIR = $(HTML_DIR)/clutter-cookbook HTML_FILES = $(srcdir)/html/*.html IMAGE_FILES = $(srcdir)/images/*.png -EXTRA_DIST = $(IMAGE_FILES) clutter-cookbook.xml.in +EXTRA_DIST = clutter-cookbook.xml.in CLEANFILES = \ pdf-build.stamp \ From 6d7e1e5434d3da27cf23a9c16f9990523eedba4e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sun, 3 Jan 2010 20:11:43 +0000 Subject: [PATCH 36/83] Release Clutter 1.1.4 --- NEWS | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 4 ++-- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index d3bba1068..5b2961e40 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,59 @@ +Clutter 1.1.4 (03/01/2010) +=============================================================================== + + * List of changes since Clutter 1.1.2 + + o Update the ClutterScript parser to be more resilient, and support + constructor and ChildMeta properties. The parser also respects the + order of the properties in the UI definitions when applying them, + and will apply the properties of an Actor after building the scene + graph. + + o Simplified the implementation of LayoutManager sub-classes, and added + support for animating a layout manager. + + o Allow short-circuiting some layout operations by setting a specific + flag on ClutterActor. + + o Improve caching of the preferred size of a ClutterActor. + + o Allow declaring "internal children" for a ClutterContainer + implementation: the memory management of these actors will be deferred + entirely to the Container. + + o Let the ClutterStage honour the :opacity property and the alpha component + of the stage color. This requires support in the Clutter backend used. + + o Improve Windows and OSX backends. + + o Simplify the feature detection code for OpenGL; this also reduced the + required OpenGL version to 1.2. + + o Improve the matrix stack handling code. The matrices can be debugged by + using the COGL_DEBUG environment variable, assuming that Clutter was + compiled with the right configure-time switch. + + o Improve COGL API for draw buffers, and for offscreen buffer support. + + o Add support for text direction to ClutterActor. + + o Documentation, introspection and build fixes. + +Many thanks to: + + Robert Bragg + Neil Roberts + Damien Lespiau + Joshua Lock + Bastian Winkler + Rob Bradford + Samuel Degrande + Christian Persch + Colin Walters + Johan Bilien + Raymond Liu + Tim Horton + Clutter 1.1.2 (23/10/2009) =============================================================================== diff --git a/configure.ac b/configure.ac index cf985f087..2af8eb167 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ # - increase clutter_interface_version to the next odd number m4_define([clutter_major_version], [1]) m4_define([clutter_minor_version], [1]) -m4_define([clutter_micro_version], [3]) +m4_define([clutter_micro_version], [4]) m4_define([clutter_release_status], [m4_if(m4_eval(clutter_micro_version % 2), [1], [git], @@ -42,7 +42,7 @@ m4_define([clutter_api_version], [1.0]) # clutter 1.2.10 -> 100 * 2 + 10 = 210, interface age = 4 -> 206 # ... # -m4_define([clutter_interface_age], [1]) +m4_define([clutter_interface_age], [0]) m4_define([clutter_binary_age], [m4_eval(100 * clutter_minor_version + clutter_micro_version)]) AC_PREREQ([2.59]) From 5ae88f5777c5ed0663937f990350f145b254692c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sun, 3 Jan 2010 20:41:17 +0000 Subject: [PATCH 37/83] Post-release version bump to 1.1.5 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 2af8eb167..2712c1b5b 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ # - increase clutter_interface_version to the next odd number m4_define([clutter_major_version], [1]) m4_define([clutter_minor_version], [1]) -m4_define([clutter_micro_version], [4]) +m4_define([clutter_micro_version], [5]) m4_define([clutter_release_status], [m4_if(m4_eval(clutter_micro_version % 2), [1], [git], From 1c69c61745ed510f0b6ab16cb963ca01994cb9fc Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 11 Nov 2009 11:00:29 +0000 Subject: [PATCH 38/83] master-clock: Take a reference before advancing timelines A timeline advancement might cause another timeline to be destroyed, which will likely lead to a segmentation fault. Before advancing the timelines we should take a reference on them - just like we do for the stages before doing event processing. This will prevent dispose() from running until the end of the advancement. http://bugzilla.openedhand.com/show_bug.cgi?id=1854 --- clutter/clutter-master-clock.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/clutter/clutter-master-clock.c b/clutter/clutter-master-clock.c index 8aa8e5ed0..f278c13db 100644 --- a/clutter/clutter-master-clock.c +++ b/clutter/clutter-master-clock.c @@ -429,12 +429,19 @@ _clutter_master_clock_advance (ClutterMasterClock *master_clock) g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock)); + /* we protect ourselves from timelines being removed during + * the advancement by other timelines + */ + g_slist_foreach (master_clock->timelines, (GFunc) g_object_ref, NULL); + for (l = master_clock->timelines; l != NULL; l = next) { next = l->next; clutter_timeline_do_tick (l->data, &master_clock->cur_tick); } + + g_slist_foreach (master_clock->timelines, (GFunc) g_object_unref, NULL); } /** From 0fb51658fdba886fd5d9cb53cfca6df13aa1f7a0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 4 Jan 2010 11:43:00 +0000 Subject: [PATCH 39/83] cogl: Const-ify vertices in cogl_polygon() The CoglTextureVertex array passed to cogl_polygon() is a pure in-argument and should be const-ified. http://bugzilla.openedhand.com/show_bug.cgi?id=1917 --- clutter/cogl/cogl/cogl-primitives.c | 27 ++++++++++++++------------- clutter/cogl/cogl/cogl-texture.h | 6 +++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/clutter/cogl/cogl/cogl-primitives.c b/clutter/cogl/cogl/cogl-primitives.c index 63bd382c6..e4fdf6a99 100644 --- a/clutter/cogl/cogl/cogl-primitives.c +++ b/clutter/cogl/cogl/cogl-primitives.c @@ -61,7 +61,7 @@ typedef struct _TextureSlicedQuadState typedef struct _TextureSlicedPolygonState { - CoglTextureVertex *vertices; + const CoglTextureVertex *vertices; int n_vertices; int stride; } TextureSlicedPolygonState; @@ -709,7 +709,7 @@ draw_polygon_sub_texture_cb (CoglHandle tex_handle, /* handles 2d-sliced textures with > 1 slice */ static void -_cogl_texture_polygon_multiple_primitives (CoglTextureVertex *vertices, +_cogl_texture_polygon_multiple_primitives (const CoglTextureVertex *vertices, unsigned int n_vertices, unsigned int stride, gboolean use_color) @@ -762,7 +762,7 @@ _cogl_texture_polygon_multiple_primitives (CoglTextureVertex *vertices, } static void -_cogl_multitexture_polygon_single_primitive (CoglTextureVertex *vertices, +_cogl_multitexture_polygon_single_primitive (const CoglTextureVertex *vertices, guint n_vertices, guint n_layers, guint stride, @@ -841,14 +841,13 @@ _cogl_multitexture_polygon_single_primitive (CoglTextureVertex *vertices, } void -cogl_polygon (CoglTextureVertex *vertices, - guint n_vertices, - gboolean use_color) +cogl_polygon (const CoglTextureVertex *vertices, + guint n_vertices, + gboolean use_color) { CoglHandle material; - const GList *layers; + const GList *layers, *tmp; int n_layers; - GList *tmp; gboolean use_sliced_polygon_fallback = FALSE; guint32 fallback_layers = 0; int i; @@ -871,10 +870,10 @@ cogl_polygon (CoglTextureVertex *vertices, layers = cogl_material_get_layers (ctx->source_material); n_layers = g_list_length ((GList *)layers); - for (tmp = (GList *)layers, i = 0; tmp != NULL; tmp = tmp->next, i++) + for (tmp = layers, i = 0; tmp != NULL; tmp = tmp->next, i++) { - CoglHandle layer = (CoglHandle)tmp->data; - CoglHandle tex_handle = cogl_material_layer_get_texture (layer); + CoglHandle layer = tmp->data; + CoglHandle tex_handle = cogl_material_layer_get_texture (layer); /* COGL_INVALID_HANDLE textures will be handled in * _cogl_material_flush_layers_gl_state */ @@ -904,6 +903,7 @@ cogl_polygon (CoglTextureVertex *vertices, warning_seen = TRUE; } } + use_sliced_polygon_fallback = TRUE; n_layers = 1; @@ -990,9 +990,10 @@ cogl_polygon (CoglTextureVertex *vertices, /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ v + 3 + 2 * i)); } - prev_n_texcoord_arrays_enabled = - ctx->n_texcoord_arrays_enabled; + + prev_n_texcoord_arrays_enabled = ctx->n_texcoord_arrays_enabled; ctx->n_texcoord_arrays_enabled = n_layers; + for (; i < prev_n_texcoord_arrays_enabled; i++) { GE (glClientActiveTexture (GL_TEXTURE0 + i)); diff --git a/clutter/cogl/cogl/cogl-texture.h b/clutter/cogl/cogl/cogl-texture.h index b831d1ae1..7859abd19 100644 --- a/clutter/cogl/cogl/cogl-texture.h +++ b/clutter/cogl/cogl/cogl-texture.h @@ -466,9 +466,9 @@ void cogl_rectangles (const float *verts, * * Since: 1.0 */ -void cogl_polygon (CoglTextureVertex *vertices, - guint n_vertices, - gboolean use_color); +void cogl_polygon (const CoglTextureVertex *vertices, + guint n_vertices, + gboolean use_color); G_END_DECLS From b98d486fc99fd46626877be728961190dbf831dc Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 4 Jan 2010 11:49:13 +0000 Subject: [PATCH 40/83] build: Maintainer cflags go in the _CFLAGS target The maintainer compiler flags are not pre-processor flags. --- clutter/cogl/cogl/Makefile.am | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clutter/cogl/cogl/Makefile.am b/clutter/cogl/cogl/Makefile.am index c3b19c424..5ef3c3b9c 100644 --- a/clutter/cogl/cogl/Makefile.am +++ b/clutter/cogl/cogl/Makefile.am @@ -78,12 +78,11 @@ noinst_LTLIBRARIES = libclutter-cogl.la libclutter_cogl_la_CPPFLAGS = \ $(COGL_DEBUG_CFLAGS) \ $(CLUTTER_DEBUG_CFLAGS) \ - $(MAINTAINER_CFLAGS) \ -DG_DISABLE_SINGLE_INCLUDES \ -DG_LOG_DOMAIN=\"Cogl-$(COGL_WINSYS)\" \ -DCLUTTER_COMPILATION -libclutter_cogl_la_CFLAGS = $(CLUTTER_CFLAGS) +libclutter_cogl_la_CFLAGS = $(CLUTTER_CFLAGS) $(MAINTAINER_CFLAGS) libclutter_cogl_la_LIBADD = -lm $(CLUTTER_LIBS) \ $(top_builddir)/clutter/cogl/cogl/driver/$(COGL_DRIVER)/libclutter-cogl-driver.la From eb9029504124daf4c032f027c2f068ec25548f3f Mon Sep 17 00:00:00 2001 From: Halton Huo Date: Mon, 4 Jan 2010 11:49:50 +0000 Subject: [PATCH 41/83] cogl-texture: Remove return in void functions http://bugzilla.o-hand.com/show_bug.cgi?id=1929 Signed-off-by: Emmanuele Bassi --- clutter/cogl/cogl/cogl-texture.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/clutter/cogl/cogl/cogl-texture.c b/clutter/cogl/cogl/cogl-texture.c index 5b3950bc3..085a23f5b 100644 --- a/clutter/cogl/cogl/cogl-texture.c +++ b/clutter/cogl/cogl/cogl-texture.c @@ -334,8 +334,6 @@ cogl_texture_get_max_waste (CoglHandle handle) tex = COGL_TEXTURE (handle); return tex->vtable->get_max_waste (tex); - - g_return_val_if_reached (0); } gboolean @@ -408,7 +406,7 @@ _cogl_texture_transform_coords_to_gl (CoglHandle handle, { CoglTexture *tex = COGL_TEXTURE (handle); - return tex->vtable->transform_coords_to_gl (tex, s, t); + tex->vtable->transform_coords_to_gl (tex, s, t); } GLenum @@ -446,7 +444,7 @@ _cogl_texture_set_filters (CoglHandle handle, tex = COGL_TEXTURE (handle); - return tex->vtable->set_filters (tex, min_filter, mag_filter); + tex->vtable->set_filters (tex, min_filter, mag_filter); } void @@ -459,7 +457,7 @@ _cogl_texture_ensure_mipmaps (CoglHandle handle) tex = COGL_TEXTURE (handle); - return tex->vtable->ensure_mipmaps (tex); + tex->vtable->ensure_mipmaps (tex); } gboolean From 4adc2c2d839c42eccc7187735ddcce061d25f14b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 4 Jan 2010 11:58:32 +0000 Subject: [PATCH 42/83] build: Move CoglMatrixMode to cogl-matrix-stack.h This avoids a redeclaration of _cogl_matrix_stack_flush_to_gl() from using GLenum to CoglMatrixMode. http://bugzilla.openedhand.com/show_bug.cgi?id=1928 --- clutter/cogl/cogl/cogl-internal.h | 7 ------- clutter/cogl/cogl/cogl-matrix-stack.h | 9 +++++++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/clutter/cogl/cogl/cogl-internal.h b/clutter/cogl/cogl/cogl-internal.h index 714b75d19..585220a1d 100644 --- a/clutter/cogl/cogl/cogl-internal.h +++ b/clutter/cogl/cogl/cogl-internal.h @@ -27,13 +27,6 @@ #include "cogl.h" #include "cogl-matrix-stack.h" -typedef enum -{ - COGL_MATRIX_MODELVIEW, - COGL_MATRIX_PROJECTION, - COGL_MATRIX_TEXTURE -} CoglMatrixMode; - typedef enum { COGL_FRONT_WINDING_CLOCKWISE, diff --git a/clutter/cogl/cogl/cogl-matrix-stack.h b/clutter/cogl/cogl/cogl-matrix-stack.h index 648eace84..892e98cb0 100644 --- a/clutter/cogl/cogl/cogl-matrix-stack.h +++ b/clutter/cogl/cogl/cogl-matrix-stack.h @@ -28,10 +28,15 @@ #define __COGL_MATRIX_STACK_H #include "cogl-matrix.h" -#include "cogl.h" /* needed for GLenum */ typedef struct _CoglMatrixStack CoglMatrixStack; +typedef enum { + COGL_MATRIX_MODELVIEW, + COGL_MATRIX_PROJECTION, + COGL_MATRIX_TEXTURE +} CoglMatrixMode; + CoglMatrixStack* _cogl_matrix_stack_new (void); void _cogl_matrix_stack_destroy (CoglMatrixStack *stack); void _cogl_matrix_stack_push (CoglMatrixStack *stack); @@ -79,7 +84,7 @@ void _cogl_matrix_stack_get (CoglMatrixStack *stack, void _cogl_matrix_stack_set (CoglMatrixStack *stack, const CoglMatrix *matrix); void _cogl_matrix_stack_flush_to_gl (CoglMatrixStack *stack, - GLenum gl_mode); + CoglMatrixMode mode); void _cogl_matrix_stack_dirty (CoglMatrixStack *stack); #endif /* __COGL_MATRIX_STACK_H */ From bf43cb6cdc84e646add7adc592487274331d4930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Pi=C3=B1eiro?= Date: Fri, 20 Nov 2009 15:17:41 +0100 Subject: [PATCH 43/83] Added ClutterText::insert-text and ClutterText::delete-text signals * clutter/clutter-marshal.list: added new marshaller * clutter/clutter-text.c (clutter_text_class_init): added insert-text and delete-text signals (clutter_text_insert_unichar): emits insert-text signal (clutter_text_insert_text): emits insert-text signal (clutter_text_delete_text): emits delete-text signal (clutter_text_delete_chars): emits delete-text signal (clutter_text_set_markup_internal): emits delete-text and insert-text signals (clutter_text_set_text): emits delete-text and insert-text signals http://bugzilla.openedhand.com/show_bug.cgi?id=1894 --- clutter/clutter-marshal.list | 1 + clutter/clutter-text.c | 87 ++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-marshal.list b/clutter/clutter-marshal.list index 069d73774..7f354b11a 100644 --- a/clutter/clutter-marshal.list +++ b/clutter/clutter-marshal.list @@ -16,3 +16,4 @@ VOID:STRING,BOOLEAN,BOOLEAN VOID:STRING,INT VOID:UINT VOID:VOID +VOID:STRING,INT,POINTER diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 0a9d426ba..37636e717 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -55,6 +55,7 @@ #include "clutter-main.h" #include "clutter-private.h" /* includes pango/cogl-pango.h */ #include "clutter-units.h" +#include "clutter-marshal.h" /* cursor width in pixels */ #define DEFAULT_CURSOR_SIZE 2 @@ -211,6 +212,8 @@ enum TEXT_CHANGED, CURSOR_EVENT, ACTIVATE, + INSERT_TEXT, + DELETE_TEXT, LAST_SIGNAL }; @@ -872,6 +875,12 @@ clutter_text_set_markup_internal (ClutterText *self, if (text) { + gint tmp_pos = 0; + + g_signal_emit (self, text_signals[DELETE_TEXT], 0, 0, -1); + g_signal_emit (self, text_signals[INSERT_TEXT], 0, text, + strlen (text), &tmp_pos); + clutter_text_set_text_internal (self, text); g_free (text); } @@ -2566,6 +2575,56 @@ clutter_text_class_init (ClutterTextClass *klass) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * ClutterText::insert-text: + * @self: the #ClutterText that emitted the signal + * @new_text: the new text to insert + * @new_text_length: the length of the new text, in bytes, or -1 if + * new_text is nul-terminated + * @position: the position, in characters, at which to insert the + * new text. this is an in-out parameter. After the signal + * emission is finished, it should point after the newly + * inserted text. + * + * This signal is emitted when text is inserted into the actor by + * the user. It is emitted before @self text changes. + * + * Since: 1.2 + */ + text_signals[INSERT_TEXT] = + g_signal_new ("insert-text", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + clutter_marshal_VOID__STRING_INT_POINTER, + G_TYPE_NONE, 3, + G_TYPE_STRING, + G_TYPE_INT, + G_TYPE_POINTER); + + /** + * ClutterText::delete-text: + * @self: the #ClutterText that emitted the signal + * @start_pos: the starting position + * @end_pos: the end position + * + * This signal is emitted when text is deleted from the actor by + * the user. It is emitted before @self text changes. + * + * Since: 1.2 + */ + text_signals[DELETE_TEXT] = + g_signal_new ("delete-text", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + clutter_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_INT); + /** * ClutterText::cursor-event: * @self: the #ClutterText that emitted the signal @@ -3480,6 +3539,14 @@ clutter_text_set_text (ClutterText *self, { g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_signal_emit (self, text_signals[DELETE_TEXT], 0, 0, -1); + if (text) + { + gint tmp_pos = 0; + g_signal_emit (self, text_signals[INSERT_TEXT], 0, text, + strlen (text), &tmp_pos); + } + clutter_text_set_use_markup_internal (self, FALSE); clutter_text_set_text_internal (self, text ? text : ""); } @@ -4238,7 +4305,9 @@ clutter_text_insert_unichar (ClutterText *self, new = g_string_insert_unichar (new, pos, wc); - clutter_text_set_text (self, new->str); + g_signal_emit (self, text_signals[INSERT_TEXT], 0, &wc, 1, &pos); + + clutter_text_set_text_internal (self, new->str); if (priv->position >= 0) { @@ -4283,7 +4352,10 @@ clutter_text_insert_text (ClutterText *self, new = g_string_new (priv->text); new = g_string_insert (new, pos_bytes, text); - clutter_text_set_text (self, new->str); + g_signal_emit (self, text_signals[INSERT_TEXT], 0, + text, g_utf8_strlen (text, -1), &position); + + clutter_text_set_text_internal (self, new->str); if (position >= 0 && priv->position >= position) { @@ -4338,7 +4410,9 @@ clutter_text_delete_text (ClutterText *self, new = g_string_new (priv->text); new = g_string_erase (new, start_bytes, end_bytes - start_bytes); - clutter_text_set_text (self, new->str); + g_signal_emit (self, text_signals[DELETE_TEXT], 0, start_pos, end_pos); + + clutter_text_set_text_internal (self, new->str); g_string_free (new, TRUE); } @@ -4362,6 +4436,7 @@ clutter_text_delete_chars (ClutterText *self, gint len; gint pos; gint num_pos; + gint start_pos; g_return_if_fail (CLUTTER_IS_TEXT (self)); @@ -4385,7 +4460,11 @@ clutter_text_delete_chars (ClutterText *self, new = g_string_erase (new, pos, num_pos - pos); } - clutter_text_set_text (self, new->str); + start_pos = clutter_text_get_cursor_position (self); + g_signal_emit (self, text_signals[DELETE_TEXT], 0, + start_pos, start_pos + n_chars); + + clutter_text_set_text_internal (self, new->str); if (priv->position > 0) clutter_text_set_cursor_position (self, priv->position - n_chars); From 5d5c7142f7fa2447e13bcb80a272b0d9dfc28f06 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 4 Jan 2010 12:03:47 +0000 Subject: [PATCH 44/83] text: Use G_SIGNAL_ACTION for ::insert-text and ::delete-text Both the ::insert-text and ::delete-text are "action" signals, that is signals that are safe to (and should) be emitted using g_signal_emit() directly. --- clutter/clutter-text.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 37636e717..f77405531 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2594,7 +2594,7 @@ clutter_text_class_init (ClutterTextClass *klass) text_signals[INSERT_TEXT] = g_signal_new ("insert-text", G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 0, NULL, NULL, clutter_marshal_VOID__STRING_INT_POINTER, @@ -2617,7 +2617,7 @@ clutter_text_class_init (ClutterTextClass *klass) text_signals[DELETE_TEXT] = g_signal_new ("delete-text", G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 0, NULL, NULL, clutter_marshal_VOID__INT_INT, From 10963d1ae03920e202ccbd0107854f489117f89b Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 28 Dec 2009 17:52:43 +0100 Subject: [PATCH 45/83] [media] Add an API to play a subtitle file along with the media Allow the user of the ClutterMedia interface to specify an external (as in not multiplexed with the audio/video streams) location of a subtitle stream. --- clutter/clutter-media.c | 55 ++++++++++++++++++++++ clutter/clutter-media.h | 3 ++ doc/reference/clutter/clutter-sections.txt | 2 + 3 files changed, 60 insertions(+) diff --git a/clutter/clutter-media.c b/clutter/clutter-media.c index 716b459bc..30e51bec6 100644 --- a/clutter/clutter-media.c +++ b/clutter/clutter-media.c @@ -109,6 +109,20 @@ clutter_media_base_init (gpointer g_iface) CLUTTER_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); + /** + * ClutterMedia:subtitle-uri: + * + * The location of a subtitle file, expressed as a valid URI. + * + * Since: 1.2 + */ + pspec = g_param_spec_string ("subtitle-uri", + "Subtitle URI", + "URI of a subtitle file", + NULL, + CLUTTER_PARAM_READWRITE); + g_object_interface_install_property (g_iface, pspec); + /** * ClutterMedia:audio-volume: * @@ -348,6 +362,47 @@ clutter_media_get_progress (ClutterMedia *media) return retval; } +/** + * clutter_media_set_subtitle_uri: + * @media: a #ClutterMedia + * @uri: the URI of a subtitle file + * + * Sets the location of a subtitle file to display while playing @media. + * + * Since: 1.2 + */ +void +clutter_media_set_subtitle_uri (ClutterMedia *media, + const char *uri) +{ + g_return_if_fail (CLUTTER_IS_MEDIA (media)); + + g_object_set (G_OBJECT (media), "subtitle-uri", uri, NULL); +} + +/** + * clutter_media_get_subtitle_uri: + * @media: a #ClutterMedia + * + * Retrieves the URI of the subtitle file in use. + * + * Return value: the URI of the subtitle file. Use g_free() + * to free the returned string + * + * Since: 1.2 + */ +gchar * +clutter_media_get_subtitle_uri (ClutterMedia *media) +{ + gchar *retval = NULL; + + g_return_val_if_fail (CLUTTER_IS_MEDIA(media), NULL); + + g_object_get (G_OBJECT (media), "subtitle-uri", &retval, NULL); + + return retval; +} + /** * clutter_media_set_audio_volume: * @media: a #ClutterMedia diff --git a/clutter/clutter-media.h b/clutter/clutter-media.h index 2ad164441..c776c647c 100644 --- a/clutter/clutter-media.h +++ b/clutter/clutter-media.h @@ -76,6 +76,9 @@ gboolean clutter_media_get_playing (ClutterMedia *media); void clutter_media_set_progress (ClutterMedia *media, gdouble progress); gdouble clutter_media_get_progress (ClutterMedia *media); +void clutter_media_set_subtitle_uri (ClutterMedia *media, + const gchar *uri); +gchar * clutter_media_get_subtitle_uri (ClutterMedia *media); void clutter_media_set_audio_volume (ClutterMedia *media, gdouble volume); gdouble clutter_media_get_audio_volume (ClutterMedia *media); diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index ac7be1d4f..3c9939377 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -9,6 +9,8 @@ clutter_media_set_playing clutter_media_get_playing clutter_media_set_progress clutter_media_get_progress +clutter_media_set_subtitles_uri +clutter_media_get_subtitles_uri clutter_media_set_audio_volume clutter_media_get_audio_volume clutter_media_get_can_seek From 2aa9d7bdc78703fc9a6354d2e54f880fde96db39 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 5 Jan 2010 10:55:28 +0000 Subject: [PATCH 46/83] script: Copy the JSON node in PropertyInfo PropertyInfo should store a copy of the JsonNodes it references, so that property_info_free() can safely dispose them, and we can reference values across different UI definition data. The implicit timeline parsing code is not copying the JsonNode; this leads to a double free in some cases, which is masked by the GSlice allocator and produces a heap corruption later on. --- clutter/clutter-script-parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/clutter-script-parser.c b/clutter/clutter-script-parser.c index 980d45412..a33702c7e 100644 --- a/clutter/clutter-script-parser.c +++ b/clutter/clutter-script-parser.c @@ -707,7 +707,7 @@ construct_timeline (ClutterScript *script, PropertyInfo *pinfo = g_slice_new0 (PropertyInfo); pinfo->name = g_strdelimit (g_strdup (name), G_STR_DELIMITERS, '-'); - pinfo->node = node; + pinfo->node = json_node_copy (node); oinfo->properties = g_list_prepend (oinfo->properties, pinfo); } From 4207524e950efd3f1fa5334f6d0b9b374a0b3f93 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 5 Jan 2010 11:02:39 +0000 Subject: [PATCH 47/83] Remove trailing comma from test UI definition The animation definition test has a trailing comma at the end of an array item. --- tests/data/test-script-animation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/test-script-animation.json b/tests/data/test-script-animation.json index dfa5255bd..d6ef90343 100644 --- a/tests/data/test-script-animation.json +++ b/tests/data/test-script-animation.json @@ -10,5 +10,5 @@ "width" : 100, "height" : 16, "color" : "white" - }, + } } From 64c1294cc26e034fbf8a9a05b4413f63b9cd882d Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 5 Jan 2010 14:10:19 +0000 Subject: [PATCH 48/83] x11 tfp: Plug a leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Destroy the dummy XImage we create even on success. http://bugzilla.openedhand.com/show_bug.cgi?id=1918 Based on a patch by: Carlos Martín Nieto Signed-off-by: Emmanuele Bassi --- clutter/x11/clutter-x11-texture-pixmap.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/clutter/x11/clutter-x11-texture-pixmap.c b/clutter/x11/clutter-x11-texture-pixmap.c index 3336d6594..53ed25383 100644 --- a/clutter/x11/clutter-x11-texture-pixmap.c +++ b/clutter/x11/clutter-x11-texture-pixmap.c @@ -213,37 +213,41 @@ try_alloc_shm (ClutterX11TexturePixmap *texture) priv->shminfo.shmid = shmget (IPC_PRIVATE, dummy_image->bytes_per_line * dummy_image->height, - IPC_CREAT|0777); + IPC_CREAT | 0777); if (priv->shminfo.shmid == -1) goto failed_shmget; - priv->shminfo.shmaddr = - shmat (priv->shminfo.shmid, 0, 0); - if (priv->shminfo.shmaddr == (void *)-1) + priv->shminfo.shmaddr = shmat (priv->shminfo.shmid, 0, 0); + if (priv->shminfo.shmaddr == (void *) -1) goto failed_shmat; priv->shminfo.readOnly = False; - if (XShmAttach(dpy, &priv->shminfo) == 0) + if (XShmAttach (dpy, &priv->shminfo) == 0) goto failed_xshmattach; if (clutter_x11_untrap_x_errors ()) g_warning ("X Error: Failed to setup XShm"); + XDestroyImage (dummy_image); + priv->have_shm = TRUE; + return TRUE; failed_xshmattach: g_warning ("XShmAttach failed"); - shmdt(priv->shminfo.shmaddr); + shmdt (priv->shminfo.shmaddr); + failed_shmat: g_warning ("shmat failed"); - shmctl(priv->shminfo.shmid, IPC_RMID, 0); + shmctl (priv->shminfo.shmid, IPC_RMID, 0); + failed_shmget: g_warning ("shmget failed"); - XDestroyImage(dummy_image); -failed_image_create: + XDestroyImage (dummy_image); +failed_image_create: if (clutter_x11_untrap_x_errors ()) g_warning ("X Error: Failed to setup XShm"); From 32b456fc8c4e8743c1b2c32164f7e9f67188a60e Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 5 Jan 2010 11:44:17 +0000 Subject: [PATCH 49/83] text: Free the Pango attribute list resources The ClutterText owns three PangoAttrList resources which were not being unref'd. This adds the unref calls to the finalize method. --- clutter/clutter-text.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index f77405531..9925d903d 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1144,6 +1144,13 @@ clutter_text_finalize (GObject *gobject) if (priv->font_desc) pango_font_description_free (priv->font_desc); + if (priv->attrs) + pango_attr_list_unref (priv->attrs); + if (priv->effective_attrs) + pango_attr_list_unref (priv->effective_attrs); + if (priv->preedit_attrs) + pango_attr_list_unref (priv->preedit_attrs); + g_free (priv->text); g_free (priv->font_name); From 59105341bce12b2ec009ff953facbb7f7b10e85f Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 5 Jan 2010 11:58:12 +0000 Subject: [PATCH 50/83] text: Store the markup attributes separately Previously when the markup property is set it would generate an attribute list from the markup and then merge it with the attributes from the attribute property and store it as the effective attributes. The markup attributes and the marked up text would then be forgotten. This breaks if the application then later changes the attributes property because it would try to regenerate the effective attributes from the markup text but the stored text no longer contains any markup. If the original markup text happened to contain entities like '<' they would end up causing parse errors because they would be converted to the actual symbols. To fix this the attributes from the markup are now stored independently from the effective attributes. The effective attributes are now regenerated if either set of attributes changes right before a layout is created. http://bugzilla.openedhand.com/show_bug.cgi?id=1940 --- clutter/clutter-text.c | 134 +++++++++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 52 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 9925d903d..4cf56b5ba 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -112,8 +112,18 @@ struct _ClutterTextPrivate LayoutCache cached_layouts[N_CACHED_LAYOUTS]; guint cache_age; + /* These are the attributes set by the attributes property */ PangoAttrList *attrs; + /* These are the attributes derived from the text when the + use-markup property is set */ + PangoAttrList *markup_attrs; + /* This is the combination of the above two lists. It is set to NULL + whenever either of them changes and then regenerated by merging + the two lists whenever a layout is needed */ PangoAttrList *effective_attrs; + /* These are the attributes for the preedit string. These are merged + with the effective attributes into a temporary list before + creating a layout */ PangoAttrList *preedit_attrs; guint alignment : 2; @@ -287,6 +297,52 @@ clutter_text_get_display_text (ClutterText *self) } } +static void +clutter_text_ensure_effective_attributes (ClutterText *self) +{ + ClutterTextPrivate *priv = self->priv; + + /* If we already have the effective attributes then we don't need to + do anything */ + if (priv->effective_attrs == NULL) + { + if (priv->attrs) + { + /* If there are no markup attributes then we can just use + these attributes directly */ + if (priv->markup_attrs == NULL) + priv->effective_attrs = pango_attr_list_ref (priv->attrs); + else + { + /* Otherwise we need to merge the two lists */ + PangoAttrIterator *iter; + GSList *attributes, *l; + + priv->effective_attrs = pango_attr_list_copy (priv->markup_attrs); + + iter = pango_attr_list_get_iterator (priv->attrs); + do + { + attributes = pango_attr_iterator_get_attrs (iter); + + for (l = attributes; l != NULL; l = l->next) + { + PangoAttribute *attr = l->data; + + pango_attr_list_insert (priv->effective_attrs, attr); + } + + g_slist_free (attributes); + } + while (pango_attr_iterator_next (iter)); + } + } + else if (priv->markup_attrs) + /* We can just use the markup attributes directly */ + priv->effective_attrs = pango_attr_list_ref (priv->markup_attrs); + } +} + static PangoLayout * clutter_text_create_layout_no_cache (ClutterText *text, gfloat allocation_width, @@ -333,8 +389,15 @@ clutter_text_create_layout_no_cache (ClutterText *text, else pango_layout_set_text (layout, contents, contents_len); - if (!priv->editable && priv->effective_attrs) - pango_layout_set_attributes (layout, priv->effective_attrs); + if (!priv->editable) + { + /* This will merge the markup attributes and the attributes + property if needed */ + clutter_text_ensure_effective_attributes (text); + + if (priv->effective_attrs) + pango_layout_set_attributes (layout, priv->effective_attrs); + } pango_layout_set_alignment (layout, priv->alignment); pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode); @@ -749,39 +812,6 @@ clutter_text_delete_selection (ClutterText *self) return TRUE; } -static void -clutter_text_merge_attributes (ClutterText *self) -{ - ClutterTextPrivate *priv = self->priv; - PangoAttrIterator *iter; - GSList *attributes, *l; - - if (!priv->attrs) - return; - - if (!priv->effective_attrs) - { - priv->effective_attrs = pango_attr_list_ref (priv->attrs); - return; - } - - iter = pango_attr_list_get_iterator (priv->attrs); - do - { - attributes = pango_attr_iterator_get_attrs (iter); - - for (l = attributes; l != NULL; l = l->next) - { - PangoAttribute *attr = l->data; - - pango_attr_list_insert (priv->effective_attrs, attr); - } - - g_slist_free (attributes); - } - while (pango_attr_iterator_next (iter)); -} - static inline void clutter_text_set_text_internal (ClutterText *self, const gchar *text) @@ -885,15 +915,18 @@ clutter_text_set_markup_internal (ClutterText *self, g_free (text); } - if (attrs) + /* Store the new markup attributes */ + if (priv->markup_attrs) + pango_attr_list_unref (priv->markup_attrs); + priv->markup_attrs = attrs; + + /* Clear the effective attributes so they will be regenerated when a + layout is created */ + if (priv->effective_attrs) { - if (priv->effective_attrs) - pango_attr_list_unref (priv->effective_attrs); - - priv->effective_attrs = attrs; + pango_attr_list_unref (priv->effective_attrs); + priv->effective_attrs = NULL; } - - clutter_text_merge_attributes (self); } static void @@ -1146,6 +1179,8 @@ clutter_text_finalize (GObject *gobject) if (priv->attrs) pango_attr_list_unref (priv->attrs); + if (priv->markup_attrs) + pango_attr_list_unref (priv->markup_attrs); if (priv->effective_attrs) pango_attr_list_unref (priv->effective_attrs); if (priv->preedit_attrs) @@ -3858,18 +3893,13 @@ clutter_text_set_attributes (ClutterText *self, priv->attrs = attrs; - if (!priv->use_markup) + /* Clear the effective attributes so they will be regenerated when a + layout is created */ + if (priv->effective_attrs) { - if (attrs) - pango_attr_list_ref (attrs); - - if (priv->effective_attrs) - pango_attr_list_unref (priv->effective_attrs); - - priv->effective_attrs = attrs; + pango_attr_list_unref (priv->effective_attrs); + priv->effective_attrs = NULL; } - else - clutter_text_set_markup_internal (self, priv->text); clutter_text_dirty_cache (self); From 793fec8138b478ba1f1ea419f9ef8bed4f453e77 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 5 Jan 2010 16:11:45 +0000 Subject: [PATCH 51/83] cogl: Fix array annotations The arrays in the cogl_program_set_uniform_* API should be marked as such, and have their length arguments specified. --- clutter/cogl/cogl/cogl-shader.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clutter/cogl/cogl/cogl-shader.h b/clutter/cogl/cogl/cogl-shader.h index 9eec9bb40..2bb082d35 100644 --- a/clutter/cogl/cogl/cogl-shader.h +++ b/clutter/cogl/cogl/cogl-shader.h @@ -265,22 +265,22 @@ void cogl_program_uniform_1i (int uniform_no, * @uniform_no: the uniform to set. * @size: Size of float vector. * @count: Size of array of uniforms. - * @value: the new value of the uniform. + * @value: (array length=count): the new value of the uniform. * * Changes the value of a float vector uniform, or uniform array in the - * currently used (see #cogl_program_use) shader program. + * currently used (see cogl_program_use()) shader program. */ -void cogl_program_uniform_float (int uniform_no, - int size, - int count, - const GLfloat *value); +void cogl_program_uniform_float (int uniform_no, + int size, + int count, + const GLfloat *value); /** * cogl_program_uniform_int: * @uniform_no: the uniform to set. * @size: Size of int vector. * @count: Size of array of uniforms. - * @value: the new value of the uniform. + * @value: (array length=count): the new value of the uniform. * * Changes the value of a int vector uniform, or uniform array in the * currently used (see cogl_program_use()) shader program. @@ -296,7 +296,7 @@ void cogl_program_uniform_int (int uniform_no, * @size: Size of matrix. * @count: Size of array of uniforms. * @transpose: Whether to transpose the matrix when setting the uniform. - * @value: the new value of the uniform. + * @value: (array length=count): the new value of the uniform. * * Changes the value of a matrix uniform, or uniform array in the * currently used (see cogl_program_use()) shader program. The @size From 58b5a46e0e6b263bd32808e4a9a46bbdd81eedfe Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 5 Jan 2010 17:54:45 +0000 Subject: [PATCH 52/83] Include cogl-defines.h before using GL types If we are using GL* types we should also be including cogl-defines.h, as that will include the right GL header. --- clutter/cogl/cogl/cogl-shader.h | 1 + clutter/cogl/cogl/cogl-texture.h | 1 + 2 files changed, 2 insertions(+) diff --git a/clutter/cogl/cogl/cogl-shader.h b/clutter/cogl/cogl/cogl-shader.h index 2bb082d35..4e2ccb97b 100644 --- a/clutter/cogl/cogl/cogl-shader.h +++ b/clutter/cogl/cogl/cogl-shader.h @@ -29,6 +29,7 @@ #define __COGL_SHADER_H__ #include +#include G_BEGIN_DECLS diff --git a/clutter/cogl/cogl/cogl-texture.h b/clutter/cogl/cogl/cogl-texture.h index 7859abd19..eaf3e5482 100644 --- a/clutter/cogl/cogl/cogl-texture.h +++ b/clutter/cogl/cogl/cogl-texture.h @@ -29,6 +29,7 @@ #define __COGL_TEXTURE_H__ #include +#include G_BEGIN_DECLS From 8b59573190907da97c2af5a5be2e563fc66f8d51 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 5 Jan 2010 18:02:29 +0000 Subject: [PATCH 53/83] Covert stb_image.c to Unix format The file is still in DOS format (CRLF instead of LF) and this confuses the hell out of some versions of Git. --- clutter/cogl/cogl/stb_image.c | 7544 ++++++++++++++++----------------- 1 file changed, 3772 insertions(+), 3772 deletions(-) diff --git a/clutter/cogl/cogl/stb_image.c b/clutter/cogl/cogl/stb_image.c index bd3e63b35..c6f8d730d 100644 --- a/clutter/cogl/cogl/stb_image.c +++ b/clutter/cogl/cogl/stb_image.c @@ -1,3772 +1,3772 @@ -/* stbi-1.12 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c - when you control the images you're loading - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline (no JPEG progressive, no oddball channel decimations) - PNG non-interlaced - BMP non-1bpp, non-RLE - TGA (not sure what subset, if a subset) - PSD (composited view only, no extra channels) - HDR (radiance rgbE format) - writes BMP,TGA (define STBI_NO_WRITE to remove code) - decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code) - supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) - - TODO: - stbi_info_* - - history: - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less - than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant -*/ - - -//// begin header file //////////////////////////////////////////////////// -// -// Limitations: -// - no progressive/interlaced support (jpeg, png) -// - 8-bit samples only (jpeg, png) -// - not threadsafe -// - channel subsampling of at most 2 in each dimension (jpeg) -// - no delayed line count (jpeg) -- IJG doesn't support either -// -// Basic usage (see HDR discussion below): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *comp -- outputs # of image components in image file -// int req_comp -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. -// If req_comp is non-zero, *comp has the number of components that _would_ -// have been output otherwise. E.g. if you set req_comp to 4, you will always -// get RGBA output, but you can check *comp to easily see if it's opaque. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() -// can be queried for an extremely brief, end-user unfriendly explanation -// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid -// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG and BMP images are automatically depalettized. -// -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); - - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_NO_HDR -#include // ldexp -#include // strcmp -#endif - -enum -{ - STBI_default = 0, // only used for req_comp - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4, -}; - -typedef unsigned char stbi_uc; - -#ifdef __cplusplus -extern "C" { -#endif - -// WRITING API - -#if !defined(STBI_NO_WRITE) && !defined(STBI_NO_STDIO) -// write a BMP/TGA file given tightly packed 'comp' channels (no padding, nor bmp-stride-padding) -// (you must include the appropriate extension in the filename). -// returns TRUE on success, FALSE if couldn't open file, error writing file -extern int stbi_write_bmp (char const *filename, int x, int y, int comp, void *data); -extern int stbi_write_tga (char const *filename, int x, int y, int comp, void *data); -#endif - -// PRIMARY API - works on images of any type - -// load image by filename, open file, or memory buffer -#ifndef STBI_NO_STDIO -extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -#endif -extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); -// for stbi_load_from_file, file pointer is left pointing immediately after image - -#ifndef STBI_NO_HDR -#ifndef STBI_NO_STDIO -extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); -extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -#endif -extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - -extern void stbi_hdr_to_ldr_gamma(float gamma); -extern void stbi_hdr_to_ldr_scale(float scale); - -extern void stbi_ldr_to_hdr_gamma(float gamma); -extern void stbi_ldr_to_hdr_scale(float scale); - -#endif // STBI_NO_HDR - -// get a VERY brief reason for failure -extern char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -extern void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -extern int stbi_info (char const *filename, int *x, int *y, int *comp); -extern int stbi_is_hdr (char const *filename); -extern int stbi_is_hdr_from_file(FILE *f); -#endif - -// ZLIB client - used by PNG, available for other purposes - -extern char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen); -extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -// TYPE-SPECIFIC ACCESS - -// is it a jpeg? -extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len); -extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); -extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); - -#ifndef STBI_NO_STDIO -extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp); -extern int stbi_jpeg_test_file (FILE *f); -extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); - -extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); -extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); -#endif - -extern int stbi_jpeg_dc_only; // only decode DC component - -// is it a png? -extern int stbi_png_test_memory (stbi_uc const *buffer, int len); -extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); -extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); - -#ifndef STBI_NO_STDIO -extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp); -extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); -extern int stbi_png_test_file (FILE *f); -extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); -#endif - -// is it a bmp? -extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len); - -extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp); -extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); -#ifndef STBI_NO_STDIO -extern int stbi_bmp_test_file (FILE *f); -extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -#endif - -// is it a tga? -extern int stbi_tga_test_memory (stbi_uc const *buffer, int len); - -extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp); -extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); -#ifndef STBI_NO_STDIO -extern int stbi_tga_test_file (FILE *f); -extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -#endif - -// is it a psd? -extern int stbi_psd_test_memory (stbi_uc const *buffer, int len); - -extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp); -extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); -#ifndef STBI_NO_STDIO -extern int stbi_psd_test_file (FILE *f); -extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -#endif - -// is it an hdr? -extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len); - -extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp); -extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); -#ifndef STBI_NO_STDIO -extern int stbi_hdr_test_file (FILE *f); -extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -#endif - -// define new loaders -typedef struct -{ - int (*test_memory)(stbi_uc const *buffer, int len); - stbi_uc * (*load_from_memory)(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - #ifndef STBI_NO_STDIO - int (*test_file)(FILE *f); - stbi_uc * (*load_from_file)(FILE *f, int *x, int *y, int *comp, int req_comp); - #endif -} stbi_loader; - -// register a loader by filling out the above structure (you must defined ALL functions) -// returns 1 if added or already added, 0 if not added (too many loaders) -extern int stbi_register_loader(stbi_loader *loader); - -// define faster low-level operations (typically SIMD support) -#if STBI_SIMD -typedef void (*stbi_idct_8x8)(uint8 *out, int out_stride, short data[64], unsigned short *dequantize); -// compute an integer IDCT on "input" -// input[x] = data[x] * dequantize[x] -// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' -// CLAMP results to 0..255 -typedef void (*stbi_YCbCr_to_RGB_run)(uint8 *output, uint8 const *y, uint8 const *cb, uint8 const *cr, int count, int step); -// compute a conversion from YCbCr to RGB -// 'count' pixels -// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B -// y: Y input channel -// cb: Cb input channel; scale/biased to be 0..255 -// cr: Cr input channel; scale/biased to be 0..255 - -extern void stbi_install_idct(stbi_idct_8x8 func); -extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); -#endif // STBI_SIMD - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// - -#ifndef STBI_NO_STDIO -#include -#endif -#include -#include -#include -#include - -#if STBI_SIMD -#include -#endif - -#ifndef _MSC_VER -#define __forceinline -#endif - - -// implementation: -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef signed short int16; -typedef unsigned int uint32; -typedef signed int int32; -typedef unsigned int uint; - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(uint32)==4]; - -#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) -#define STBI_NO_WRITE -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Generic API that works on all image types -// - -static char *failure_reason; - -char *stbi_failure_reason(void) -{ - return failure_reason; -} - -static int e(char *str) -{ - failure_reason = str; - return 0; -} - -#ifdef STBI_NO_FAILURE_STRINGS - #define e(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define e(x,y) e(y) -#else - #define e(x,y) e(x) -#endif - -#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) -#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) - -void stbi_image_free(void *retval_from_stbi_load) -{ - free(retval_from_stbi_load); -} - -#define MAX_LOADERS 32 -stbi_loader *loaders[MAX_LOADERS]; -static int max_loaders = 0; - -int stbi_register_loader(stbi_loader *loader) -{ - int i; - for (i=0; i < MAX_LOADERS; ++i) { - // already present? - if (loaders[i] == loader) - return 1; - // end of the list? - if (loaders[i] == NULL) { - loaders[i] = loader; - max_loaders = i+1; - return 1; - } - } - // no room for it - return 0; -} - -#ifndef STBI_NO_HDR -static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_STDIO -unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = fopen(filename, "rb"); - unsigned char *result; - if (!f) return epuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - int i; - if (stbi_jpeg_test_file(f)) - return stbi_jpeg_load_from_file(f,x,y,comp,req_comp); - if (stbi_png_test_file(f)) - return stbi_png_load_from_file(f,x,y,comp,req_comp); - if (stbi_bmp_test_file(f)) - return stbi_bmp_load_from_file(f,x,y,comp,req_comp); - if (stbi_psd_test_file(f)) - return stbi_psd_load_from_file(f,x,y,comp,req_comp); - #ifndef STBI_NO_HDR - if (stbi_hdr_test_file(f)) { - float *hdr = stbi_hdr_load_from_file(f, x,y,comp,req_comp); - return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - for (i=0; i < max_loaders; ++i) - if (loaders[i]->test_file(f)) - return loaders[i]->load_from_file(f,x,y,comp,req_comp); - // test tga last because it's a crappy test! - if (stbi_tga_test_file(f)) - return stbi_tga_load_from_file(f,x,y,comp,req_comp); - return epuc("unknown image type", "Image not of any known type, or corrupt"); -} -#endif - -unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - int i; - if (stbi_jpeg_test_memory(buffer,len)) - return stbi_jpeg_load_from_memory(buffer,len,x,y,comp,req_comp); - if (stbi_png_test_memory(buffer,len)) - return stbi_png_load_from_memory(buffer,len,x,y,comp,req_comp); - if (stbi_bmp_test_memory(buffer,len)) - return stbi_bmp_load_from_memory(buffer,len,x,y,comp,req_comp); - if (stbi_psd_test_memory(buffer,len)) - return stbi_psd_load_from_memory(buffer,len,x,y,comp,req_comp); - #ifndef STBI_NO_HDR - if (stbi_hdr_test_memory(buffer, len)) { - float *hdr = stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); - return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - for (i=0; i < max_loaders; ++i) - if (loaders[i]->test_memory(buffer,len)) - return loaders[i]->load_from_memory(buffer,len,x,y,comp,req_comp); - // test tga last because it's a crappy test! - if (stbi_tga_test_memory(buffer,len)) - return stbi_tga_load_from_memory(buffer,len,x,y,comp,req_comp); - return epuc("unknown image type", "Image not of any known type, or corrupt"); -} - -#ifndef STBI_NO_HDR - -#ifndef STBI_NO_STDIO -float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = fopen(filename, "rb"); - float *result; - if (!f) return epf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi_hdr_test_file(f)) - return stbi_hdr_load_from_file(f,x,y,comp,req_comp); - #endif - data = stbi_load_from_file(f, x, y, comp, req_comp); - if (data) - return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return epf("unknown image type", "Image not of any known type, or corrupt"); -} -#endif - -float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *data; - #ifndef STBI_NO_HDR - if (stbi_hdr_test_memory(buffer, len)) - return stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); - #endif - data = stbi_load_from_memory(buffer, len, x, y, comp, req_comp); - if (data) - return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return epf("unknown image type", "Image not of any known type, or corrupt"); -} -#endif - -// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is -// defined, for API simplicity; if STBI_NO_HDR is defined, it always -// reports false! - -extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - return stbi_hdr_test_memory(buffer, len); - #else - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -extern int stbi_is_hdr (char const *filename) -{ - FILE *f = fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -extern int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - return stbi_hdr_test_file(f); - #else - return 0; - #endif -} - -#endif - -// @TODO: get image dimensions & components without fully decoding -#ifndef STBI_NO_STDIO -extern int stbi_info (char const *filename, int *x, int *y, int *comp); -extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -#endif -extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); - -#ifndef STBI_NO_HDR -static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; -static float l2h_gamma=2.2f, l2h_scale=1.0f; - -void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } -void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } - -void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } -void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -// image width, height, # components -static uint32 img_x, img_y; -static int img_n, img_out_n; - -enum -{ - SCAN_load=0, - SCAN_type, - SCAN_header, -}; - -// An API for reading either from memory or file. -#ifndef STBI_NO_STDIO -static FILE *img_file; -#endif -static uint8 const *img_buffer, *img_buffer_end; - -#ifndef STBI_NO_STDIO -static void start_file(FILE *f) -{ - img_file = f; -} -#endif - -static void start_mem(uint8 const *buffer, int len) -{ -#ifndef STBI_NO_STDIO - img_file = NULL; -#endif - img_buffer = buffer; - img_buffer_end = buffer+len; -} - -static int get8(void) -{ -#ifndef STBI_NO_STDIO - if (img_file) { - int c = fgetc(img_file); - return c == EOF ? 0 : c; - } -#endif - if (img_buffer < img_buffer_end) - return *img_buffer++; - return 0; -} - -static int at_eof(void) -{ -#ifndef STBI_NO_STDIO - if (img_file) - return feof(img_file); -#endif - return img_buffer >= img_buffer_end; -} - -static uint8 get8u(void) -{ - return (uint8) get8(); -} - -static void skip(int n) -{ -#ifndef STBI_NO_STDIO - if (img_file) - fseek(img_file, n, SEEK_CUR); - else -#endif - img_buffer += n; -} - -static int get16(void) -{ - int z = get8(); - return (z << 8) + get8(); -} - -static uint32 get32(void) -{ - uint32 z = get16(); - return (z << 16) + get16(); -} - -static int get16le(void) -{ - int z = get8(); - return z + (get8() << 8); -} - -static uint32 get32le(void) -{ - uint32 z = get16le(); - return z + (get16le() << 16); -} - -static void getn(stbi_uc *buffer, int n) -{ -#ifndef STBI_NO_STDIO - if (img_file) { - fread(buffer, 1, n, img_file); - return; - } -#endif - memcpy(buffer, img_buffer, n); - img_buffer += n; -} - -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static uint8 compute_y(int r, int g, int b) -{ - return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp) -{ - uint i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - assert(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) malloc(req_comp * img_x * img_y); - if (good == NULL) { - free(data); - return epuc("outofmem", "Out of memory"); - } - - for (j=0; j < img_y; ++j) { - unsigned char *src = data + j * img_x * img_n ; - unsigned char *dest = good + j * img_x * req_comp; - - #define COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case COMBO(a,b): for(i=0; i < img_x; ++i, src += a, dest += b) - - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch(COMBO(img_n, req_comp)) { - CASE(1,2) dest[0]=src[0], dest[1]=255; break; - CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; - CASE(2,1) dest[0]=src[0]; break; - CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; - CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; - CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; - CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; - CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; - CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; - default: assert(0); - } - #undef CASE - } - - free(data); - img_out_n = req_comp; - return good; -} - -#ifndef STBI_NO_HDR -static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output = (float *) malloc(x * y * comp * sizeof(float)); - if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; - } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; - } - free(data); - return output; -} - -#define float2int(x) ((int) (x)) -static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output = (stbi_uc *) malloc(x * y * comp); - if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = float2int(z); - } - } - free(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) -// -// simple implementation -// - channel subsampling of at most 2 in each dimension -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - uses a lot of intermediate memory, could cache poorly -// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 -// stb_jpeg: 1.34 seconds (MSVC6, default release build) -// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) -// IJL11.dll: 1.08 seconds (compiled by intel) -// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) -// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) - -int stbi_jpeg_dc_only; - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - uint8 fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - uint16 code[256]; - uint8 values[256]; - uint8 size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} huffman; - -static huffman huff_dc[4]; // baseline is 2 tables, extended is 4 -static huffman huff_ac[4]; -static uint8 dequant[4][64]; -#if STBI_SIMD -static __declspec(align(16)) unsigned short dequant2[4][64]; -#endif - -static int build_huffman(huffman *h, int *count) -{ - int i,j,k=0,code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (uint8) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (uint16) (code++); - if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (uint8) i; - } - } - } - return 1; -} - -// sizes for components, interleaved MCUs -static int img_h_max, img_v_max; -static int img_mcu_x, img_mcu_y; -static int img_mcu_w, img_mcu_h; - -// definition of jpeg image component -static struct -{ - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - uint8 *data; - void *raw_data; - uint8 *linebuf; -} img_comp[4]; - -static uint32 code_buffer; // jpeg entropy-coded buffer -static int code_bits; // number of valid bits -static unsigned char marker; // marker seen while filling entropy buffer -static int nomore; // flag if we saw a marker so must stop - -static void grow_buffer_unsafe(void) -{ - do { - int b = nomore ? 0 : get8(); - if (b == 0xff) { - int c = get8(); - if (c != 0) { - marker = (unsigned char) c; - nomore = 1; - return; - } - } - code_buffer = (code_buffer << 8) | b; - code_bits += 8; - } while (code_bits <= 24); -} - -// (1 << n) - 1 -static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -__forceinline static int decode(huffman *h) -{ - unsigned int temp; - int c,k; - - if (code_bits < 16) grow_buffer_unsafe(); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (code_buffer >> (code_bits - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - if (h->size[k] > code_bits) - return -1; - code_bits -= h->size[k]; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - if (code_bits < 16) - temp = (code_buffer << (16 - code_bits)) & 0xffff; - else - temp = (code_buffer >> (code_bits - 16)) & 0xffff; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - code_bits -= 16; - return -1; - } - - if (k > code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((code_buffer >> (code_bits - k)) & bmask[k]) + h->delta[k]; - assert((((code_buffer) >> (code_bits - h->size[c])) & bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - code_bits -= k; - return h->values[c]; -} - -// combined JPEG 'receive' and JPEG 'extend', since baseline -// always extends everything it receives. -__forceinline static int extend_receive(int n) -{ - unsigned int m = 1 << (n-1); - unsigned int k; - if (code_bits < n) grow_buffer_unsafe(); - k = (code_buffer >> (code_bits - n)) & bmask[n]; - code_bits -= n; - // the following test is probably a random branch that won't - // predict well. I tried to table accelerate it but failed. - // maybe it's compiling as a conditional move? - if (k < m) - return (-1 << n) + k + 1; - else - return k; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static uint8 dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int decode_block(short data[64], huffman *hdc, huffman *hac, int b) -{ - int diff,dc,k; - int t = decode(hdc); - if (t < 0) return e("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? extend_receive(t) : 0; - dc = img_comp[b].dc_pred + diff; - img_comp[b].dc_pred = dc; - data[0] = (short) dc; - - // decode AC components, see JPEG spec - k = 1; - do { - int r,s; - int rs = decode(hac); - if (rs < 0) return e("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - data[dezigzag[k++]] = (short) extend_receive(s); - } - } while (k < 64); - return 1; -} - -// take a -128..127 value and clamp it and convert to 0..255 -__forceinline static uint8 clamp(int x) -{ - x += 128; - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (uint8) x; -} - -#define f2f(x) (int) (((x) * 4096 + 0.5)) -#define fsh(x) ((x) << 12) - -// derived from jidctint -- DCT_ISLOW -#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * f2f(0.5411961f); \ - t2 = p1 + p3*f2f(-1.847759065f); \ - t3 = p1 + p2*f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = fsh(p2+p3); \ - t1 = fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*f2f( 1.175875602f); \ - t0 = t0*f2f( 0.298631336f); \ - t1 = t1*f2f( 2.053119869f); \ - t2 = t2*f2f( 3.072711026f); \ - t3 = t3*f2f( 1.501321110f); \ - p1 = p5 + p1*f2f(-0.899976223f); \ - p2 = p5 + p2*f2f(-2.562915447f); \ - p3 = p3*f2f(-1.961570560f); \ - p4 = p4*f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -#if !STBI_SIMD -// .344 seconds on 3*anemones.jpg -static void idct_block(uint8 *out, int out_stride, short data[64], uint8 *dequantize) -{ - int i,val[64],*v=val; - uint8 *o,*dq = dequantize; - short *d = data; - - if (stbi_jpeg_dc_only) { - // ok, I don't really know why this is right, but it seems to be: - int z = 128 + ((d[0] * dq[0]) >> 3); - for (i=0; i < 8; ++i) { - out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = z; - out += out_stride; - } - return; - } - - // columns - for (i=0; i < 8; ++i,++d,++dq, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0] * dq[0] << 2; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], - d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536; - o[0] = clamp((x0+t3) >> 17); - o[7] = clamp((x0-t3) >> 17); - o[1] = clamp((x1+t2) >> 17); - o[6] = clamp((x1-t2) >> 17); - o[2] = clamp((x2+t1) >> 17); - o[5] = clamp((x2-t1) >> 17); - o[3] = clamp((x3+t0) >> 17); - o[4] = clamp((x3-t0) >> 17); - } -} -#else -static void idct_block(uint8 *out, int out_stride, short data[64], unsigned short *dequantize) -{ - int i,val[64],*v=val; - uint8 *o; - unsigned short *dq = dequantize; - short *d = data; - - if (stbi_jpeg_dc_only) { - // ok, I don't really know why this is right, but it seems to be: - int z = 128 + ((d[0] * dq[0]) >> 3); - for (i=0; i < 8; ++i) { - out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = z; - out += out_stride; - } - return; - } - - // columns - for (i=0; i < 8; ++i,++d,++dq, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0] * dq[0] << 2; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], - d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536; - o[0] = clamp((x0+t3) >> 17); - o[7] = clamp((x0-t3) >> 17); - o[1] = clamp((x1+t2) >> 17); - o[6] = clamp((x1-t2) >> 17); - o[2] = clamp((x2+t1) >> 17); - o[5] = clamp((x2-t1) >> 17); - o[3] = clamp((x3+t0) >> 17); - o[4] = clamp((x3-t0) >> 17); - } -} -static stbi_idct_8x8 stbi_idct_installed = idct_block; - -extern void stbi_install_idct(stbi_idct_8x8 func) -{ - stbi_idct_installed = func; -} -#endif - -#define MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static uint8 get_marker(void) -{ - uint8 x; - if (marker != MARKER_none) { x = marker; marker = MARKER_none; return x; } - x = get8u(); - if (x != 0xff) return MARKER_none; - while (x == 0xff) - x = get8u(); - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -static int scan_n, order[4]; -static int restart_interval, todo; -#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, reset the entropy decoder and -// the dc prediction -static void reset(void) -{ - code_bits = 0; - code_buffer = 0; - nomore = 0; - img_comp[0].dc_pred = img_comp[1].dc_pred = img_comp[2].dc_pred = 0; - marker = MARKER_none; - todo = restart_interval ? restart_interval : 0x7fffffff; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int parse_entropy_coded_data(void) -{ - reset(); - if (scan_n == 1) { - int i,j; - #if STBI_SIMD - __declspec(align(16)) - #endif - short data[64]; - int n = order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (img_comp[n].x+7) >> 3; - int h = (img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0; - #if STBI_SIMD - stbi_idct_installed(img_comp[n].data+img_comp[n].w2*j*8+i*8, img_comp[n].w2, data, dequant2[img_comp[n].tq]); - #else - idct_block(img_comp[n].data+img_comp[n].w2*j*8+i*8, img_comp[n].w2, data, dequant[img_comp[n].tq]); - #endif - // every data block is an MCU, so countdown the restart interval - if (--todo <= 0) { - if (code_bits < 24) grow_buffer_unsafe(); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!RESTART(marker)) return 1; - reset(); - } - } - } - } else { // interleaved! - int i,j,k,x,y; - short data[64]; - for (j=0; j < img_mcu_y; ++j) { - for (i=0; i < img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < scan_n; ++k) { - int n = order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < img_comp[n].v; ++y) { - for (x=0; x < img_comp[n].h; ++x) { - int x2 = (i*img_comp[n].h + x)*8; - int y2 = (j*img_comp[n].v + y)*8; - if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0; - #if STBI_SIMD - stbi_idct_installed(img_comp[n].data+img_comp[n].w2*y2+x2, img_comp[n].w2, data, dequant2[img_comp[n].tq]); - #else - idct_block(img_comp[n].data+img_comp[n].w2*y2+x2, img_comp[n].w2, data, dequant[img_comp[n].tq]); - #endif - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--todo <= 0) { - if (code_bits < 24) grow_buffer_unsafe(); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!RESTART(marker)) return 1; - reset(); - } - } - } - } - return 1; -} - -static int process_marker(int m) -{ - int L; - switch (m) { - case MARKER_none: // no marker found - return e("expected marker","Corrupt JPEG"); - - case 0xC2: // SOF - progressive - return e("progressive jpeg","JPEG format not supported (progressive)"); - - case 0xDD: // DRI - specify restart interval - if (get16() != 4) return e("bad DRI len","Corrupt JPEG"); - restart_interval = get16(); - return 1; - - case 0xDB: // DQT - define quantization table - L = get16()-2; - while (L > 0) { - int z = get8(); - int p = z >> 4; - int t = z & 15,i; - if (p != 0) return e("bad DQT type","Corrupt JPEG"); - if (t > 3) return e("bad DQT table","Corrupt JPEG"); - for (i=0; i < 64; ++i) - dequant[t][dezigzag[i]] = get8u(); - #if STBI_SIMD - for (i=0; i < 64; ++i) - dequant2[t][i] = dequant[t][i]; - #endif - L -= 65; - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = get16()-2; - while (L > 0) { - uint8 *v; - int sizes[16],i,m=0; - int z = get8(); - int tc = z >> 4; - int th = z & 15; - if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = get8(); - m += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!build_huffman(huff_dc+th, sizes)) return 0; - v = huff_dc[th].values; - } else { - if (!build_huffman(huff_ac+th, sizes)) return 0; - v = huff_ac[th].values; - } - for (i=0; i < m; ++i) - v[i] = get8u(); - L -= m; - } - return L==0; - } - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - skip(get16()-2); - return 1; - } - return 0; -} - -// after we see SOS -static int process_scan_header(void) -{ - int i; - int Ls = get16(); - scan_n = get8(); - if (scan_n < 1 || scan_n > 4 || scan_n > (int) img_n) return e("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*scan_n) return e("bad SOS len","Corrupt JPEG"); - for (i=0; i < scan_n; ++i) { - int id = get8(), which; - int z = get8(); - for (which = 0; which < img_n; ++which) - if (img_comp[which].id == id) - break; - if (which == img_n) return 0; - img_comp[which].hd = z >> 4; if (img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); - img_comp[which].ha = z & 15; if (img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); - order[i] = which; - } - if (get8() != 0) return e("bad SOS","Corrupt JPEG"); - get8(); // should be 63, but might be 0 - if (get8() != 0) return e("bad SOS","Corrupt JPEG"); - - return 1; -} - -static int process_frame_header(int scan) -{ - int Lf,p,i,z, h_max=1,v_max=1; - Lf = get16(); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG - p = get8(); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - img_y = get16(); if (img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - img_x = get16(); if (img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires - img_n = get8(); - if (img_n != 3 && img_n != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires - - if (Lf != 8+3*img_n) return e("bad SOF len","Corrupt JPEG"); - - for (i=0; i < img_n; ++i) { - img_comp[i].id = get8(); - if (img_comp[i].id != i+1) // JFIF requires - if (img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! - return e("bad component ID","Corrupt JPEG"); - z = get8(); - img_comp[i].h = (z >> 4); if (!img_comp[i].h || img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); - img_comp[i].v = z & 15; if (!img_comp[i].v || img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); - img_comp[i].tq = get8(); if (img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); - } - - if (scan != SCAN_load) return 1; - - if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode"); - - for (i=0; i < img_n; ++i) { - if (img_comp[i].h > h_max) h_max = img_comp[i].h; - if (img_comp[i].v > v_max) v_max = img_comp[i].v; - } - - // compute interleaved mcu info - img_h_max = h_max; - img_v_max = v_max; - img_mcu_w = h_max * 8; - img_mcu_h = v_max * 8; - img_mcu_x = (img_x + img_mcu_w-1) / img_mcu_w; - img_mcu_y = (img_y + img_mcu_h-1) / img_mcu_h; - - for (i=0; i < img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - img_comp[i].x = (img_x * img_comp[i].h + h_max-1) / h_max; - img_comp[i].y = (img_y * img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - img_comp[i].w2 = img_mcu_x * img_comp[i].h * 8; - img_comp[i].h2 = img_mcu_y * img_comp[i].v * 8; - img_comp[i].raw_data = malloc(img_comp[i].w2 * img_comp[i].h2+15); - if (img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - free(img_comp[i].raw_data); - img_comp[i].data = NULL; - } - return e("outofmem", "Out of memory"); - } - img_comp[i].data = (uint8*) (((int) img_comp[i].raw_data + 15) & ~15); - img_comp[i].linebuf = NULL; - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define DNL(x) ((x) == 0xdc) -#define SOI(x) ((x) == 0xd8) -#define EOI(x) ((x) == 0xd9) -#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) -#define SOS(x) ((x) == 0xda) - -static int decode_jpeg_header(int scan) -{ - int m; - marker = MARKER_none; // initialize cached marker to empty - m = get_marker(); - if (!SOI(m)) return e("no SOI","Corrupt JPEG"); - if (scan == SCAN_type) return 1; - m = get_marker(); - while (!SOF(m)) { - if (!process_marker(m)) return 0; - m = get_marker(); - while (m == MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (at_eof()) return e("no SOF", "Corrupt JPEG"); - m = get_marker(); - } - } - if (!process_frame_header(scan)) return 0; - return 1; -} - -static int decode_jpeg_image(void) -{ - int m; - restart_interval = 0; - if (!decode_jpeg_header(SCAN_load)) return 0; - m = get_marker(); - while (!EOI(m)) { - if (SOS(m)) { - if (!process_scan_header()) return 0; - if (!parse_entropy_coded_data()) return 0; - } else { - if (!process_marker(m)) return 0; - } - m = get_marker(); - } - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, - int w, int hs); - -#define div4(x) ((uint8) ((x) >> 2)) - -static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) -{ - return in_near; -} - -static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - for (i=0; i < w; ++i) - out[i] = div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - uint8 *input = in_near; - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = div4(n+input[i-1]); - out[i*2+1] = div4(n+input[i+1]); - } - out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - return out; -} - -#define div16(x) ((uint8) ((x) >> 4)) - -static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = div16(3*t0 + t1 + 8); - out[i*2 ] = div16(3*t1 + t0 + 8); - } - out[w*2-1] = div4(t1+2); - return out; -} - -static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) - -// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) -// VC6 without processor=Pro is generating multiple LEAs per multiply! -static void YCbCr_to_RGB_row(uint8 *out, uint8 *y, uint8 *pcb, uint8 *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 16) + 32768; // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr*float2fixed(1.40200f); - g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); - b = y_fixed + cb*float2fixed(1.77200f); - r >>= 16; - g >>= 16; - b >>= 16; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (uint8)r; - out[1] = (uint8)g; - out[2] = (uint8)b; - out[3] = 255; - out += step; - } -} - -#if STBI_SIMD -static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; - -void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) -{ - stbi_YCbCr_installed = func; -} -#endif - - -// clean up the temporary component buffers -static void cleanup_jpeg(void) -{ - int i; - for (i=0; i < img_n; ++i) { - if (img_comp[i].data) { - free(img_comp[i].raw_data); - img_comp[i].data = NULL; - } - if (img_comp[i].linebuf) { - free(img_comp[i].linebuf); - img_comp[i].linebuf = NULL; - } - } -} - -typedef struct -{ - resample_row_func resample; - uint8 *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi_resample; - -static uint8 *load_jpeg_image(int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n; - // validate req_comp - if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source - if (!decode_jpeg_image()) { cleanup_jpeg(); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : img_n; - - if (img_n == 3 && n < 3) - decode_n = 1; - else - decode_n = img_n; - - // resample and color-convert - { - int k; - uint i,j; - uint8 *output; - uint8 *coutput[4]; - - stbi_resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi_resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - img_comp[k].linebuf = (uint8 *) malloc(img_x + 3); - if (!img_comp[k].linebuf) { cleanup_jpeg(); return epuc("outofmem", "Out of memory"); } - - r->hs = img_h_max / img_comp[k].h; - r->vs = img_v_max / img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; - else r->resample = resample_row_generic; - } - - // can't error after this so, this is safe - output = (uint8 *) malloc(n * img_x * img_y + 1); - if (!output) { cleanup_jpeg(); return epuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < img_y; ++j) { - uint8 *out = output + n * img_x * j; - for (k=0; k < decode_n; ++k) { - stbi_resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < img_comp[k].y) - r->line1 += img_comp[k].w2; - } - } - if (n >= 3) { - uint8 *y = coutput[0]; - if (img_n == 3) { - #if STBI_SIMD - stbi_YCbCr_installed(out, y, coutput[1], coutput[2], img_x, n); - #else - YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], img_x, n); - #endif - } else - for (i=0; i < img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - uint8 *y = coutput[0]; - if (n == 1) - for (i=0; i < img_x; ++i) out[i] = y[i]; - else - for (i=0; i < img_x; ++i) *out++ = y[i], *out++ = 255; - } - } - cleanup_jpeg(); - *out_x = img_x; - *out_y = img_y; - if (comp) *comp = img_n; // report original components, not output - return output; - } -} - -#ifndef STBI_NO_STDIO -unsigned char *stbi_jpeg_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - start_file(f); - return load_jpeg_image(x,y,comp,req_comp); -} - -unsigned char *stbi_jpeg_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - FILE *f = fopen(filename, "rb"); - if (!f) return NULL; - data = stbi_jpeg_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return data; -} -#endif - -unsigned char *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - start_mem(buffer,len); - return load_jpeg_image(x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -int stbi_jpeg_test_file(FILE *f) -{ - int n,r; - n = ftell(f); - start_file(f); - r = decode_jpeg_header(SCAN_type); - fseek(f,n,SEEK_SET); - return r; -} -#endif - -int stbi_jpeg_test_memory(stbi_uc const *buffer, int len) -{ - start_mem(buffer,len); - return decode_jpeg_header(SCAN_type); -} - -// @TODO: -#ifndef STBI_NO_STDIO -extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); -extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); -#endif -extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define ZFAST_BITS 9 // accelerate all cases in default tables -#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - uint16 fast[1 << ZFAST_BITS]; - uint16 firstcode[16]; - int maxcode[17]; - uint16 firstsymbol[16]; - uint8 size[288]; - uint16 value[288]; -} zhuffman; - -__forceinline static int bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -__forceinline static int bit_reverse(int v, int bits) -{ - assert(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return bitreverse16(v) >> (16-bits); -} - -static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 255, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - assert(sizes[i] <= (1 << i)); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (uint16) code; - z->firstsymbol[i] = (uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - z->size[c] = (uint8)s; - z->value[c] = (uint16)i; - if (s <= ZFAST_BITS) { - int k = bit_reverse(next_code[s],s); - while (k < (1 << ZFAST_BITS)) { - z->fast[k] = (uint16) c; - k += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -static uint8 *zbuffer, *zbuffer_end; - -__forceinline static int zget8(void) -{ - if (zbuffer >= zbuffer_end) return 0; - return *zbuffer++; -} - -//static uint32 code_buffer; -static int num_bits; - -static void fill_bits(void) -{ - do { - assert(code_buffer < (1U << num_bits)); - code_buffer |= zget8() << num_bits; - num_bits += 8; - } while (num_bits <= 24); -} - -__forceinline static unsigned int zreceive(int n) -{ - unsigned int k; - if (num_bits < n) fill_bits(); - k = code_buffer & ((1 << n) - 1); - code_buffer >>= n; - num_bits -= n; - return k; -} - -__forceinline static int zhuffman_decode(zhuffman *z) -{ - int b,s,k; - if (num_bits < 16) fill_bits(); - b = z->fast[code_buffer & ZFAST_MASK]; - if (b < 0xffff) { - s = z->size[b]; - code_buffer >>= s; - num_bits -= s; - return z->value[b]; - } - - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = bit_reverse(code_buffer, 16); - for (s=ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - assert(z->size[b] == s); - code_buffer >>= s; - num_bits -= s; - return z->value[b]; -} - -static char *zout; -static char *zout_start; -static char *zout_end; -static int z_expandable; - -static int expand(int n) // need to make room for n bytes -{ - char *q; - int cur, limit; - if (!z_expandable) return e("output buffer limit","Corrupt PNG"); - cur = (int) (zout - zout_start); - limit = (int) (zout_end - zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *) realloc(zout_start, limit); - if (q == NULL) return e("outofmem", "Out of memory"); - zout_start = q; - zout = q + cur; - zout_end = q + limit; - return 1; -} - -static zhuffman z_length, z_distance; - -static int length_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static int length_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static int dist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int parse_huffman_block(void) -{ - for(;;) { - int z = zhuffman_decode(&z_length); - if (z < 256) { - if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= zout_end) if (!expand(1)) return 0; - *zout++ = (char) z; - } else { - uint8 *p; - int len,dist; - if (z == 256) return 1; - z -= 257; - len = length_base[z]; - if (length_extra[z]) len += zreceive(length_extra[z]); - z = zhuffman_decode(&z_distance); - if (z < 0) return e("bad huffman code","Corrupt PNG"); - dist = dist_base[z]; - if (dist_extra[z]) dist += zreceive(dist_extra[z]); - if (zout - zout_start < dist) return e("bad dist","Corrupt PNG"); - if (zout + len > zout_end) if (!expand(len)) return 0; - p = (uint8 *) (zout - dist); - while (len--) - *zout++ = *p++; - } - } -} - -static int compute_huffman_codes(void) -{ - static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - static zhuffman z_codelength; // static just to save stack space - uint8 lencodes[286+32+137];//padding for maximum single op - uint8 codelength_sizes[19]; - int i,n; - - int hlit = zreceive(5) + 257; - int hdist = zreceive(5) + 1; - int hclen = zreceive(4) + 4; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = zreceive(3); - codelength_sizes[length_dezigzag[i]] = (uint8) s; - } - if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < hlit + hdist) { - int c = zhuffman_decode(&z_codelength); - assert(c >= 0 && c < 19); - if (c < 16) - lencodes[n++] = (uint8) c; - else if (c == 16) { - c = zreceive(2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = zreceive(3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - assert(c == 18); - c = zreceive(7)+11; - memset(lencodes+n, 0, c); - n += c; - } - } - if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); - if (!zbuild_huffman(&z_length, lencodes, hlit)) return 0; - if (!zbuild_huffman(&z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int parse_uncompressed_block(void) -{ - uint8 header[4]; - int len,nlen,k; - if (num_bits & 7) - zreceive(num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (num_bits > 0) { - header[k++] = (uint8) (code_buffer & 255); // wtf this warns? - code_buffer >>= 8; - num_bits -= 8; - } - assert(num_bits == 0); - // now fill header the normal way - while (k < 4) - header[k++] = (uint8) zget8(); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); - if (zbuffer + len > zbuffer_end) return e("read past buffer","Corrupt PNG"); - if (zout + len > zout_end) - if (!expand(len)) return 0; - memcpy(zout, zbuffer, len); - zbuffer += len; - zout += len; - return 1; -} - -static int parse_zlib_header(void) -{ - int cmf = zget8(); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = zget8(); - if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static uint8 default_length[288], default_distance[32]; -static void init_defaults(void) -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) default_length[i] = 8; - for ( ; i <= 255; ++i) default_length[i] = 9; - for ( ; i <= 279; ++i) default_length[i] = 7; - for ( ; i <= 287; ++i) default_length[i] = 8; - - for (i=0; i <= 31; ++i) default_distance[i] = 5; -} - -static int parse_zlib(int parse_header) -{ - int final, type; - if (parse_header) - if (!parse_zlib_header()) return 0; - num_bits = 0; - code_buffer = 0; - do { - final = zreceive(1); - type = zreceive(2); - if (type == 0) { - if (!parse_uncompressed_block()) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!default_length[0]) init_defaults(); - if (!zbuild_huffman(&z_length , default_length , 288)) return 0; - if (!zbuild_huffman(&z_distance, default_distance, 32)) return 0; - } else { - if (!compute_huffman_codes()) return 0; - } - if (!parse_huffman_block()) return 0; - } - } while (!final); - return 1; -} - -static int do_zlib(char *obuf, int olen, int exp, int parse_header) -{ - zout_start = obuf; - zout = obuf; - zout_end = obuf + olen; - z_expandable = exp; - - return parse_zlib(parse_header); -} - -char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen) -{ - char *p = (char *) malloc(initial_size); - if (p == NULL) return NULL; - if (do_zlib(p, initial_size, 1, 1)) { - *outlen = (int) (zout - zout_start); - return zout_start; - } else { - free(zout_start); - return NULL; - } -} - -char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - zbuffer = (uint8 *) buffer; - zbuffer_end = (uint8 *) buffer+len; - return stbi_zlib_decode_malloc_guesssize(16384, outlen); -} - -int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - zbuffer = (uint8 *) ibuffer; - zbuffer_end = (uint8 *) ibuffer + ilen; - if (do_zlib(obuffer, olen, 0, 1)) - return (int) (zout - zout_start); - else - return -1; -} - -char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - char *p = (char *) malloc(16384); - if (p == NULL) return NULL; - zbuffer = (uint8 *) buffer; - zbuffer_end = (uint8 *) buffer+len; - if (do_zlib(p, 16384, 1, 0)) { - *outlen = (int) (zout - zout_start); - return zout_start; - } else { - free(zout_start); - return NULL; - } -} - -int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - zbuffer = (uint8 *) ibuffer; - zbuffer_end = (uint8 *) ibuffer + ilen; - if (do_zlib(obuffer, olen, 0, 0)) - return (int) (zout - zout_start); - else - return -1; -} - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - - -typedef struct -{ - uint32 length; - uint32 type; -} chunk; - -#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) - -static chunk get_chunk_header(void) -{ - chunk c; - c.length = get32(); - c.type = get32(); - return c; -} - -static int check_png_header(void) -{ - static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (get8() != png_sig[i]) return e("bad png sig","Not a PNG"); - return 1; -} - -static uint8 *idata, *expanded, *out; - -enum { - F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, - F_avg_first, F_paeth_first, -}; - -static uint8 first_row_filter[5] = -{ - F_none, F_sub, F_none, F_avg_first, F_paeth_first -}; - -static int paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -// create the png data from post-deflated data -static int create_png_image(uint8 *raw, uint32 raw_len, int out_n) -{ - uint32 i,j,stride = img_x*out_n; - int k; - assert(out_n == img_n || out_n == img_n+1); - out = (uint8 *) malloc(img_x * img_y * out_n); - if (!out) return e("outofmem", "Out of memory"); - if (raw_len != (img_n * img_x + 1) * img_y) return e("not enough pixels","Corrupt PNG"); - for (j=0; j < img_y; ++j) { - uint8 *cur = out + stride*j; - uint8 *prior = cur - stride; - int filter = *raw++; - if (filter > 4) return e("invalid filter","Corrupt PNG"); - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - // handle first pixel explicitly - for (k=0; k < img_n; ++k) { - switch(filter) { - case F_none : cur[k] = raw[k]; break; - case F_sub : cur[k] = raw[k]; break; - case F_up : cur[k] = raw[k] + prior[k]; break; - case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; - case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; - case F_avg_first : cur[k] = raw[k]; break; - case F_paeth_first: cur[k] = raw[k]; break; - } - } - if (img_n != out_n) cur[img_n] = 255; - raw += img_n; - cur += out_n; - prior += out_n; - // this is a little gross, so that we don't switch per-pixel or per-component - if (img_n == out_n) { - #define CASE(f) \ - case f: \ - for (i=1; i < img_x; ++i, raw+=img_n,cur+=img_n,prior+=img_n) \ - for (k=0; k < img_n; ++k) - switch(filter) { - CASE(F_none) cur[k] = raw[k]; break; - CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; - CASE(F_up) cur[k] = raw[k] + prior[k]; break; - CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; - CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; - CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; - CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; - } - #undef CASE - } else { - assert(img_n+1 == out_n); - #define CASE(f) \ - case f: \ - for (i=1; i < img_x; ++i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ - for (k=0; k < img_n; ++k) - switch(filter) { - CASE(F_none) cur[k] = raw[k]; break; - CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; - CASE(F_up) cur[k] = raw[k] + prior[k]; break; - CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; - CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; - CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; - CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; - } - #undef CASE - } - } - return 1; -} - -static int compute_transparency(uint8 tc[3], int out_n) -{ - uint32 i, pixel_count = img_x * img_y; - uint8 *p = out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - assert(out_n == 2 || out_n == 4); - - p = out; - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int expand_palette(uint8 *palette, int len, int pal_img_n) -{ - uint32 i, pixel_count = img_x * img_y; - uint8 *p, *temp_out, *orig = out; - - p = (uint8 *) malloc(pixel_count * pal_img_n); - if (p == NULL) return e("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - free(out); - out = temp_out; - return 1; -} - -static int parse_png_file(int scan, int req_comp) -{ - uint8 palette[1024], pal_img_n=0; - uint8 has_trans=0, tc[3]; - uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k; - - if (!check_png_header()) return 0; - - if (scan == SCAN_type) return 1; - - for(;;first=0) { - chunk c = get_chunk_header(); - if (first && c.type != PNG_TYPE('I','H','D','R')) - return e("first not IHDR","Corrupt PNG"); - switch (c.type) { - case PNG_TYPE('I','H','D','R'): { - int depth,color,interlace,comp,filter; - if (!first) return e("multiple IHDR","Corrupt PNG"); - if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); - img_x = get32(); if (img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); - img_y = get32(); if (img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); - depth = get8(); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); - color = get8(); if (color > 6) return e("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); - comp = get8(); if (comp) return e("bad comp method","Corrupt PNG"); - filter= get8(); if (filter) return e("bad filter method","Corrupt PNG"); - interlace = get8(); if (interlace) return e("interlaced","PNG not supported: interlaced mode"); - if (!img_x || !img_y) return e("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode"); - if (scan == SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - img_n = 1; - if ((1 << 30) / img_x / 4 < img_y) return e("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case PNG_TYPE('P','L','T','E'): { - if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = get8u(); - palette[i*4+1] = get8u(); - palette[i*4+2] = get8u(); - palette[i*4+3] = 255; - } - break; - } - - case PNG_TYPE('t','R','N','S'): { - if (idata) return e("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == SCAN_header) { img_n = 4; return 1; } - if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = get8u(); - } else { - if (!(img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); - if (c.length != (uint32) img_n*2) return e("bad tRNS len","Corrupt PNG"); - has_trans = 1; - for (k=0; k < img_n; ++k) - tc[k] = (uint8) get16(); // non 8-bit images will be larger - } - break; - } - - case PNG_TYPE('I','D','A','T'): { - if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); - if (scan == SCAN_header) { img_n = pal_img_n; return 1; } - if (ioff + c.length > idata_limit) { - uint8 *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - p = (uint8 *) realloc(idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); - idata = p; - } - #ifndef STBI_NO_STDIO - if (img_file) - { - if (fread(idata+ioff,1,c.length,img_file) != c.length) return e("outofdata","Corrupt PNG"); - } - else - #endif - { - memcpy(idata+ioff, img_buffer, c.length); - img_buffer += c.length; - } - ioff += c.length; - break; - } - - case PNG_TYPE('I','E','N','D'): { - uint32 raw_len; - if (scan != SCAN_load) return 1; - if (idata == NULL) return e("no IDAT","Corrupt PNG"); - expanded = (uint8 *) stbi_zlib_decode_malloc((char *) idata, ioff, (int *) &raw_len); - if (expanded == NULL) return 0; // zlib should set error - free(idata); idata = NULL; - if ((req_comp == img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - img_out_n = img_n+1; - else - img_out_n = img_n; - if (!create_png_image(expanded, raw_len, img_out_n)) return 0; - if (has_trans) - if (!compute_transparency(tc, img_out_n)) return 0; - if (pal_img_n) { - // pal_img_n == 3 or 4 - img_n = pal_img_n; // record the actual colors we had - img_out_n = pal_img_n; - if (req_comp >= 3) img_out_n = req_comp; - if (!expand_palette(palette, pal_len, img_out_n)) - return 0; - } - free(expanded); expanded = NULL; - return 1; - } - - default: - // if critical, fail - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - static char invalid_chunk[] = "XXXX chunk not known"; - invalid_chunk[0] = (uint8) (c.type >> 24); - invalid_chunk[1] = (uint8) (c.type >> 16); - invalid_chunk[2] = (uint8) (c.type >> 8); - invalid_chunk[3] = (uint8) (c.type >> 0); - #endif - return e(invalid_chunk, "PNG not supported: unknown chunk type"); - } - skip(c.length); - break; - } - // end of chunk, read and skip CRC - get8(); get8(); get8(); get8(); - } -} - -static unsigned char *do_png(int *x, int *y, int *n, int req_comp) -{ - unsigned char *result=NULL; - if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); - if (parse_png_file(SCAN_load, req_comp)) { - result = out; - out = NULL; - if (req_comp && req_comp != img_out_n) { - result = convert_format(result, img_out_n, req_comp); - if (result == NULL) return result; - } - *x = img_x; - *y = img_y; - if (n) *n = img_n; - } - free(out); out = NULL; - free(expanded); expanded = NULL; - free(idata); idata = NULL; - - return result; -} - -#ifndef STBI_NO_STDIO -unsigned char *stbi_png_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - start_file(f); - return do_png(x,y,comp,req_comp); -} - -unsigned char *stbi_png_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - FILE *f = fopen(filename, "rb"); - if (!f) return NULL; - data = stbi_png_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return data; -} -#endif - -unsigned char *stbi_png_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - start_mem(buffer,len); - return do_png(x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -int stbi_png_test_file(FILE *f) -{ - int n,r; - n = ftell(f); - start_file(f); - r = parse_png_file(SCAN_type,STBI_default); - fseek(f,n,SEEK_SET); - return r; -} -#endif - -int stbi_png_test_memory(stbi_uc const *buffer, int len) -{ - start_mem(buffer, len); - return parse_png_file(SCAN_type,STBI_default); -} - -// TODO: load header from png -#ifndef STBI_NO_STDIO -extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); -extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); -#endif -extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); - -// Microsoft/Windows BMP image - -static int bmp_test(void) -{ - int sz; - if (get8() != 'B') return 0; - if (get8() != 'M') return 0; - get32le(); // discard filesize - get16le(); // discard reserved - get16le(); // discard reserved - get32le(); // discard data offset - sz = get32le(); - if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; - return 0; -} - -#ifndef STBI_NO_STDIO -int stbi_bmp_test_file (FILE *f) -{ - int r,n = ftell(f); - start_file(f); - r = bmp_test(); - fseek(f,n,SEEK_SET); - return r; -} -#endif - -int stbi_bmp_test_memory (stbi_uc const *buffer, int len) -{ - start_mem(buffer, len); - return bmp_test(); -} - -// returns 0..31 for the highest set bit -static int high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; - return n; -} - -static int bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -static int shiftsigned(int v, int shift, int bits) -{ - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; -} - -static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp) -{ - unsigned int mr=0,mg=0,mb=0,ma=0; - stbi_uc pal[256][4]; - int psize=0,i,j,compress=0,width; - int bpp, flip_vertically, pad, target, offset, hsz; - if (get8() != 'B' || get8() != 'M') return epuc("not BMP", "Corrupt BMP"); - get32le(); // discard filesize - get16le(); // discard reserved - get16le(); // discard reserved - offset = get32le(); - hsz = get32le(); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); - failure_reason = "bad BMP"; - if (hsz == 12) { - img_x = get16le(); - img_y = get16le(); - } else { - img_x = get32le(); - img_y = get32le(); - } - if (get16le() != 1) return 0; - bpp = get16le(); - if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); - flip_vertically = ((int) img_y) > 0; - img_y = abs((int) img_y); - if (hsz == 12) { - if (bpp < 24) - psize = (offset - 14 - 24) / 3; - } else { - compress = get32le(); - if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); - get32le(); // discard sizeof - get32le(); // discard hres - get32le(); // discard vres - get32le(); // discard colorsused - get32le(); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - get32le(); - get32le(); - get32le(); - get32le(); - } - if (bpp == 16 || bpp == 32) { - mr = mg = mb = 0; - if (compress == 0) { - if (bpp == 32) { - mr = 0xff << 16; - mg = 0xff << 8; - mb = 0xff << 0; - } else { - mr = 31 << 10; - mg = 31 << 5; - mb = 31 << 0; - } - } else if (compress == 3) { - mr = get32le(); - mg = get32le(); - mb = get32le(); - // not documented, but generated by photoshop and handled by mspaint - if (mr == mg && mg == mb) { - // ?!?!? - return NULL; - } - } else - return NULL; - } - } else { - assert(hsz == 108); - mr = get32le(); - mg = get32le(); - mb = get32le(); - ma = get32le(); - get32le(); // discard color space - for (i=0; i < 12; ++i) - get32le(); // discard color space parameters - } - if (bpp < 16) - psize = (offset - 14 - hsz) >> 2; - } - img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = img_n; // if they want monochrome, we'll post-convert - out = (stbi_uc *) malloc(target * img_x * img_y); - if (!out) return epuc("outofmem", "Out of memory"); - if (bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) return epuc("invalid", "Corrupt BMP"); - for (i=0; i < psize; ++i) { - pal[i][2] = get8(); - pal[i][1] = get8(); - pal[i][0] = get8(); - if (hsz != 12) get8(); - pal[i][3] = 255; - } - skip(offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); - if (bpp == 4) width = (img_x + 1) >> 1; - else if (bpp == 8) width = img_x; - else return epuc("bad bpp", "Corrupt BMP"); - pad = (-width)&3; - for (j=0; j < (int) img_y; ++j) { - for (i=0; i < (int) img_x; i += 2) { - int v=get8(),v2=0; - if (bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) img_x) break; - v = (bpp == 8) ? get8() : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - skip(pad); - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - skip(offset - 14 - hsz); - if (bpp == 24) width = 3 * img_x; - else if (bpp == 16) width = 2*img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (bpp == 24) { - easy = 1; - } else if (bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0xff000000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) return epuc("bad masks", "Corrupt BMP"); - // right shift amt to put high bit in position #7 - rshift = high_bit(mr)-7; rcount = bitcount(mr); - gshift = high_bit(mg)-7; gcount = bitcount(mr); - bshift = high_bit(mb)-7; bcount = bitcount(mr); - ashift = high_bit(ma)-7; acount = bitcount(mr); - } - for (j=0; j < (int) img_y; ++j) { - if (easy) { - for (i=0; i < (int) img_x; ++i) { - int a; - out[z+2] = get8(); - out[z+1] = get8(); - out[z+0] = get8(); - z += 3; - a = (easy == 2 ? get8() : 255); - if (target == 4) out[z++] = a; - } - } else { - for (i=0; i < (int) img_x; ++i) { - uint32 v = (bpp == 16 ? get16le() : get32le()); - int a; - out[z++] = shiftsigned(v & mr, rshift, rcount); - out[z++] = shiftsigned(v & mg, gshift, gcount); - out[z++] = shiftsigned(v & mb, bshift, bcount); - a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); - if (target == 4) out[z++] = a; - } - } - skip(pad); - } - } - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) img_y>>1; ++j) { - stbi_uc *p1 = out + j *img_x*target; - stbi_uc *p2 = out + (img_y-1-j)*img_x*target; - for (i=0; i < (int) img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = convert_format(out, target, req_comp); - if (out == NULL) return out; // convert_format frees input on failure - } - - *x = img_x; - *y = img_y; - if (comp) *comp = target; - return out; -} - -#ifndef STBI_NO_STDIO -stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *data; - FILE *f = fopen(filename, "rb"); - if (!f) return NULL; - data = stbi_bmp_load_from_file(f, x,y,comp,req_comp); - fclose(f); - return data; -} - -stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) -{ - start_file(f); - return bmp_load(x,y,comp,req_comp); -} -#endif - -stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - start_mem(buffer, len); - return bmp_load(x,y,comp,req_comp); -} - -// Targa Truevision - TGA -// by Jonathan Dummer - -static int tga_test(void) -{ - int sz; - get8u(); // discard Offset - sz = get8u(); // color type - if( sz > 1 ) return 0; // only RGB or indexed allowed - sz = get8u(); // image type - if( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE - get16(); // discard palette start - get16(); // discard palette length - get8(); // discard bits per palette color entry - get16(); // discard x origin - get16(); // discard y origin - if( get16() < 1 ) return 0; // test width - if( get16() < 1 ) return 0; // test height - sz = get8(); // bits per pixel - if( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed - return 1; // seems to have passed everything -} - -#ifndef STBI_NO_STDIO -int stbi_tga_test_file (FILE *f) -{ - int r,n = ftell(f); - start_file(f); - r = tga_test(); - fseek(f,n,SEEK_SET); - return r; -} -#endif - -int stbi_tga_test_memory (stbi_uc const *buffer, int len) -{ - start_mem(buffer, len); - return tga_test(); -} - -static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp) -{ - // read in the TGA header stuff - int tga_offset = get8u(); - int tga_indexed = get8u(); - int tga_image_type = get8u(); - int tga_is_RLE = 0; - int tga_palette_start = get16le(); - int tga_palette_len = get16le(); - int tga_palette_bits = get8u(); - int tga_x_origin = get16le(); - int tga_y_origin = get16le(); - int tga_width = get16le(); - int tga_height = get16le(); - int tga_bits_per_pixel = get8u(); - int tga_inverted = get8u(); - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4]; - unsigned char trans_data[4]; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - // do a tiny bit of precessing - if( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - /* int tga_alpha_bits = tga_inverted & 15; */ - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // error check - if( //(tga_indexed) || - (tga_width < 1) || (tga_height < 1) || - (tga_image_type < 1) || (tga_image_type > 3) || - ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && - (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) - ) - { - return NULL; - } - - // If I'm paletted, then I'll use the number of bits from the palette - if( tga_indexed ) - { - tga_bits_per_pixel = tga_palette_bits; - } - - // tga info - *x = tga_width; - *y = tga_height; - if( (req_comp < 1) || (req_comp > 4) ) - { - // just use whatever the file was - req_comp = tga_bits_per_pixel / 8; - *comp = req_comp; - } else - { - // force a new number of components - *comp = tga_bits_per_pixel/8; - } - tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); - - // skip to the data's starting position (offset usually = 0) - skip( tga_offset ); - // do I need to load a palette? - if( tga_indexed ) - { - // any data to skip? (offset usually = 0) - skip( tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); - getn( tga_palette, tga_palette_len * tga_palette_bits / 8 ); - } - // load the data - for( i = 0; i < tga_width * tga_height; ++i ) - { - // if I'm in RLE mode, do I need to get a RLE chunk? - if( tga_is_RLE ) - { - if( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = get8u(); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if( read_next_pixel ) - { - // load however much data we did have - if( tga_indexed ) - { - // read in 1 byte, then perform the lookup - int pal_idx = get8u(); - if( pal_idx >= tga_palette_len ) - { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_bits_per_pixel / 8; - for( j = 0; j*8 < tga_bits_per_pixel; ++j ) - { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else - { - // read in the data raw - for( j = 0; j*8 < tga_bits_per_pixel; ++j ) - { - raw_data[j] = get8u(); - } - } - // convert raw to the intermediate format - switch( tga_bits_per_pixel ) - { - case 8: - // Luminous => RGBA - trans_data[0] = raw_data[0]; - trans_data[1] = raw_data[0]; - trans_data[2] = raw_data[0]; - trans_data[3] = 255; - break; - case 16: - // Luminous,Alpha => RGBA - trans_data[0] = raw_data[0]; - trans_data[1] = raw_data[0]; - trans_data[2] = raw_data[0]; - trans_data[3] = raw_data[1]; - break; - case 24: - // BGR => RGBA - trans_data[0] = raw_data[2]; - trans_data[1] = raw_data[1]; - trans_data[2] = raw_data[0]; - trans_data[3] = 255; - break; - case 32: - // BGRA => RGBA - trans_data[0] = raw_data[2]; - trans_data[1] = raw_data[1]; - trans_data[2] = raw_data[0]; - trans_data[3] = raw_data[3]; - break; - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - // convert to final format - switch( req_comp ) - { - case 1: - // RGBA => Luminance - tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); - break; - case 2: - // RGBA => Luminance,Alpha - tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); - tga_data[i*req_comp+1] = trans_data[3]; - break; - case 3: - // RGBA => RGB - tga_data[i*req_comp+0] = trans_data[0]; - tga_data[i*req_comp+1] = trans_data[1]; - tga_data[i*req_comp+2] = trans_data[2]; - break; - case 4: - // RGBA => RGBA - tga_data[i*req_comp+0] = trans_data[0]; - tga_data[i*req_comp+1] = trans_data[1]; - tga_data[i*req_comp+2] = trans_data[2]; - tga_data[i*req_comp+3] = trans_data[3]; - break; - } - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if( tga_inverted ) - { - for( j = 0; j*2 < tga_height; ++j ) - { - int index1 = j * tga_width * req_comp; - int index2 = (tga_height - 1 - j) * tga_width * req_comp; - for( i = tga_width * req_comp; i > 0; --i ) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if( tga_palette != NULL ) - { - free( tga_palette ); - } - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - // OK, done - return tga_data; -} - -#ifndef STBI_NO_STDIO -stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *data; - FILE *f = fopen(filename, "rb"); - if (!f) return NULL; - data = stbi_tga_load_from_file(f, x,y,comp,req_comp); - fclose(f); - return data; -} - -stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) -{ - start_file(f); - return tga_load(x,y,comp,req_comp); -} -#endif - -stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - start_mem(buffer, len); - return tga_load(x,y,comp,req_comp); -} - - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicholas Schulz, tweaked by STB - -static int psd_test(void) -{ - if (get32() != 0x38425053) return 0; // "8BPS" - else return 1; -} - -#ifndef STBI_NO_STDIO -int stbi_psd_test_file(FILE *f) -{ - int r,n = ftell(f); - start_file(f); - r = psd_test(); - fseek(f,n,SEEK_SET); - return r; -} -#endif - -int stbi_psd_test_memory(stbi_uc const *buffer, int len) -{ - start_mem(buffer, len); - return psd_test(); -} - -static stbi_uc *psd_load(int *x, int *y, int *comp, int req_comp) -{ - int pixelCount; - int channelCount, compression; - int channel, i, count, len; - int w,h; - - // Check identifier - if (get32() != 0x38425053) // "8BPS" - return epuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (get16() != 1) - return epuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - skip( 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = get16(); - if (channelCount < 0 || channelCount > 16) - return epuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = get32(); - w = get32(); - - // Make sure the depth is 8 bits. - if (get16() != 8) - return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (get16() != 3) - return epuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - skip(get32() ); - - // Skip the image resources. (resolution, pen tool paths, etc) - skip( get32() ); - - // Skip the reserved data. - skip( get32() ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = get16(); - if (compression > 1) - return epuc("unknown compression type", "PSD has an unknown compression format"); - - // Create the destination image. - out = (stbi_uc *) malloc(4 * w*h); - if (!out) return epuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, - // which we're going to just skip. - skip( h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - uint8 *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; - } else { - // Read the RLE data. - count = 0; - while (count < pixelCount) { - len = get8(); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - count += len; - while (len) { - *p = get8(); - p += 4; - len--; - } - } else if (len > 128) { - uint32 val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len ^= 0x0FF; - len += 2; - val = get8(); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - uint8 *p; - - p = out + channel; - if (channel > channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; - } else { - // Read the data. - count = 0; - for (i = 0; i < pixelCount; i++) - *p = get8(), p += 4; - } - } - } - - if (req_comp && req_comp != 4) { - img_x = w; - img_y = h; - out = convert_format(out, 4, req_comp); - if (out == NULL) return out; // convert_format frees input on failure - } - - if (comp) *comp = channelCount; - *y = h; - *x = w; - - return out; -} - -#ifndef STBI_NO_STDIO -stbi_uc *stbi_psd_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *data; - FILE *f = fopen(filename, "rb"); - if (!f) return NULL; - data = stbi_psd_load_from_file(f, x,y,comp,req_comp); - fclose(f); - return data; -} - -stbi_uc *stbi_psd_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - start_file(f); - return psd_load(x,y,comp,req_comp); -} -#endif - -stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - start_mem(buffer, len); - return psd_load(x,y,comp,req_comp); -} - - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int hdr_test(void) -{ - char *signature = "#?RADIANCE\n"; - int i; - for (i=0; signature[i]; ++i) - if (get8() != signature[i]) - return 0; - return 1; -} - -int stbi_hdr_test_memory(stbi_uc const *buffer, int len) -{ - start_mem(buffer, len); - return hdr_test(); -} - -#ifndef STBI_NO_STDIO -int stbi_hdr_test_file(FILE *f) -{ - int r,n = ftell(f); - start_file(f); - r = hdr_test(); - fseek(f,n,SEEK_SET); - return r; -} -#endif - -#define HDR_BUFLEN 1024 -static char *hdr_gettoken(char *buffer) -{ - int len=0; - char *s = buffer, c = '\0'; - - c = get8(); - - while (!at_eof() && c != '\n') { - buffer[len++] = c; - if (len == HDR_BUFLEN-1) { - // flush to end of line - while (!at_eof() && get8() != '\n') - ; - break; - } - c = get8(); - } - - buffer[len] = 0; - return buffer; -} - -static void hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - - -static float *hdr_load(int *x, int *y, int *comp, int req_comp) -{ - char buffer[HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - - - // Check identifier - if (strcmp(hdr_gettoken(buffer), "#?RADIANCE") != 0) - return epf("not HDR", "Corrupt HDR image"); - - // Parse header - while(1) { - token = hdr_gettoken(buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return epf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = hdr_gettoken(buffer); - if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = strtol(token, NULL, 10); - - *x = width; - *y = height; - - *comp = 3; - if (req_comp == 0) req_comp = 3; - - // Read data - hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); - - // Load image data - // image data is stored as some number of sca - if( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - getn(rgbe, 4); - hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = get8(); - c2 = get8(); - len = get8(); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4] = { c1,c2,len, get8() }; - hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - free(scanline); - goto main_decode_loop; // yes, this is fucking insane; blame the fucking insane format - } - len <<= 8; - len |= get8(); - if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); - - for (k = 0; k < 4; ++k) { - i = 0; - while (i < width) { - count = get8(); - if (count > 128) { - // Run - value = get8(); - count -= 128; - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = get8(); - } - } - } - for (i=0; i < width; ++i) - hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - free(scanline); - } - - return hdr_data; -} - -#ifndef STBI_NO_STDIO -float *stbi_hdr_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - start_file(f); - return hdr_load(x,y,comp,req_comp); -} -#endif - -float *stbi_hdr_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - start_mem(buffer, len); - return hdr_load(x,y,comp,req_comp); -} - -#endif // STBI_NO_HDR - -/////////////////////// write image /////////////////////// - -#ifndef STBI_NO_WRITE - -static void write8(FILE *f, int x) { uint8 z = (uint8) x; fwrite(&z,1,1,f); } - -static void writefv(FILE *f, char *fmt, va_list v) -{ - while (*fmt) { - switch (*fmt++) { - case ' ': break; - case '1': { uint8 x = va_arg(v, int); write8(f,x); break; } - case '2': { int16 x = va_arg(v, int); write8(f,x); write8(f,x>>8); break; } - case '4': { int32 x = va_arg(v, int); write8(f,x); write8(f,x>>8); write8(f,x>>16); write8(f,x>>24); break; } - default: - assert(0); - va_end(v); - return; - } - } -} - -static void writef(FILE *f, char *fmt, ...) -{ - va_list v; - va_start(v, fmt); - writefv(f,fmt,v); - va_end(v); -} - -static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad) -{ - uint8 bg[3] = { 255, 0, 255}, px[3]; - uint32 zero = 0; - int i,j,k, j_end; - - if (vdir < 0) - j_end = -1, j = y-1; - else - j_end = y, j = 0; - - for (; j != j_end; j += vdir) { - for (i=0; i < x; ++i) { - uint8 *d = (uint8 *) data + (j*x+i)*comp; - if (write_alpha < 0) - fwrite(&d[comp-1], 1, 1, f); - switch (comp) { - case 1: - case 2: writef(f, "111", d[0],d[0],d[0]); - break; - case 4: - if (!write_alpha) { - for (k=0; k < 3; ++k) - px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255; - writef(f, "111", px[1-rgb_dir],px[1],px[1+rgb_dir]); - break; - } - /* FALLTHROUGH */ - case 3: - writef(f, "111", d[1-rgb_dir],d[1],d[1+rgb_dir]); - break; - } - if (write_alpha > 0) - fwrite(&d[comp-1], 1, 1, f); - } - fwrite(&zero,scanline_pad,1,f); - } -} - -static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, char *fmt, ...) -{ - FILE *f = fopen(filename, "wb"); - if (f) { - va_list v; - va_start(v, fmt); - writefv(f, fmt, v); - va_end(v); - write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad); - fclose(f); - } - return f != NULL; -} - -int stbi_write_bmp(char const *filename, int x, int y, int comp, void *data) -{ - int pad = (-x*3) & 3; - return outfile(filename,-1,-1,x,y,comp,data,0,pad, - "11 4 22 4" "4 44 22 444444", - 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header - 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header -} - -int stbi_write_tga(char const *filename, int x, int y, int comp, void *data) -{ - int has_alpha = !(comp & 1); - return outfile(filename, -1,-1, x, y, comp, data, has_alpha, 0, - "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha); -} - -// any other image formats that do interleaved rgb data? -// PNG: requires adler32,crc32 -- significant amount of code -// PSD: no, channels output separately -// TIFF: no, stripwise-interleaved... i think - -#endif // STBI_NO_WRITE +/* stbi-1.12 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive, no oddball channel decimations) + PNG non-interlaced + BMP non-1bpp, non-RLE + TGA (not sure what subset, if a subset) + PSD (composited view only, no extra channels) + HDR (radiance rgbE format) + writes BMP,TGA (define STBI_NO_WRITE to remove code) + decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code) + supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + TODO: + stbi_info_* + + history: + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less + than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant +*/ + + +//// begin header file //////////////////////////////////////////////////// +// +// Limitations: +// - no progressive/interlaced support (jpeg, png) +// - 8-bit samples only (jpeg, png) +// - not threadsafe +// - channel subsampling of at most 2 in each dimension (jpeg) +// - no delayed line count (jpeg) -- IJG doesn't support either +// +// Basic usage (see HDR discussion below): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to easily see if it's opaque. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG and BMP images are automatically depalettized. +// +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); + + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp +#endif + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4, +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +// WRITING API + +#if !defined(STBI_NO_WRITE) && !defined(STBI_NO_STDIO) +// write a BMP/TGA file given tightly packed 'comp' channels (no padding, nor bmp-stride-padding) +// (you must include the appropriate extension in the filename). +// returns TRUE on success, FALSE if couldn't open file, error writing file +extern int stbi_write_bmp (char const *filename, int x, int y, int comp, void *data); +extern int stbi_write_tga (char const *filename, int x, int y, int comp, void *data); +#endif + +// PRIMARY API - works on images of any type + +// load image by filename, open file, or memory buffer +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image + +#ifndef STBI_NO_HDR +#ifndef STBI_NO_STDIO +extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); +extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif +extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +extern void stbi_hdr_to_ldr_gamma(float gamma); +extern void stbi_hdr_to_ldr_scale(float scale); + +extern void stbi_ldr_to_hdr_gamma(float gamma); +extern void stbi_ldr_to_hdr_scale(float scale); + +#endif // STBI_NO_HDR + +// get a VERY brief reason for failure +extern char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +// TYPE-SPECIFIC ACCESS + +// is it a jpeg? +extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern int stbi_jpeg_test_file (FILE *f); +extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + +extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + +extern int stbi_jpeg_dc_only; // only decode DC component + +// is it a png? +extern int stbi_png_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_png_test_file (FILE *f); +extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + +// is it a bmp? +extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_bmp_test_file (FILE *f); +extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a tga? +extern int stbi_tga_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_tga_test_file (FILE *f); +extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a psd? +extern int stbi_psd_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_psd_test_file (FILE *f); +extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it an hdr? +extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len); + +extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_hdr_test_file (FILE *f); +extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// define new loaders +typedef struct +{ + int (*test_memory)(stbi_uc const *buffer, int len); + stbi_uc * (*load_from_memory)(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + #ifndef STBI_NO_STDIO + int (*test_file)(FILE *f); + stbi_uc * (*load_from_file)(FILE *f, int *x, int *y, int *comp, int req_comp); + #endif +} stbi_loader; + +// register a loader by filling out the above structure (you must defined ALL functions) +// returns 1 if added or already added, 0 if not added (too many loaders) +extern int stbi_register_loader(stbi_loader *loader); + +// define faster low-level operations (typically SIMD support) +#if STBI_SIMD +typedef void (*stbi_idct_8x8)(uint8 *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(uint8 *output, uint8 const *y, uint8 const *cb, uint8 const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +#include + +#if STBI_SIMD +#include +#endif + +#ifndef _MSC_VER +#define __forceinline +#endif + + +// implementation: +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef unsigned int uint; + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(uint32)==4]; + +#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) +#define STBI_NO_WRITE +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Generic API that works on all image types +// + +static char *failure_reason; + +char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(char *str) +{ + failure_reason = str; + return 0; +} + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#define MAX_LOADERS 32 +stbi_loader *loaders[MAX_LOADERS]; +static int max_loaders = 0; + +int stbi_register_loader(stbi_loader *loader) +{ + int i; + for (i=0; i < MAX_LOADERS; ++i) { + // already present? + if (loaders[i] == loader) + return 1; + // end of the list? + if (loaders[i] == NULL) { + loaders[i] = loader; + max_loaders = i+1; + return 1; + } + } + // no room for it + return 0; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + int i; + if (stbi_jpeg_test_file(f)) + return stbi_jpeg_load_from_file(f,x,y,comp,req_comp); + if (stbi_png_test_file(f)) + return stbi_png_load_from_file(f,x,y,comp,req_comp); + if (stbi_bmp_test_file(f)) + return stbi_bmp_load_from_file(f,x,y,comp,req_comp); + if (stbi_psd_test_file(f)) + return stbi_psd_load_from_file(f,x,y,comp,req_comp); + #ifndef STBI_NO_HDR + if (stbi_hdr_test_file(f)) { + float *hdr = stbi_hdr_load_from_file(f, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + for (i=0; i < max_loaders; ++i) + if (loaders[i]->test_file(f)) + return loaders[i]->load_from_file(f,x,y,comp,req_comp); + // test tga last because it's a crappy test! + if (stbi_tga_test_file(f)) + return stbi_tga_load_from_file(f,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + int i; + if (stbi_jpeg_test_memory(buffer,len)) + return stbi_jpeg_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_png_test_memory(buffer,len)) + return stbi_png_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_bmp_test_memory(buffer,len)) + return stbi_bmp_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_psd_test_memory(buffer,len)) + return stbi_psd_load_from_memory(buffer,len,x,y,comp,req_comp); + #ifndef STBI_NO_HDR + if (stbi_hdr_test_memory(buffer, len)) { + float *hdr = stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + for (i=0; i < max_loaders; ++i) + if (loaders[i]->test_memory(buffer,len)) + return loaders[i]->load_from_memory(buffer,len,x,y,comp,req_comp); + // test tga last because it's a crappy test! + if (stbi_tga_test_memory(buffer,len)) + return stbi_tga_load_from_memory(buffer,len,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_HDR + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test_file(f)) + return stbi_hdr_load_from_file(f,x,y,comp,req_comp); + #endif + data = stbi_load_from_file(f, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test_memory(buffer, len)) + return stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); + #endif + data = stbi_load_from_memory(buffer, len, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + return stbi_hdr_test_memory(buffer, len); + #else + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + return stbi_hdr_test_file(f); + #else + return 0; + #endif +} + +#endif + +// @TODO: get image dimensions & components without fully decoding +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +// image width, height, # components +static uint32 img_x, img_y; +static int img_n, img_out_n; + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header, +}; + +// An API for reading either from memory or file. +#ifndef STBI_NO_STDIO +static FILE *img_file; +#endif +static uint8 const *img_buffer, *img_buffer_end; + +#ifndef STBI_NO_STDIO +static void start_file(FILE *f) +{ + img_file = f; +} +#endif + +static void start_mem(uint8 const *buffer, int len) +{ +#ifndef STBI_NO_STDIO + img_file = NULL; +#endif + img_buffer = buffer; + img_buffer_end = buffer+len; +} + +static int get8(void) +{ +#ifndef STBI_NO_STDIO + if (img_file) { + int c = fgetc(img_file); + return c == EOF ? 0 : c; + } +#endif + if (img_buffer < img_buffer_end) + return *img_buffer++; + return 0; +} + +static int at_eof(void) +{ +#ifndef STBI_NO_STDIO + if (img_file) + return feof(img_file); +#endif + return img_buffer >= img_buffer_end; +} + +static uint8 get8u(void) +{ + return (uint8) get8(); +} + +static void skip(int n) +{ +#ifndef STBI_NO_STDIO + if (img_file) + fseek(img_file, n, SEEK_CUR); + else +#endif + img_buffer += n; +} + +static int get16(void) +{ + int z = get8(); + return (z << 8) + get8(); +} + +static uint32 get32(void) +{ + uint32 z = get16(); + return (z << 16) + get16(); +} + +static int get16le(void) +{ + int z = get8(); + return z + (get8() << 8); +} + +static uint32 get32le(void) +{ + uint32 z = get16le(); + return z + (get16le() << 16); +} + +static void getn(stbi_uc *buffer, int n) +{ +#ifndef STBI_NO_STDIO + if (img_file) { + fread(buffer, 1, n, img_file); + return; + } +#endif + memcpy(buffer, img_buffer, n); + img_buffer += n; +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static uint8 compute_y(int r, int g, int b) +{ + return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp) +{ + uint i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * img_x * img_y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < img_y; ++j) { + unsigned char *src = data + j * img_x * img_n ; + unsigned char *dest = good + j * img_x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=0; i < img_x; ++i, src += a, dest += b) + + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch(COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + img_out_n = req_comp; + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +int stbi_jpeg_dc_only; + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + uint16 code[256]; + uint8 values[256]; + uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +static huffman huff_dc[4]; // baseline is 2 tables, extended is 4 +static huffman huff_ac[4]; +static uint8 dequant[4][64]; +#if STBI_SIMD +static __declspec(align(16)) unsigned short dequant2[4][64]; +#endif + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (uint8) i; + } + } + } + return 1; +} + +// sizes for components, interleaved MCUs +static int img_h_max, img_v_max; +static int img_mcu_x, img_mcu_y; +static int img_mcu_w, img_mcu_h; + +// definition of jpeg image component +static struct +{ + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; +} img_comp[4]; + +static uint32 code_buffer; // jpeg entropy-coded buffer +static int code_bits; // number of valid bits +static unsigned char marker; // marker seen while filling entropy buffer +static int nomore; // flag if we saw a marker so must stop + +static void grow_buffer_unsafe(void) +{ + do { + int b = nomore ? 0 : get8(); + if (b == 0xff) { + int c = get8(); + if (c != 0) { + marker = (unsigned char) c; + nomore = 1; + return; + } + } + code_buffer = (code_buffer << 8) | b; + code_bits += 8; + } while (code_bits <= 24); +} + +// (1 << n) - 1 +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +__forceinline static int decode(huffman *h) +{ + unsigned int temp; + int c,k; + + if (code_bits < 16) grow_buffer_unsafe(); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (code_buffer >> (code_bits - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + if (h->size[k] > code_bits) + return -1; + code_bits -= h->size[k]; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + if (code_bits < 16) + temp = (code_buffer << (16 - code_bits)) & 0xffff; + else + temp = (code_buffer >> (code_bits - 16)) & 0xffff; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + code_bits -= 16; + return -1; + } + + if (k > code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((code_buffer >> (code_bits - k)) & bmask[k]) + h->delta[k]; + assert((((code_buffer) >> (code_bits - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + code_bits -= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +__forceinline static int extend_receive(int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (code_bits < n) grow_buffer_unsafe(); + k = (code_buffer >> (code_bits - n)) & bmask[n]; + code_bits -= n; + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(t) : 0; + dc = img_comp[b].dc_pred + diff; + img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +__forceinline static uint8 clamp(int x) +{ + x += 128; + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#if !STBI_SIMD +// .344 seconds on 3*anemones.jpg +static void idct_block(uint8 *out, int out_stride, short data[64], uint8 *dequantize) +{ + int i,val[64],*v=val; + uint8 *o,*dq = dequantize; + short *d = data; + + if (stbi_jpeg_dc_only) { + // ok, I don't really know why this is right, but it seems to be: + int z = 128 + ((d[0] * dq[0]) >> 3); + for (i=0; i < 8; ++i) { + out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = z; + out += out_stride; + } + return; + } + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536; + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} +#else +static void idct_block(uint8 *out, int out_stride, short data[64], unsigned short *dequantize) +{ + int i,val[64],*v=val; + uint8 *o; + unsigned short *dq = dequantize; + short *d = data; + + if (stbi_jpeg_dc_only) { + // ok, I don't really know why this is right, but it seems to be: + int z = 128 + ((d[0] * dq[0]) >> 3); + for (i=0; i < 8; ++i) { + out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = z; + out += out_stride; + } + return; + } + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536; + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +extern void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static uint8 get_marker(void) +{ + uint8 x; + if (marker != MARKER_none) { x = marker; marker = MARKER_none; return x; } + x = get8u(); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +static int scan_n, order[4]; +static int restart_interval, todo; +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(void) +{ + code_bits = 0; + code_buffer = 0; + nomore = 0; + img_comp[0].dc_pred = img_comp[1].dc_pred = img_comp[2].dc_pred = 0; + marker = MARKER_none; + todo = restart_interval ? restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(void) +{ + reset(); + if (scan_n == 1) { + int i,j; + #if STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (img_comp[n].x+7) >> 3; + int h = (img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0; + #if STBI_SIMD + stbi_idct_installed(img_comp[n].data+img_comp[n].w2*j*8+i*8, img_comp[n].w2, data, dequant2[img_comp[n].tq]); + #else + idct_block(img_comp[n].data+img_comp[n].w2*j*8+i*8, img_comp[n].w2, data, dequant[img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--todo <= 0) { + if (code_bits < 24) grow_buffer_unsafe(); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(marker)) return 1; + reset(); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < img_mcu_y; ++j) { + for (i=0; i < img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < scan_n; ++k) { + int n = order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < img_comp[n].v; ++y) { + for (x=0; x < img_comp[n].h; ++x) { + int x2 = (i*img_comp[n].h + x)*8; + int y2 = (j*img_comp[n].v + y)*8; + if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0; + #if STBI_SIMD + stbi_idct_installed(img_comp[n].data+img_comp[n].w2*y2+x2, img_comp[n].w2, data, dequant2[img_comp[n].tq]); + #else + idct_block(img_comp[n].data+img_comp[n].w2*y2+x2, img_comp[n].w2, data, dequant[img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--todo <= 0) { + if (code_bits < 24) grow_buffer_unsafe(); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(marker)) return 1; + reset(); + } + } + } + } + return 1; +} + +static int process_marker(int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16() != 4) return e("bad DRI len","Corrupt JPEG"); + restart_interval = get16(); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16()-2; + while (L > 0) { + int z = get8(); + int p = z >> 4; + int t = z & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + dequant[t][dezigzag[i]] = get8u(); + #if STBI_SIMD + for (i=0; i < 64; ++i) + dequant2[t][i] = dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16()-2; + while (L > 0) { + uint8 *v; + int sizes[16],i,m=0; + int z = get8(); + int tc = z >> 4; + int th = z & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(); + m += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(huff_dc+th, sizes)) return 0; + v = huff_dc[th].values; + } else { + if (!build_huffman(huff_ac+th, sizes)) return 0; + v = huff_ac[th].values; + } + for (i=0; i < m; ++i) + v[i] = get8u(); + L -= m; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(get16()-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(void) +{ + int i; + int Ls = get16(); + scan_n = get8(); + if (scan_n < 1 || scan_n > 4 || scan_n > (int) img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < scan_n; ++i) { + int id = get8(), which; + int z = get8(); + for (which = 0; which < img_n; ++which) + if (img_comp[which].id == id) + break; + if (which == img_n) return 0; + img_comp[which].hd = z >> 4; if (img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + img_comp[which].ha = z & 15; if (img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + order[i] = which; + } + if (get8() != 0) return e("bad SOS","Corrupt JPEG"); + get8(); // should be 63, but might be 0 + if (get8() != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(int scan) +{ + int Lf,p,i,z, h_max=1,v_max=1; + Lf = get16(); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + img_y = get16(); if (img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + img_x = get16(); if (img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + img_n = get8(); + if (img_n != 3 && img_n != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + + if (Lf != 8+3*img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < img_n; ++i) { + img_comp[i].id = get8(); + if (img_comp[i].id != i+1) // JFIF requires + if (img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + z = get8(); + img_comp[i].h = (z >> 4); if (!img_comp[i].h || img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + img_comp[i].v = z & 15; if (!img_comp[i].v || img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + img_comp[i].tq = get8(); if (img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < img_n; ++i) { + if (img_comp[i].h > h_max) h_max = img_comp[i].h; + if (img_comp[i].v > v_max) v_max = img_comp[i].v; + } + + // compute interleaved mcu info + img_h_max = h_max; + img_v_max = v_max; + img_mcu_w = h_max * 8; + img_mcu_h = v_max * 8; + img_mcu_x = (img_x + img_mcu_w-1) / img_mcu_w; + img_mcu_y = (img_y + img_mcu_h-1) / img_mcu_h; + + for (i=0; i < img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + img_comp[i].x = (img_x * img_comp[i].h + h_max-1) / h_max; + img_comp[i].y = (img_y * img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + img_comp[i].w2 = img_mcu_x * img_comp[i].h * 8; + img_comp[i].h2 = img_mcu_y * img_comp[i].v * 8; + img_comp[i].raw_data = malloc(img_comp[i].w2 * img_comp[i].h2+15); + if (img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(img_comp[i].raw_data); + img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + img_comp[i].data = (uint8*) (((int) img_comp[i].raw_data + 15) & ~15); + img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(int scan) +{ + int m; + marker = MARKER_none; // initialize cached marker to empty + m = get_marker(); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(); + while (!SOF(m)) { + if (!process_marker(m)) return 0; + m = get_marker(); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof()) return e("no SOF", "Corrupt JPEG"); + m = get_marker(); + } + } + if (!process_frame_header(scan)) return 0; + return 1; +} + +static int decode_jpeg_image(void) +{ + int m; + restart_interval = 0; + if (!decode_jpeg_header(SCAN_load)) return 0; + m = get_marker(); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header()) return 0; + if (!parse_entropy_coded_data()) return 0; + } else { + if (!process_marker(m)) return 0; + } + m = get_marker(); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); + +#define div4(x) ((uint8) ((x) >> 2)) + +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + uint8 *input = in_near; + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + return out; +} + +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(uint8 *out, uint8 *y, uint8 *pcb, uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (uint8)r; + out[1] = (uint8)g; + out[2] = (uint8)b; + out[3] = 255; + out += step; + } +} + +#if STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(void) +{ + int i; + for (i=0; i < img_n; ++i) { + if (img_comp[i].data) { + free(img_comp[i].raw_data); + img_comp[i].data = NULL; + } + if (img_comp[i].linebuf) { + free(img_comp[i].linebuf); + img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source + if (!decode_jpeg_image()) { cleanup_jpeg(); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : img_n; + + if (img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = img_n; + + // resample and color-convert + { + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + img_comp[k].linebuf = (uint8 *) malloc(img_x + 3); + if (!img_comp[k].linebuf) { cleanup_jpeg(); return epuc("outofmem", "Out of memory"); } + + r->hs = img_h_max / img_comp[k].h; + r->vs = img_v_max / img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * img_x * img_y + 1); + if (!output) { cleanup_jpeg(); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < img_y; ++j) { + uint8 *out = output + n * img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < img_comp[k].y) + r->line1 += img_comp[k].w2; + } + } + if (n >= 3) { + uint8 *y = coutput[0]; + if (img_n == 3) { + #if STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], img_x, n); + #endif + } else + for (i=0; i < img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < img_x; ++i) out[i] = y[i]; + else + for (i=0; i < img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(); + *out_x = img_x; + *out_y = img_y; + if (comp) *comp = img_n; // report original components, not output + return output; + } +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_jpeg_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return load_jpeg_image(x,y,comp,req_comp); +} + +unsigned char *stbi_jpeg_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_jpeg_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return data; +} +#endif + +unsigned char *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer,len); + return load_jpeg_image(x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +int stbi_jpeg_test_file(FILE *f) +{ + int n,r; + n = ftell(f); + start_file(f); + r = decode_jpeg_header(SCAN_type); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_jpeg_test_memory(stbi_uc const *buffer, int len) +{ + start_mem(buffer,len); + return decode_jpeg_header(SCAN_type); +} + +// @TODO: +#ifndef STBI_NO_STDIO +extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + uint16 fast[1 << ZFAST_BITS]; + uint16 firstcode[16]; + int maxcode[17]; + uint16 firstsymbol[16]; + uint8 size[288]; + uint16 value[288]; +} zhuffman; + +__forceinline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +__forceinline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (uint16) code; + z->firstsymbol[i] = (uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (uint8)s; + z->value[c] = (uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +static uint8 *zbuffer, *zbuffer_end; + +__forceinline static int zget8(void) +{ + if (zbuffer >= zbuffer_end) return 0; + return *zbuffer++; +} + +//static uint32 code_buffer; +static int num_bits; + +static void fill_bits(void) +{ + do { + assert(code_buffer < (1U << num_bits)); + code_buffer |= zget8() << num_bits; + num_bits += 8; + } while (num_bits <= 24); +} + +__forceinline static unsigned int zreceive(int n) +{ + unsigned int k; + if (num_bits < n) fill_bits(); + k = code_buffer & ((1 << n) - 1); + code_buffer >>= n; + num_bits -= n; + return k; +} + +__forceinline static int zhuffman_decode(zhuffman *z) +{ + int b,s,k; + if (num_bits < 16) fill_bits(); + b = z->fast[code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + code_buffer >>= s; + num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + code_buffer >>= s; + num_bits -= s; + return z->value[b]; +} + +static char *zout; +static char *zout_start; +static char *zout_end; +static int z_expandable; + +static int expand(int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (zout - zout_start); + limit = (int) (zout_end - zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + zout_start = q; + zout = q + cur; + zout_end = q + limit; + return 1; +} + +static zhuffman z_length, z_distance; + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(void) +{ + for(;;) { + int z = zhuffman_decode(&z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= zout_end) if (!expand(1)) return 0; + *zout++ = (char) z; + } else { + uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(length_extra[z]); + z = zhuffman_decode(&z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(dist_extra[z]); + if (zout - zout_start < dist) return e("bad dist","Corrupt PNG"); + if (zout + len > zout_end) if (!expand(len)) return 0; + p = (uint8 *) (zout - dist); + while (len--) + *zout++ = *p++; + } + } +} + +static int compute_huffman_codes(void) +{ + static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static zhuffman z_codelength; // static just to save stack space + uint8 lencodes[286+32+137];//padding for maximum single op + uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(5) + 257; + int hdist = zreceive(5) + 1; + int hclen = zreceive(4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(3); + codelength_sizes[length_dezigzag[i]] = (uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(&z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (uint8) c; + else if (c == 16) { + c = zreceive(2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(void) +{ + uint8 header[4]; + int len,nlen,k; + if (num_bits & 7) + zreceive(num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (num_bits > 0) { + header[k++] = (uint8) (code_buffer & 255); // wtf this warns? + code_buffer >>= 8; + num_bits -= 8; + } + assert(num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (uint8) zget8(); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (zbuffer + len > zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (zout + len > zout_end) + if (!expand(len)) return 0; + memcpy(zout, zbuffer, len); + zbuffer += len; + zout += len; + return 1; +} + +static int parse_zlib_header(void) +{ + int cmf = zget8(); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = zget8(); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +static int parse_zlib(int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header()) return 0; + num_bits = 0; + code_buffer = 0; + do { + final = zreceive(1); + type = zreceive(2); + if (type == 0) { + if (!parse_uncompressed_block()) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_length[0]) init_defaults(); + if (!zbuild_huffman(&z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes()) return 0; + } + if (!parse_huffman_block()) return 0; + } + } while (!final); + return 1; +} + +static int do_zlib(char *obuf, int olen, int exp, int parse_header) +{ + zout_start = obuf; + zout = obuf; + zout_end = obuf + olen; + z_expandable = exp; + + return parse_zlib(parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen) +{ + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + if (do_zlib(p, initial_size, 1, 1)) { + *outlen = (int) (zout - zout_start); + return zout_start; + } else { + free(zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + zbuffer = (uint8 *) buffer; + zbuffer_end = (uint8 *) buffer+len; + return stbi_zlib_decode_malloc_guesssize(16384, outlen); +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuffer = (uint8 *) ibuffer; + zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(obuffer, olen, 0, 1)) + return (int) (zout - zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + zbuffer = (uint8 *) buffer; + zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(p, 16384, 1, 0)) { + *outlen = (int) (zout - zout_start); + return zout_start; + } else { + free(zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuffer = (uint8 *) ibuffer; + zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(obuffer, olen, 0, 0)) + return (int) (zout - zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + uint32 length; + uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(void) +{ + chunk c; + c.length = get32(); + c.type = get32(); + return c; +} + +static int check_png_header(void) +{ + static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8() != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +static uint8 *idata, *expanded, *out; + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first, +}; + +static uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image(uint8 *raw, uint32 raw_len, int out_n) +{ + uint32 i,j,stride = img_x*out_n; + int k; + assert(out_n == img_n || out_n == img_n+1); + out = (uint8 *) malloc(img_x * img_y * out_n); + if (!out) return e("outofmem", "Out of memory"); + if (raw_len != (img_n * img_x + 1) * img_y) return e("not enough pixels","Corrupt PNG"); + for (j=0; j < img_y; ++j) { + uint8 *cur = out + stride*j; + uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch(filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=1; i < img_x; ++i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch(filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=1; i < img_x; ++i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch(filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int compute_transparency(uint8 tc[3], int out_n) +{ + uint32 i, pixel_count = img_x * img_y; + uint8 *p = out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + p = out; + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(uint8 *palette, int len, int pal_img_n) +{ + uint32 i, pixel_count = img_x * img_y; + uint8 *p, *temp_out, *orig = out; + + p = (uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(out); + out = temp_out; + return 1; +} + +static int parse_png_file(int scan, int req_comp) +{ + uint8 palette[1024], pal_img_n=0; + uint8 has_trans=0, tc[3]; + uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k; + + if (!check_png_header()) return 0; + + if (scan == SCAN_type) return 1; + + for(;;first=0) { + chunk c = get_chunk_header(); + if (first && c.type != PNG_TYPE('I','H','D','R')) + return e("first not IHDR","Corrupt PNG"); + switch (c.type) { + case PNG_TYPE('I','H','D','R'): { + int depth,color,interlace,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + img_x = get32(); if (img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + img_y = get32(); if (img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(); if (interlace) return e("interlaced","PNG not supported: interlaced mode"); + if (!img_x || !img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + img_n = 1; + if ((1 << 30) / img_x / 4 < img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(); + palette[i*4+1] = get8u(); + palette[i*4+2] = get8u(); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(); + } else { + if (!(img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < img_n; ++k) + tc[k] = (uint8) get16(); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (uint8 *) realloc(idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + idata = p; + } + #ifndef STBI_NO_STDIO + if (img_file) + { + if (fread(idata+ioff,1,c.length,img_file) != c.length) return e("outofdata","Corrupt PNG"); + } + else + #endif + { + memcpy(idata+ioff, img_buffer, c.length); + img_buffer += c.length; + } + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + uint32 raw_len; + if (scan != SCAN_load) return 1; + if (idata == NULL) return e("no IDAT","Corrupt PNG"); + expanded = (uint8 *) stbi_zlib_decode_malloc((char *) idata, ioff, (int *) &raw_len); + if (expanded == NULL) return 0; // zlib should set error + free(idata); idata = NULL; + if ((req_comp == img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + img_out_n = img_n+1; + else + img_out_n = img_n; + if (!create_png_image(expanded, raw_len, img_out_n)) return 0; + if (has_trans) + if (!compute_transparency(tc, img_out_n)) return 0; + if (pal_img_n) { + // pal_img_n == 3 or 4 + img_n = pal_img_n; // record the actual colors we had + img_out_n = pal_img_n; + if (req_comp >= 3) img_out_n = req_comp; + if (!expand_palette(palette, pal_len, img_out_n)) + return 0; + } + free(expanded); expanded = NULL; + return 1; + } + + default: + // if critical, fail + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (uint8) (c.type >> 24); + invalid_chunk[1] = (uint8) (c.type >> 16); + invalid_chunk[2] = (uint8) (c.type >> 8); + invalid_chunk[3] = (uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(c.length); + break; + } + // end of chunk, read and skip CRC + get8(); get8(); get8(); get8(); + } +} + +static unsigned char *do_png(int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(SCAN_load, req_comp)) { + result = out; + out = NULL; + if (req_comp && req_comp != img_out_n) { + result = convert_format(result, img_out_n, req_comp); + if (result == NULL) return result; + } + *x = img_x; + *y = img_y; + if (n) *n = img_n; + } + free(out); out = NULL; + free(expanded); expanded = NULL; + free(idata); idata = NULL; + + return result; +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_png_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return do_png(x,y,comp,req_comp); +} + +unsigned char *stbi_png_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_png_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return data; +} +#endif + +unsigned char *stbi_png_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer,len); + return do_png(x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +int stbi_png_test_file(FILE *f) +{ + int n,r; + n = ftell(f); + start_file(f); + r = parse_png_file(SCAN_type,STBI_default); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_png_test_memory(stbi_uc const *buffer, int len) +{ + start_mem(buffer, len); + return parse_png_file(SCAN_type,STBI_default); +} + +// TODO: load header from png +#ifndef STBI_NO_STDIO +extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +// Microsoft/Windows BMP image + +static int bmp_test(void) +{ + int sz; + if (get8() != 'B') return 0; + if (get8() != 'M') return 0; + get32le(); // discard filesize + get16le(); // discard reserved + get16le(); // discard reserved + get32le(); // discard data offset + sz = get32le(); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +#ifndef STBI_NO_STDIO +int stbi_bmp_test_file (FILE *f) +{ + int r,n = ftell(f); + start_file(f); + r = bmp_test(); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_bmp_test_memory (stbi_uc const *buffer, int len) +{ + start_mem(buffer, len); + return bmp_test(); +} + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp) +{ + unsigned int mr=0,mg=0,mb=0,ma=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8() != 'B' || get8() != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(); // discard filesize + get16le(); // discard reserved + get16le(); // discard reserved + offset = get32le(); + hsz = get32le(); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + failure_reason = "bad BMP"; + if (hsz == 12) { + img_x = get16le(); + img_y = get16le(); + } else { + img_x = get32le(); + img_y = get32le(); + } + if (get16le() != 1) return 0; + bpp = get16le(); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) img_y) > 0; + img_y = abs((int) img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(); // discard sizeof + get32le(); // discard hres + get32le(); // discard vres + get32le(); // discard colorsused + get32le(); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(); + get32le(); + get32le(); + get32le(); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xff << 16; + mg = 0xff << 8; + mb = 0xff << 0; + } else { + mr = 31 << 10; + mg = 31 << 5; + mb = 31 << 0; + } + } else if (compress == 3) { + mr = get32le(); + mg = get32le(); + mb = get32le(); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return NULL; + } + } else + return NULL; + } + } else { + assert(hsz == 108); + mr = get32le(); + mg = get32le(); + mb = get32le(); + ma = get32le(); + get32le(); // discard color space + for (i=0; i < 12; ++i) + get32le(); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * img_x * img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) return epuc("invalid", "Corrupt BMP"); + for (i=0; i < psize; ++i) { + pal[i][2] = get8(); + pal[i][1] = get8(); + pal[i][0] = get8(); + if (hsz != 12) get8(); + pal[i][3] = 255; + } + skip(offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (img_x + 1) >> 1; + else if (bpp == 8) width = img_x; + else return epuc("bad bpp", "Corrupt BMP"); + pad = (-width)&3; + for (j=0; j < (int) img_y; ++j) { + for (i=0; i < (int) img_x; i += 2) { + int v=get8(),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) img_x) break; + v = (bpp == 8) ? get8() : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(offset - 14 - hsz); + if (bpp == 24) width = 3 * img_x; + else if (bpp == 16) width = 2*img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0xff000000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) return epuc("bad masks", "Corrupt BMP"); + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mr); + bshift = high_bit(mb)-7; bcount = bitcount(mr); + ashift = high_bit(ma)-7; acount = bitcount(mr); + } + for (j=0; j < (int) img_y; ++j) { + if (easy) { + for (i=0; i < (int) img_x; ++i) { + int a; + out[z+2] = get8(); + out[z+1] = get8(); + out[z+0] = get8(); + z += 3; + a = (easy == 2 ? get8() : 255); + if (target == 4) out[z++] = a; + } + } else { + for (i=0; i < (int) img_x; ++i) { + uint32 v = (bpp == 16 ? get16le() : get32le()); + int a; + out[z++] = shiftsigned(v & mr, rshift, rcount); + out[z++] = shiftsigned(v & mg, gshift, gcount); + out[z++] = shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = a; + } + } + skip(pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) img_y>>1; ++j) { + stbi_uc *p1 = out + j *img_x*target; + stbi_uc *p2 = out + (img_y-1-j)*img_x*target; + for (i=0; i < (int) img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = img_x; + *y = img_y; + if (comp) *comp = target; + return out; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_bmp_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return bmp_load(x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer, len); + return bmp_load(x,y,comp,req_comp); +} + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_test(void) +{ + int sz; + get8u(); // discard Offset + sz = get8u(); // color type + if( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(); // image type + if( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(); // discard palette start + get16(); // discard palette length + get8(); // discard bits per palette color entry + get16(); // discard x origin + get16(); // discard y origin + if( get16() < 1 ) return 0; // test width + if( get16() < 1 ) return 0; // test height + sz = get8(); // bits per pixel + if( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +#ifndef STBI_NO_STDIO +int stbi_tga_test_file (FILE *f) +{ + int r,n = ftell(f); + start_file(f); + r = tga_test(); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_tga_test_memory (stbi_uc const *buffer, int len) +{ + start_mem(buffer, len); + return tga_test(); +} + +static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(); + int tga_indexed = get8u(); + int tga_image_type = get8u(); + int tga_is_RLE = 0; + int tga_palette_start = get16le(); + int tga_palette_len = get16le(); + int tga_palette_bits = get8u(); + int tga_x_origin = get16le(); + int tga_y_origin = get16le(); + int tga_width = get16le(); + int tga_height = get16le(); + int tga_bits_per_pixel = get8u(); + int tga_inverted = get8u(); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + unsigned char trans_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + // do a tiny bit of precessing + if( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; + } + + // If I'm paletted, then I'll use the number of bits from the palette + if( tga_indexed ) + { + tga_bits_per_pixel = tga_palette_bits; + } + + // tga info + *x = tga_width; + *y = tga_height; + if( (req_comp < 1) || (req_comp > 4) ) + { + // just use whatever the file was + req_comp = tga_bits_per_pixel / 8; + *comp = req_comp; + } else + { + // force a new number of components + *comp = tga_bits_per_pixel/8; + } + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + + // skip to the data's starting position (offset usually = 0) + skip( tga_offset ); + // do I need to load a palette? + if( tga_indexed ) + { + // any data to skip? (offset usually = 0) + skip( tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + getn( tga_palette, tga_palette_len * tga_palette_bits / 8 ); + } + // load the data + for( i = 0; i < tga_width * tga_height; ++i ) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if( tga_is_RLE ) + { + if( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if( read_next_pixel ) + { + // load however much data we did have + if( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(); + if( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for( j = 0; j*8 < tga_bits_per_pixel; ++j ) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for( j = 0; j*8 < tga_bits_per_pixel; ++j ) + { + raw_data[j] = get8u(); + } + } + // convert raw to the intermediate format + switch( tga_bits_per_pixel ) + { + case 8: + // Luminous => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 16: + // Luminous,Alpha => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[1]; + break; + case 24: + // BGR => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 32: + // BGRA => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[3]; + break; + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + // convert to final format + switch( req_comp ) + { + case 1: + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + break; + case 2: + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + tga_data[i*req_comp+1] = trans_data[3]; + break; + case 3: + // RGBA => RGB + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + break; + case 4: + // RGBA => RGBA + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + tga_data[i*req_comp+3] = trans_data[3]; + break; + } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if( tga_inverted ) + { + for( j = 0; j*2 < tga_height; ++j ) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for( i = tga_width * req_comp; i > 0; --i ) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if( tga_palette != NULL ) + { + free( tga_palette ); + } + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_tga_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return tga_load(x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer, len); + return tga_load(x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicholas Schulz, tweaked by STB + +static int psd_test(void) +{ + if (get32() != 0x38425053) return 0; // "8BPS" + else return 1; +} + +#ifndef STBI_NO_STDIO +int stbi_psd_test_file(FILE *f) +{ + int r,n = ftell(f); + start_file(f); + r = psd_test(); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_psd_test_memory(stbi_uc const *buffer, int len) +{ + start_mem(buffer, len); + return psd_test(); +} + +static stbi_uc *psd_load(int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + + // Check identifier + if (get32() != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16() != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip( 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(); + w = get32(); + + // Make sure the depth is 8 bits. + if (get16() != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16() != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(get32() ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip( get32() ); + + // Skip the reserved data. + skip( get32() ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(); + if (compression > 1) + return epuc("unknown compression type", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip( h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8(); + p += 4; + len--; + } + } else if (len > 128) { + uint32 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8(); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + count = 0; + for (i = 0; i < pixelCount; i++) + *p = get8(), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + img_x = w; + img_y = h; + out = convert_format(out, 4, req_comp); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_psd_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_psd_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_psd_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return psd_load(x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer, len); + return psd_load(x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(void) +{ + char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8() != signature[i]) + return 0; + return 1; +} + +int stbi_hdr_test_memory(stbi_uc const *buffer, int len) +{ + start_mem(buffer, len); + return hdr_test(); +} + +#ifndef STBI_NO_STDIO +int stbi_hdr_test_file(FILE *f) +{ + int r,n = ftell(f); + start_file(f); + r = hdr_test(); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(char *buffer) +{ + int len=0; + char *s = buffer, c = '\0'; + + c = get8(); + + while (!at_eof() && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof() && get8() != '\n') + ; + break; + } + c = get8(); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + + +static float *hdr_load(int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + while(1) { + token = hdr_gettoken(buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(); + c2 = get8(); + len = get8(); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4] = { c1,c2,len, get8() }; + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this is fucking insane; blame the fucking insane format + } + len <<= 8; + len |= get8(); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8(); + if (count > 128) { + // Run + value = get8(); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8(); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +#ifndef STBI_NO_STDIO +float *stbi_hdr_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return hdr_load(x,y,comp,req_comp); +} +#endif + +float *stbi_hdr_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer, len); + return hdr_load(x,y,comp,req_comp); +} + +#endif // STBI_NO_HDR + +/////////////////////// write image /////////////////////// + +#ifndef STBI_NO_WRITE + +static void write8(FILE *f, int x) { uint8 z = (uint8) x; fwrite(&z,1,1,f); } + +static void writefv(FILE *f, char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { uint8 x = va_arg(v, int); write8(f,x); break; } + case '2': { int16 x = va_arg(v, int); write8(f,x); write8(f,x>>8); break; } + case '4': { int32 x = va_arg(v, int); write8(f,x); write8(f,x>>8); write8(f,x>>16); write8(f,x>>24); break; } + default: + assert(0); + va_end(v); + return; + } + } +} + +static void writef(FILE *f, char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + writefv(f,fmt,v); + va_end(v); +} + +static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad) +{ + uint8 bg[3] = { 255, 0, 255}, px[3]; + uint32 zero = 0; + int i,j,k, j_end; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + uint8 *d = (uint8 *) data + (j*x+i)*comp; + if (write_alpha < 0) + fwrite(&d[comp-1], 1, 1, f); + switch (comp) { + case 1: + case 2: writef(f, "111", d[0],d[0],d[0]); + break; + case 4: + if (!write_alpha) { + for (k=0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255; + writef(f, "111", px[1-rgb_dir],px[1],px[1+rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + writef(f, "111", d[1-rgb_dir],d[1],d[1+rgb_dir]); + break; + } + if (write_alpha > 0) + fwrite(&d[comp-1], 1, 1, f); + } + fwrite(&zero,scanline_pad,1,f); + } +} + +static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, char *fmt, ...) +{ + FILE *f = fopen(filename, "wb"); + if (f) { + va_list v; + va_start(v, fmt); + writefv(f, fmt, v); + va_end(v); + write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad); + fclose(f); + } + return f != NULL; +} + +int stbi_write_bmp(char const *filename, int x, int y, int comp, void *data) +{ + int pad = (-x*3) & 3; + return outfile(filename,-1,-1,x,y,comp,data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +int stbi_write_tga(char const *filename, int x, int y, int comp, void *data) +{ + int has_alpha = !(comp & 1); + return outfile(filename, -1,-1, x, y, comp, data, has_alpha, 0, + "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha); +} + +// any other image formats that do interleaved rgb data? +// PNG: requires adler32,crc32 -- significant amount of code +// PSD: no, channels output separately +// TIFF: no, stripwise-interleaved... i think + +#endif // STBI_NO_WRITE From 821e622de671ad4e18a55e9dbd33aee004e822df Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 7 Jan 2010 15:58:53 +0000 Subject: [PATCH 54/83] Don't set the GLX_TRANSPARENT_TYPE attribute to choose an FBConfig When Clutter tries to pick an ARGB visual it tried to set the GLX_TRANSPARENT_TYPE attribute of the FBConfig to GLX_TRANSPARENT_RGB. However the code to do this was broken so that it was actually trying to set the non-existant attribute number 0x8008 instead. Mesa silently ignored this so it appeared as if it was working but the Nvidia drivers do not like it. It appears that the TRANSPARENT_TYPE attribute is not neccessary for getting an ARGB visual anyway and instead it is intended to support color-key transparency. Therefore we can just remove it and get all of the FBConfigs. Then if we need an ARGB visual we can just walk the list to look for one with depth == 32. The fbconfig is now stored in a single variable instead of having a separate variable for the rgb and rgba configs because the old code only ever retrieved one of them anyway. --- clutter/glx/clutter-backend-glx.c | 141 +++++++++++------------------- clutter/glx/clutter-backend-glx.h | 5 +- 2 files changed, 55 insertions(+), 91 deletions(-) diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index ce1256e6c..beca4df41 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -354,20 +354,6 @@ clutter_backend_glx_get_features (ClutterBackend *backend) return flags; } -enum -{ - DRAWABLE_TYPE = 0, - RENDER_TYPE = 2, - DOUBLE_BUFFER = 4, - RED_SIZE = 6, - GREEN_SIZE = 8, - BLUE_SIZE = 10, - ALPHA_SIZE = 12, - DEPTH_SIZE = 14, - STENCIL_SIZE = 16, - TRANSPARENT_TYPE = 18 -}; - /* It seems the GLX spec never defined an invalid GLXFBConfig that * we could overload as an indication of error, so we have to return * an explicit boolean status. */ @@ -377,10 +363,9 @@ _clutter_backend_glx_get_fbconfig (ClutterBackendGLX *backend_glx, { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend_glx); GLXFBConfig *configs = NULL; - gboolean retval = FALSE; gboolean use_argb = clutter_x11_has_argb_visuals (); int n_configs, i; - static int attributes[] = { + static const int attributes[] = { GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DOUBLEBUFFER, GL_TRUE, @@ -390,97 +375,77 @@ _clutter_backend_glx_get_fbconfig (ClutterBackendGLX *backend_glx, GLX_ALPHA_SIZE, 1, GLX_DEPTH_SIZE, 1, GLX_STENCIL_SIZE, 1, - GLX_TRANSPARENT_TYPE, GLX_NONE, None }; if (backend_x11->xdpy == None || backend_x11->xscreen == None) return FALSE; - if (backend_glx->found_fbconfig > 0) + /* If we don't already have a cached config then try to get one */ + if (!backend_glx->found_fbconfig) { - if (use_argb && backend_glx->found_fbconfig == 2) - *config = backend_glx->fbconfig_rgba; - else - *config = backend_glx->fbconfig_rgb; + CLUTTER_NOTE (BACKEND, + "Retrieving GL fbconfig, dpy: %p, xscreen; %p (%d)", + backend_x11->xdpy, + backend_x11->xscreen, + backend_x11->xscreen_num); - return TRUE; - } - - if (use_argb) - { - attributes[ALPHA_SIZE] = 8; - attributes[TRANSPARENT_TYPE] = GLX_TRANSPARENT_RGB; - } - - CLUTTER_NOTE (BACKEND, - "Retrieving GL fbconfig, dpy: %p, xscreen; %p (%d)", - backend_x11->xdpy, - backend_x11->xscreen, - backend_x11->xscreen_num); - - configs = glXChooseFBConfig (backend_x11->xdpy, - backend_x11->xscreen_num, - attributes, - &n_configs); - if (!configs) - return FALSE; - - if (!use_argb) - { - *config = configs[0]; - - backend_glx->found_fbconfig = 1; - backend_glx->fbconfig_rgb = configs[0]; - - retval = TRUE; - - goto out; - } - - for (i = 0; i < n_configs; i++) - { - XVisualInfo *vinfo; - - vinfo = glXGetVisualFromFBConfig (backend_x11->xdpy, configs[i]); - if (vinfo == None) - continue; - - if (vinfo->depth == 32 && - (vinfo->red_mask == 0xff0000 && - vinfo->green_mask == 0x00ff00 && - vinfo->blue_mask == 0x0000ff)) + configs = glXChooseFBConfig (backend_x11->xdpy, + backend_x11->xscreen_num, + attributes, + &n_configs); + if (configs) { - CLUTTER_NOTE (BACKEND, "Found GLX visual ARGB [index:%d]", i); + if (use_argb) + { + for (i = 0; i < n_configs; i++) + { + XVisualInfo *vinfo; - *config = configs[i]; + vinfo = glXGetVisualFromFBConfig (backend_x11->xdpy, + configs[i]); + if (vinfo == None) + continue; - backend_glx->found_fbconfig = 2; - backend_glx->fbconfig_rgba = configs[i]; + if (vinfo->depth == 32 && + (vinfo->red_mask == 0xff0000 && + vinfo->green_mask == 0x00ff00 && + vinfo->blue_mask == 0x0000ff)) + { + CLUTTER_NOTE (BACKEND, + "Found GLX visual ARGB [index:%d]", i); - retval = TRUE; + backend_glx->found_fbconfig = TRUE; + backend_glx->fbconfig = configs[i]; - goto out; + goto out; + } + } + + /* If we make it here then we didn't find an RGBA config so + we'll fall back to using an RGB config */ + CLUTTER_NOTE (BACKEND, "ARGB visual requested, but none found"); + } + + if (n_configs >= 1) + { + backend_glx->found_fbconfig = TRUE; + backend_glx->fbconfig = configs[0]; + } + + out: + XFree (configs); } } - /* XXX - we might add a warning here */ - if (use_argb && !backend_glx->found_fbconfig != 2) + if (backend_glx->found_fbconfig) { - CLUTTER_NOTE (BACKEND, "ARGB visual requested, but none found"); + *config = backend_glx->fbconfig; - *config = configs[0]; - - backend_glx->found_fbconfig = 1; - backend_glx->fbconfig_rgb = configs[0]; - - retval = TRUE; + return TRUE; } - -out: - XFree (configs); - - return retval; + else + return FALSE; } static XVisualInfo * diff --git a/clutter/glx/clutter-backend-glx.h b/clutter/glx/clutter-backend-glx.h index e7d6d9836..2bd380813 100644 --- a/clutter/glx/clutter-backend-glx.h +++ b/clutter/glx/clutter-backend-glx.h @@ -63,9 +63,8 @@ struct _ClutterBackendGLX ClutterBackendX11 parent_instance; /* Single context for all wins */ - gint found_fbconfig; - GLXFBConfig fbconfig_rgb; - GLXFBConfig fbconfig_rgba; + gboolean found_fbconfig; + GLXFBConfig fbconfig; GLXContext gl_context; /* Vblank stuff */ From bf4818bd759f612a9cec62c2e072544d54cc104c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 7 Jan 2010 11:08:52 +0000 Subject: [PATCH 55/83] actor: Just emit a relayout cycle warning Currently, ClutterActor detects a relayout cycle (an actor causing a relayout to be queued from within an allocate() function) and aborts after printing out a warning. This might be a little bit too anal retentive, and it currently breaks GTK+ embedding inside clutter-gtk so we should probably relax the behaviour a bit. Now we just emit the warning but we still go ahead with the relayout. --- clutter/clutter-actor.c | 1 - 1 file changed, 1 deletion(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 26c7f5e28..724a2ac49 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -4547,7 +4547,6 @@ clutter_actor_queue_relayout (ClutterActor *self) "not allowed", priv->name ? priv->name : G_OBJECT_TYPE_NAME (self)); - return; } g_signal_emit (self, actor_signals[QUEUE_RELAYOUT], 0); From 9599adbd820db7526d77790d8b611a6b61cb2dc7 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Wed, 6 Jan 2010 00:44:24 +0000 Subject: [PATCH 56/83] doc: Fix media::{get,set}_subtitle_uri() symbol names At first, those symbols were called {get,set}_subtitles_uri() but were renamed to {get,set}_subtitle_uri() without updating the clutter-section.txt file. This fix makes gtk-doc document those symbols again. --- doc/reference/clutter/clutter-sections.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 3c9939377..89bfa4039 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -9,8 +9,8 @@ clutter_media_set_playing clutter_media_get_playing clutter_media_set_progress clutter_media_get_progress -clutter_media_set_subtitles_uri -clutter_media_get_subtitles_uri +clutter_media_set_subtitle_uri +clutter_media_get_subtitle_uri clutter_media_set_audio_volume clutter_media_get_audio_volume clutter_media_get_can_seek From 5157da9fc8340cebdd29989a4ea02ccf607d3c52 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 8 Jan 2010 15:04:56 +0000 Subject: [PATCH 57/83] x11: Switch back to RGB visuals by default Since asking for ARGB by default is still somewhat experimental on X11 and not every toolkit or complex widgets (like WebKit) still do not like dealing with ARGB visuals, we should switch back to RGB by default - now that at least we know it works. For applications (and toolkit integration libraries) that want to enable the ClutterStage:use-alpha property there is a new function: void clutter_x11_set_use_argb_visual (gboolean use_argb); which needs to be called before clutter_init(). The CLUTTER_DISABLE_ARGB_VISUAL environment variable can still be used to force this value off at run-time. --- clutter/glx/clutter-backend-glx.c | 2 +- clutter/x11/clutter-backend-x11.c | 70 +++++++++++++++++++++- clutter/x11/clutter-stage-x11.c | 6 +- clutter/x11/clutter-x11.h | 9 ++- doc/reference/clutter/clutter-sections.txt | 3 + 5 files changed, 83 insertions(+), 7 deletions(-) diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index beca4df41..dfcf9ee32 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -363,7 +363,7 @@ _clutter_backend_glx_get_fbconfig (ClutterBackendGLX *backend_glx, { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend_glx); GLXFBConfig *configs = NULL; - gboolean use_argb = clutter_x11_has_argb_visuals (); + gboolean use_argb = clutter_x11_get_use_argb_visual (); int n_configs, i; static const int attributes[] = { GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index ec9afebea..4eaf08106 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -97,7 +97,7 @@ static ClutterBackendX11 *backend_singleton = NULL; /* various flags corresponding to pre init setup calls */ static gboolean _no_xevent_retrieval = FALSE; static gboolean clutter_enable_xinput = FALSE; -static gboolean clutter_enable_argb = TRUE; +static gboolean clutter_enable_argb = FALSE; static Display *_foreign_dpy = NULL; /* options */ @@ -988,8 +988,52 @@ clutter_x11_has_composite_extension (void) return have_composite; } +/** + * clutter_x11_set_use_argb_visual: + * @use_argb: %TRUE if ARGB visuals should be requested by default + * + * Sets whether the Clutter X11 backend should request ARGB visuals by default + * or not. + * + * By default, Clutter requests RGB visuals. + * + * If no ARGB visuals are found, the X11 backend will fall back to + * requesting a RGB visual instead. + * + * ARGB visuals are required for the #ClutterStage:use-alpha property to work. + * + * This function can only be called once, and before clutter_init() is + * called. + * + * Since: 1.2 + */ +void +clutter_x11_set_use_argb_visual (gboolean use_argb) +{ + if (backend_singleton != NULL) + { + g_warning ("%s() can only be used before calling clutter_init()", + G_STRFUNC); + return; + } + + CLUTTER_NOTE (BACKEND, "ARGB visuals are %s", + use_argb ? "enabled" : "disabled"); + + clutter_enable_argb = use_argb; +} + +/** + * clutter_x11_get_use_argb_visual: + * + * Retrieves whether the Clutter X11 backend is using ARGB visuals by default + * + * Return value: %TRUE if ARGB visuals are queried by default + * + * Since: 1.2 + */ gboolean -clutter_x11_has_argb_visuals (void) +clutter_x11_get_use_argb_visual (void) { return clutter_enable_argb; } @@ -1007,3 +1051,25 @@ clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11) return NULL; } + +/** + * clutter_x11_get_visual_info: + * + * Retrieves the XVisualInfo used by the Clutter X11 + * backend. + * + * Return value: a XVisualInfo, or + * None. The returned value should be freed using XFree() + * when done + * + * Since: 1.2 + */ +XVisualInfo * +clutter_x11_get_visual_info (void) +{ + ClutterBackendX11 *backend_x11; + + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); + + return clutter_backend_x11_get_visual_info (backend_x11); +} diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index 6dbc29050..12b13225c 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -770,8 +770,10 @@ clutter_x11_get_stage_from_window (Window win) * along the lines of clutter_backend_x11_get_foreign_visual () or perhaps * clutter_stage_x11_get_foreign_visual () * - * Return value: An XVisualInfo suitable for creating a foreign stage. - * You should free this using XFree. + * Return value: An XVisualInfo suitable for creating a foreign stage. Use + * XFree() to free the returned value instead + * + * Deprecated: 1.2: Use clutter_x11_get_visual_info() instead * * Since: 0.4 */ diff --git a/clutter/x11/clutter-x11.h b/clutter/x11/clutter-x11.h index 2b960578c..368a117b0 100644 --- a/clutter/x11/clutter-x11.h +++ b/clutter/x11/clutter-x11.h @@ -98,10 +98,14 @@ gint clutter_x11_untrap_x_errors (void); Display *clutter_x11_get_default_display (void); int clutter_x11_get_default_screen (void); Window clutter_x11_get_root_window (void); +XVisualInfo *clutter_x11_get_visual_info (void); void clutter_x11_set_display (Display * xdpy); +#ifndef CLUTTER_DISABLE_DEPRECATED +XVisualInfo *clutter_x11_get_stage_visual (ClutterStage *stage) G_GNUC_DEPRECATED; +#endif + Window clutter_x11_get_stage_window (ClutterStage *stage); -XVisualInfo *clutter_x11_get_stage_visual (ClutterStage *stage); gboolean clutter_x11_set_stage_foreign (ClutterStage *stage, Window xwindow); @@ -124,7 +128,8 @@ gboolean clutter_x11_has_xinput (void); gboolean clutter_x11_has_composite_extension (void); -gboolean clutter_x11_has_argb_visuals (void); +void clutter_X11_set_use_argb_visual (gboolean use_argb); +gboolean clutter_x11_get_use_argb_visual (void); Time clutter_x11_get_current_event_time (void); diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 89bfa4039..0da1f24bb 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1081,6 +1081,9 @@ clutter_x11_trap_x_errors clutter_x11_untrap_x_errors clutter_x11_has_composite_extension clutter_x11_get_current_event_time +clutter_x11_set_use_argb_visual +clutter_x11_get_use_argb_visual +clutter_x11_get_visual_info ClutterX11FilterFunc From bd41db7c00fc54aa8077f53177ff33bd33a19c83 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 17 Mar 2009 13:07:46 +0000 Subject: [PATCH 58/83] Adds a --enable-profile option to configure.ac There is nothing else hooked up to this option so far --- README | 4 ++++ configure.ac | 58 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/README b/README index 7f38af7f7..46674a119 100644 --- a/README +++ b/README @@ -22,6 +22,10 @@ If you are building the Introspection data you will also need: • GObject-Introspection >= 0.6.4 +If you want built in support for profiling Clutter you will also need: + + • UProf 0.2 available from git://git.moblin.org/uprof.git + The official website is: http://www.clutter-project.org diff --git a/configure.ac b/configure.ac index 2712c1b5b..bc681f9e6 100644 --- a/configure.ac +++ b/configure.ac @@ -638,19 +638,6 @@ AS_CASE([$with_json], AC_SUBST(JSON_PREFIX) AM_CONDITIONAL(LOCAL_JSON_GLIB, test "x$have_json" = "xno") -dnl === Dependencies, compiler flags and linker libraries ===================== -CLUTTER_REQUIRES="cairo >= 1.4 pangocairo >= 1.20 gobject-2.0 >= 2.16 gthread-2.0 gmodule-no-export-2.0 $IMAGE_PC_FILES $BACKEND_PC_FILES $JSON_GLIB_PC" - -PKG_CHECK_MODULES(CLUTTER_DEPS, [$CLUTTER_REQUIRES]) - -AC_SUBST(CLUTTER_REQUIRES) - -CLUTTER_CFLAGS="$SDL_CFLAGS $EGL_CFLAGS $GLX_CFLAGS $OSX_CFLAGS $WIN32_CFLAGS $CLUTTER_DEPS_CFLAGS" -CLUTTER_LIBS="$SDL_LIBS $EGL_LIBS $X11_LIBS $GLX_LIBS $OSX_LIBS $WIN32_LIBS $CLUTTER_DEPS_LIBS" - -AC_SUBST(CLUTTER_CFLAGS) -AC_SUBST(CLUTTER_LIBS) - dnl === Enable debug level ==================================================== m4_define([debug_default], [m4_if(m4_eval(clutter_minor_version % 2), [1], [yes], [minimum])]) @@ -702,6 +689,37 @@ AS_CASE([$enable_cogl_debug], AC_SUBST(COGL_DEBUG_CFLAGS) +m4_define([profile_default], [no]) +AC_ARG_ENABLE(profile, + AC_HELP_STRING([--enable-profile=@<:@no/yes@:>@], + [Turn on profiling support. yes; All profiling probe points are compiled in and may be runtime enabled. no; No profiling support will built into clutter. @<:@default=no@:>@]), + [], + [enable_profile=profile_default]) + +AS_CASE([$enable_profile], + + [yes], [ + if test "x$GCC" = "xyes"; then + PKG_CHECK_MODULES([PROFILE_DEP], [uprof-0.2]) + CLUTTER_PROFILE_CFLAGS=" -DCLUTTER_ENABLE_PROFILE $PROFILE_DEP_CFLAGS" + CLUTTER_PROFILE_LDFLAGS=" $PROFILE_DEP_LIBS" + if test "x$enable_debug" = "xyes"; then + CLUTTER_PROFILE_CFLAGS+=" -DUPROF_DEBUG" + fi + else + AC_MSG_ERROR([--enable-profile is currently only supported if using GCC]) + fi + ], + [no], [ + CLUTTER_PROFILE_CFLAGS="" + CLUTTER_PROFILE_LDFLAGS="" + ], + [*], [AC_MSG_ERROR([Invalid value for --enable-profile])] +) +AM_CONDITIONAL(PROFILE, test "x$enable_profile" != "xno") +AC_SUBST(CLUTTER_PROFILE_CFLAGS) +AC_SUBST(CLUTTER_PROFILE_LDFLAGS) + dnl === Enable strict compiler flags ========================================== # use strict compiler flags only on development releases @@ -723,6 +741,19 @@ AS_IF([test "x$enable_maintainer_flags" = "xyes" && test "x$GCC" = "xyes"], AC_SUBST(MAINTAINER_CFLAGS) +dnl === Dependencies, compiler flags and linker libraries ===================== +CLUTTER_REQUIRES="cairo >= 1.4 pangocairo >= 1.20 gobject-2.0 >= 2.16 gthread-2.0 gmodule-no-export-2.0 $IMAGE_PC_FILES $BACKEND_PC_FILES $JSON_GLIB_PC" + +PKG_CHECK_MODULES(CLUTTER_DEPS, [$CLUTTER_REQUIRES]) + +AC_SUBST(CLUTTER_REQUIRES) + +CLUTTER_CFLAGS="$SDL_CFLAGS $EGL_CFLAGS $GLX_CFLAGS $OSX_CFLAGS $WIN32_CFLAGS $CLUTTER_DEPS_CFLAGS $CLUTTER_PROFILE_CFLAGS" +CLUTTER_LIBS="$SDL_LIBS $EGL_LIBS $X11_LIBS $GLX_LIBS $OSX_LIBS $WIN32_LIBS $CLUTTER_DEPS_LIBS $CLUTTER_PROFILE_LDFLAGS" + +AC_SUBST(CLUTTER_CFLAGS) +AC_SUBST(CLUTTER_LIBS) + dnl === GObject-Introspection check =========================================== GOBJECT_INTROSPECTION_CHECK([0.6.4]) @@ -876,6 +907,7 @@ echo " • Compiler options:" echo " Clutter debug level: ${enable_debug}" echo " COGL debug level: ${enable_cogl_debug}" echo " Compiler flags: ${MAINTAINER_CFLAGS}" +echo " Profiling enabled: ${enable_profile}" # Documentation echo "" From 00577558540db2c9f7bf24e870ff6231abb2b99d Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 30 Mar 2009 16:41:02 +0100 Subject: [PATCH 59/83] profiling: Adds initial UProf support across clutter UProf is a small library that aims to help applications/libraries provide domain specific reports about performance. It currently provides high precision timer primitives (rdtsc on x86) and simple counters, the ability to link statistics between optional components at runtime and makes report generation easy. This adds initial accounting for: - Total mainloop time - Painting - Picking - Layouting - Idle time The timing done by uprof is of wall clock time. It's not based on stochastic samples we simply sample a counter at the start and end. When dealing with the complexities of GPU drivers and with various kinds of IO this form of profiling can be quite enlightening as it will be able to represent where your application is blocking unlike tools such as sysprof. To enable uprof accounting you must configure Clutter with --enable-profile and have uprof-0.2 installed from git://git.moblin.org/uprof If you want to see a report of statistics when Clutter applications exit you should export CLUTTER_PROFILE_OUTPUT_REPORT=1 before running them. Just a final word of caution; this stuff is new and the manual nature of adding uprof instrumentation means it is prone to some errors when modifying code. This just means that when you question strange results don't rule out a mistake in the instrumentation. Obviously though we hope the benfits out weigh e.g. by focusing on very key stats and by having automatic reporting. --- clutter/Makefile.am | 2 + clutter/clutter-actor.c | 14 +++ clutter/clutter-backend.c | 15 +++ clutter/clutter-main.c | 94 ++++++++++++++++- clutter/clutter-profile.c | 170 ++++++++++++++++++++++++++++++ clutter/clutter-profile.h | 57 ++++++++++ clutter/clutter-stage.c | 9 ++ clutter/glx/clutter-backend-glx.c | 17 +++ 8 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 clutter/clutter-profile.c create mode 100644 clutter/clutter-profile.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 315144185..196d6ce43 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -106,6 +106,7 @@ source_h = \ $(srcdir)/clutter-types.h \ $(srcdir)/clutter-units.h \ $(srcdir)/clutter-util.h \ + $(srcdir)/clutter-profile.h \ $(top_builddir)/clutter/clutter-version.h \ $(NULL) @@ -180,6 +181,7 @@ source_c = \ $(srcdir)/clutter-timeout-pool.c \ $(srcdir)/clutter-units.c \ $(srcdir)/clutter-util.c \ + $(srcdir)/clutter-profile.c \ $(NULL) source_h_priv = \ diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 724a2ac49..c873236b4 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -204,6 +204,7 @@ #include "clutter-private.h" #include "clutter-debug.h" #include "clutter-units.h" +#include "clutter-profile.h" #include "cogl/cogl.h" typedef struct _ShaderData ShaderData; @@ -2367,6 +2368,15 @@ clutter_actor_paint (ClutterActor *self) ClutterActorPrivate *priv; ClutterMainContext *context; gboolean clip_set = FALSE; + CLUTTER_STATIC_COUNTER (actor_paint_counter, + "Actor real-paint counter", + "Increments each time any actor is painted", + 0 /* no application private data */); + CLUTTER_STATIC_COUNTER (actor_pick_counter, + "Actor pick-paint counter", + "Increments each time any actor is painted " + "for picking", + 0 /* no application private data */); g_return_if_fail (CLUTTER_IS_ACTOR (self)); @@ -2422,6 +2432,8 @@ clutter_actor_paint (ClutterActor *self) { ClutterColor col = { 0, }; + CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_pick_counter); + _clutter_id_to_color (clutter_actor_get_gid (self), &col); /* Actor will then paint silhouette of itself in supplied @@ -2432,6 +2444,8 @@ clutter_actor_paint (ClutterActor *self) } else { + CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_paint_counter); + clutter_actor_shader_pre_paint (self, FALSE); self->priv->queued_redraw = FALSE; diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 3e70e255c..54c7fa963 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -46,6 +46,7 @@ #include "clutter-fixed.h" #include "clutter-marshal.h" #include "clutter-private.h" +#include "clutter-profile.h" #include @@ -302,10 +303,24 @@ _clutter_backend_redraw (ClutterBackend *backend, ClutterStage *stage) { ClutterBackendClass *klass; + CLUTTER_STATIC_COUNTER (redraw_counter, + "_clutter_backend_redraw counter", + "Increments for each _clutter_backend_redraw call", + 0 /* no application private data */); + CLUTTER_STATIC_TIMER (redraw_timer, + "Mainloop", /* parent */ + "Redrawing", + "The time spent redrawing everything", + 0 /* no application private data */); + + CLUTTER_COUNTER_INC (_clutter_uprof_context, redraw_counter); + CLUTTER_TIMER_START (_clutter_uprof_context, redraw_timer); klass = CLUTTER_BACKEND_GET_CLASS (backend); if (G_LIKELY (klass->redraw)) klass->redraw (backend, stage); + + CLUTTER_TIMER_STOP (_clutter_uprof_context, redraw_timer); } gboolean diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 84f5ed696..f5f0afa21 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -109,6 +109,7 @@ #include "clutter-debug.h" #include "clutter-version.h" /* For flavour define */ #include "clutter-frame-source.h" +#include "clutter-profile.h" #include "cogl/cogl.h" #include "pango/cogl-pango.h" @@ -187,10 +188,16 @@ _clutter_stage_maybe_relayout (ClutterActor *stage) { gfloat natural_width, natural_height; ClutterActorBox box = { 0, }; + CLUTTER_STATIC_TIMER (relayout_timer, + "Mainloop", /* no parent */ + "Layouting", + "The time spent reallocating the stage", + 0 /* no application private data */); /* avoid reentrancy */ if (!(CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_IN_RELAYOUT)) { + CLUTTER_TIMER_START (_clutter_uprof_context, relayout_timer); CLUTTER_NOTE (ACTOR, "Recomputing layout"); CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_IN_RELAYOUT); @@ -212,6 +219,7 @@ _clutter_stage_maybe_relayout (ClutterActor *stage) clutter_actor_allocate (stage, &box, CLUTTER_ALLOCATION_NONE); CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_IN_RELAYOUT); + CLUTTER_TIMER_STOP (_clutter_uprof_context, relayout_timer); } } @@ -529,10 +537,39 @@ _clutter_do_pick (ClutterStage *stage, CoglColor stage_pick_id; guint32 id; GLboolean dither_was_on; + ClutterActor *actor; + CLUTTER_STATIC_COUNTER (do_pick_counter, + "_clutter_do_pick counter", + "Increments for each full pick run", + 0 /* no application private data */); + CLUTTER_STATIC_TIMER (pick_timer, + "Mainloop", /* parent */ + "Picking", + "The time spent picking", + 0 /* no application private data */); + CLUTTER_STATIC_TIMER (pick_clear, + "Picking", /* parent */ + "Stage clear (pick)", + "The time spent clearing stage for picking", + 0 /* no application private data */); + CLUTTER_STATIC_TIMER (pick_paint, + "Picking", /* parent */ + "Painting actors (pick mode)", + "The time spent painting actors in pick mode", + 0 /* no application private data */); + CLUTTER_STATIC_TIMER (pick_read, + "Picking", /* parent */ + "Read Pixels", + "The time spent issuing a read pixels", + 0 /* no application private data */); + if (clutter_debug_flags & CLUTTER_DEBUG_NOP_PICKING) return CLUTTER_ACTOR (stage); + CLUTTER_COUNTER_INC (_clutter_uprof_context, do_pick_counter); + CLUTTER_TIMER_START (_clutter_uprof_context, pick_timer); + context = _clutter_context_get_default (); _clutter_backend_ensure_context (context->backend, stage); @@ -545,9 +582,11 @@ _clutter_do_pick (ClutterStage *stage, cogl_disable_fog (); cogl_color_set_from_4ub (&stage_pick_id, 255, 255, 255, 255); + CLUTTER_TIMER_START (_clutter_uprof_context, pick_clear); cogl_clear (&stage_pick_id, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH); + CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_clear); /* Disable dithering (if any) when doing the painting in pick mode */ dither_was_on = glIsEnabled (GL_DITHER); @@ -557,9 +596,11 @@ _clutter_do_pick (ClutterStage *stage, /* Render the entire scence in pick mode - just single colored silhouette's * are drawn offscreen (as we never swap buffers) */ + CLUTTER_TIMER_START (_clutter_uprof_context, pick_paint); context->pick_mode = mode; clutter_actor_paint (CLUTTER_ACTOR (stage)); context->pick_mode = CLUTTER_PICK_NONE; + CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_paint); if (G_LIKELY (!(clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) cogl_clip_pop (); @@ -568,10 +609,12 @@ _clutter_do_pick (ClutterStage *stage, cogl_flush (); /* Read the color of the screen co-ords pixel */ + CLUTTER_TIMER_START (_clutter_uprof_context, pick_read); cogl_read_pixels (x, y, 1, 1, COGL_READ_PIXELS_COLOR_BUFFER, COGL_PIXEL_FORMAT_RGBA_8888, pixel); + CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_read); if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) { @@ -585,11 +628,17 @@ _clutter_do_pick (ClutterStage *stage, glEnable (GL_DITHER); if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) - return CLUTTER_ACTOR (stage); + { + CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_timer); + return CLUTTER_ACTOR (stage); + } id = _clutter_pixel_to_id (pixel); + actor = clutter_get_actor_by_gid (id); - return clutter_get_actor_by_gid (id); + CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_timer); + + return actor; } static ClutterTextDirection @@ -723,6 +772,28 @@ clutter_main_level (void) return clutter_main_loop_level; } +#ifdef CLUTTER_ENABLE_PROFILE +static gint (*prev_poll) (GPollFD *ufds, guint nfsd, gint timeout_) = NULL; + +static gint +timed_poll (GPollFD *ufds, + guint nfsd, + gint timeout_) +{ + gint ret; + CLUTTER_STATIC_TIMER (poll_timer, + "Mainloop", /* parent */ + "poll (idle)", + "The time spent idle in poll()", + 0 /* no application private data */); + + CLUTTER_TIMER_START (_clutter_uprof_context, poll_timer); + ret = prev_poll (ufds, nfsd, timeout_); + CLUTTER_TIMER_STOP (_clutter_uprof_context, poll_timer); + return ret; +} +#endif + /** * clutter_main: * @@ -732,6 +803,14 @@ void clutter_main (void) { GMainLoop *loop; + CLUTTER_STATIC_TIMER (mainloop_timer, + NULL, /* no parent */ + "Mainloop", + "The time spent in the clutter mainloop", + 0 /* no application private data */); + + if (clutter_main_loop_level == 0) + CLUTTER_TIMER_START (_clutter_uprof_context, mainloop_timer); /* Make sure there is a context */ CLUTTER_CONTEXT (); @@ -747,6 +826,14 @@ clutter_main (void) clutter_main_loop_level++; +#ifdef CLUTTER_ENABLE_PROFILE + if (!prev_poll) + { + prev_poll = g_main_context_get_poll_func (NULL); + g_main_context_set_poll_func (NULL, timed_poll); + } +#endif + loop = g_main_loop_new (NULL, TRUE); main_loops = g_slist_prepend (main_loops, loop); @@ -771,6 +858,9 @@ clutter_main (void) clutter_main_loop_level--; CLUTTER_MARK (); + + if (clutter_main_loop_level == 0) + CLUTTER_TIMER_STOP (_clutter_uprof_context, mainloop_timer); } static void diff --git a/clutter/clutter-profile.c b/clutter/clutter-profile.c new file mode 100644 index 000000000..f08d37306 --- /dev/null +++ b/clutter/clutter-profile.c @@ -0,0 +1,170 @@ + +#ifdef CLUTTER_ENABLE_PROFILE + +#include "clutter-profile.h" + +#include + +UProfContext *_clutter_uprof_context; +#define REPORT_COLUMN0_WIDTH 40 + +typedef struct _ClutterUProfReportState +{ + gulong n_frames; +} ClutterUProfReportState; + +static void +print_counter (UProfCounterResult *counter, + gpointer data) +{ + ClutterUProfReportState *state = data; + gulong count = uprof_counter_result_get_count (counter); + if (count == 0) + return; + + g_print (" %-*s %-5ld %-5ld\n", REPORT_COLUMN0_WIDTH - 2, + uprof_counter_result_get_name (counter), + uprof_counter_result_get_count (counter), + uprof_counter_result_get_count (counter) / state->n_frames); +} + +static char * +print_timer_fields (UProfTimerResult *timer, + guint *fields_width, + gpointer data) +{ + ClutterUProfReportState *state = data; + /* Print the field titles when timer == NULL */ + if (!timer) + return g_strdup_printf ("Per Frame"); + + return g_strdup_printf ("%-10.2f", + uprof_timer_result_get_total_msecs (timer) / + (float)state->n_frames); +} + +static void +print_report (UProfReport *report, UProfContext *context) +{ + GList *root_timers; + GList *l; + UProfTimerResult *stage_paint_timer; + UProfTimerResult *mainloop_timer; + UProfTimerResult *do_pick_timer; + float fps; + ClutterUProfReportState state; + + g_print ("\n"); + + /* FIXME: We need to fix the way Clutter initializes the uprof library + * (we don't currently call uprof_init()) and add a mechanism to know + * if uprof_init hasn't been called so we can simply bail out of report + * generation and not print spurious warning about missing timers. + * Probably we can just have uprof_report_print bail out if uprof wasn't + * initialized, so we don't have to care here. + */ + + stage_paint_timer = uprof_context_get_timer_result (context, "Redrawing"); +#if 0 + if (!stage_paint_timer) + g_critical ("Failed to find \"Redrawing\" timer " + "(you need to update print_report code if you rename it)\n"); +#endif + + state.n_frames = uprof_timer_result_get_start_count (stage_paint_timer); + g_print ("Frame count = %lu\n", state.n_frames); + + mainloop_timer = uprof_context_get_timer_result (context, "Mainloop"); + fps = (float)state.n_frames / (uprof_timer_result_get_total_msecs (mainloop_timer) + / 1000.0); + g_print ("Average fps = %5.2f\n", fps); + + do_pick_timer = uprof_context_get_timer_result (context, "Do pick"); + if (do_pick_timer) + { + int n_picks = uprof_timer_result_get_start_count (do_pick_timer); + + g_print ("Pick Stats:\n"); + g_print ("Pick count = %d\n", n_picks); + g_print ("Average picks per frame = %3.2f\n", + (float)n_picks / (float)state.n_frames); + g_print ("Average Msecs per pick = %3.2f\n", + (float)uprof_timer_result_get_total_msecs (do_pick_timer) + / (float)n_picks); + + g_print ("\n"); + } + + /* XXX: UProfs default reporting code now supports dynamic sizing for the Name + * column, the only thing it's missing is support for adding custom columns but + * when that's added we should switch away from manual report generation. */ + g_print ("Counters:\n"); + g_print (" %-*s %5s %s\n", REPORT_COLUMN0_WIDTH - 2, "Name", "Total", "Per Frame"); + g_print (" %-*s %5s %s\n", REPORT_COLUMN0_WIDTH - 2, "----", "-----", "---------"); + uprof_context_foreach_counter (context, + UPROF_COUNTER_SORT_COUNT_INC, + print_counter, + &state); + + g_print ("\n"); + g_print ("Timers:\n"); + root_timers = uprof_context_get_root_timer_results (context); + for (l = root_timers; l != NULL; l = l->next) + uprof_timer_result_print_and_children ((UProfTimerResult *)l->data, + print_timer_fields, + &state); + + g_print ("\n"); +} + +/* FIXME: we should be able to deal with creating the uprof context in + * clutter_init instead. I think the only reason I did it this way originally + * was as a quick hack. + */ +static void __attribute__ ((constructor)) +clutter_uprof_constructor (void) +{ + _clutter_uprof_context = uprof_context_new ("Clutter"); +} + +#if 0 +static void +print_timers (UProfContext *context) +{ + GList *root_timers; + GList *l; + + root_timers = uprof_context_get_root_timer_results (); + + root_timers = + g_list_sort_with_data (context->root_timers, + (GCompareDataFunc)_uprof_timer_compare_total_times, + NULL); + for (l = context->timers; l != NULL; l = l->next) + { + UProfTimerState *timer = l->data; + timer->children = + g_list_sort_with_data (timer->children, + (GCompareDataFunc) + _uprof_timer_compare_total_times, + NULL); + } +} +#endif + +static void __attribute__ ((destructor)) +clutter_uprof_destructor (void) +{ + if (getenv ("CLUTTER_PROFILE_OUTPUT_REPORT")) + { + UProfReport *report = uprof_report_new ("Clutter report"); + uprof_report_add_context (report, _clutter_uprof_context); + uprof_report_add_context_callback (report, print_report); + uprof_report_print (report); + uprof_report_unref (report); + } + uprof_context_unref (_clutter_uprof_context); +} + +#endif + diff --git a/clutter/clutter-profile.h b/clutter/clutter-profile.h new file mode 100644 index 000000000..a8c57c6ec --- /dev/null +++ b/clutter/clutter-profile.h @@ -0,0 +1,57 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2009 Intel Corporation. + * + * 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, see . + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef _CLUTTER_PROFILE_H_ +#define _CLUTTER_PROFILE_H_ + +G_BEGIN_DECLS + +#ifdef CLUTTER_ENABLE_PROFILE + +#include + +extern UProfContext *_clutter_uprof_context; + +#define CLUTTER_STATIC_TIMER UPROF_STATIC_TIMER +#define CLUTTER_STATIC_COUNTER UPROF_STATIC_COUNTER +#define CLUTTER_COUNTER_INC UPROF_COUNTER_INC +#define CLUTTER_COUNTER_DEC UPROF_COUNTER_DEC +#define CLUTTER_TIMER_START UPROF_TIMER_START +#define CLUTTER_TIMER_STOP UPROF_TIMER_STOP + +#else /* CLUTTER_ENABLE_PROFILE */ + +#define CLUTTER_STATIC_TIMER(A,B,C,D,E) extern void _clutter_dummy_decl (void) +#define CLUTTER_STATIC_COUNTER(A,B,C,D) extern void _clutter_dummy_decl (void) +#define CLUTTER_COUNTER_INC(A,B) G_STMT_START{ (void)0; }G_STMT_END +#define CLUTTER_COUNTER_DEC(A,B) G_STMT_START{ (void)0; }G_STMT_END +#define CLUTTER_TIMER_START(A,B) G_STMT_START{ (void)0; }G_STMT_END +#define CLUTTER_TIMER_STOP(A,B) G_STMT_START{ (void)0; }G_STMT_END + +#endif /* CLUTTER_ENABLE_PROFILE */ + +G_END_DECLS + +#endif /* _CLUTTER_PROFILE_H_ */ diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 32eac253f..66b7d0eba 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -68,6 +68,7 @@ #include "clutter-version.h" /* For flavour */ #include "clutter-id-pool.h" #include "clutter-container.h" +#include "clutter-profile.h" #include "cogl/cogl.h" @@ -246,6 +247,11 @@ clutter_stage_paint (ClutterActor *self) ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv; CoglColor stage_color; guint8 real_alpha; + CLUTTER_STATIC_TIMER (stage_clear_timer, + "Painting actors", /* parent */ + "Stage clear", + "The time spent clearing the stage", + 0 /* no application private data */); CLUTTER_NOTE (PAINT, "Initializing stage paint"); @@ -264,9 +270,12 @@ clutter_stage_paint (ClutterActor *self) priv->use_alpha ? real_alpha : 255); cogl_color_premultiply (&stage_color); + + CLUTTER_TIMER_START (_clutter_uprof_context, stage_clear_timer); cogl_clear (&stage_color, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH); + CLUTTER_TIMER_STOP (_clutter_uprof_context, stage_clear_timer); if (priv->use_fog) { diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index dfcf9ee32..7d8d417b9 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -42,6 +42,7 @@ #include "clutter-backend-glx.h" #include "clutter-stage-glx.h" #include "clutter-glx.h" +#include "clutter-profile.h" #include "../clutter-event.h" #include "../clutter-main.h" @@ -51,6 +52,7 @@ #include "cogl/cogl.h" + G_DEFINE_TYPE (ClutterBackendGLX, clutter_backend_glx, CLUTTER_TYPE_BACKEND_X11); /* singleton object */ @@ -654,6 +656,16 @@ clutter_backend_glx_redraw (ClutterBackend *backend, ClutterStageGLX *stage_glx; ClutterStageX11 *stage_x11; ClutterStageWindow *impl; + CLUTTER_STATIC_TIMER (painting_timer, + "Redrawing", /* parent */ + "Painting actors", + "The time spent painting actors", + 0 /* no application private data */); + CLUTTER_STATIC_TIMER (swapbuffers_timer, + "Redrawing", /* parent */ + "glXSwapBuffers", + "The time spent blocked by glXSwapBuffers", + 0 /* no application private data */); impl = _clutter_stage_get_window (stage); if (G_UNLIKELY (impl == NULL)) @@ -668,9 +680,11 @@ clutter_backend_glx_redraw (ClutterBackend *backend, stage_x11 = CLUTTER_STAGE_X11 (impl); stage_glx = CLUTTER_STAGE_GLX (impl); + CLUTTER_TIMER_START (_clutter_uprof_context, painting_timer); /* this will cause the stage implementation to be painted */ clutter_actor_paint (CLUTTER_ACTOR (stage)); cogl_flush (); + CLUTTER_TIMER_STOP (_clutter_uprof_context, painting_timer); if (stage_x11->xwin != None) { @@ -682,7 +696,10 @@ clutter_backend_glx_redraw (ClutterBackend *backend, CLUTTER_NOTE (BACKEND, "glXSwapBuffers (display: %p, window: 0x%lx)", backend_x11->xdpy, (unsigned long) stage_x11->xwin); + + CLUTTER_TIMER_START (_clutter_uprof_context, swapbuffers_timer); glXSwapBuffers (backend_x11->xdpy, stage_x11->xwin); + CLUTTER_TIMER_STOP (_clutter_uprof_context, swapbuffers_timer); } } From 9cb530d42e7860d1a44f441b97910fd3903480f1 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 6 Jan 2010 18:15:13 +0000 Subject: [PATCH 60/83] profiling: Parse --clutter-profile and CLUTTER_PROFILE= options As we have for debugging, this adds the ability to control profiling flags either via the command line or an environment variable. The first option added is CLUTTER_PROFILE=disable-report This also changes the reporting to be opt-out so you don't need to export CLUTTER_PROFILE_OUTPUT_REPORT=1 to see a report but you can use CLUTTER_PROFILE=disable-report to disable it if desired. --- clutter/clutter-main.c | 51 +++++++++++++++++++++++++++++++++++++++ clutter/clutter-profile.c | 2 +- clutter/clutter-profile.h | 8 ++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index f5f0afa21..ac11ccc78 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -137,6 +137,7 @@ static guint clutter_main_loop_level = 0; static GSList *main_loops = NULL; guint clutter_debug_flags = 0; /* global clutter debug flag */ +guint clutter_profile_flags = 0; /* global clutter profile flag */ const guint clutter_major_version = CLUTTER_MAJOR_VERSION; const guint clutter_minor_version = CLUTTER_MINOR_VERSION; @@ -165,6 +166,12 @@ static const GDebugKey clutter_debug_keys[] = { }; #endif /* CLUTTER_ENABLE_DEBUG */ +#ifdef CLUTTER_ENABLE_PROFILE +static const GDebugKey clutter_profile_keys[] = { + {"disable-report", CLUTTER_PROFILE_DISABLE_REPORT } +}; +#endif /* CLUTTER_ENABLE_DEBUG */ + /** * clutter_get_show_fps: * @@ -1423,6 +1430,32 @@ clutter_arg_no_debug_cb (const char *key, } #endif /* CLUTTER_ENABLE_DEBUG */ +#ifdef CLUTTER_ENABLE_PROFILE +static gboolean +clutter_arg_profile_cb (const char *key, + const char *value, + gpointer user_data) +{ + clutter_profile_flags |= + g_parse_debug_string (value, + clutter_profile_keys, + G_N_ELEMENTS (clutter_profile_keys)); + return TRUE; +} + +static gboolean +clutter_arg_no_profile_cb (const char *key, + const char *value, + gpointer user_data) +{ + clutter_profile_flags &= + ~g_parse_debug_string (value, + clutter_profile_keys, + G_N_ELEMENTS (clutter_profile_keys)); + return TRUE; +} +#endif /* CLUTTER_ENABLE_PROFILE */ + GQuark clutter_init_error_quark (void) { @@ -1557,6 +1590,12 @@ static GOptionEntry clutter_args[] = { { "clutter-no-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_no_debug_cb, N_("Clutter debugging flags to unset"), "FLAGS" }, #endif /* CLUTTER_ENABLE_DEBUG */ +#ifdef CLUTTER_ENABLE_PROFILE + { "clutter-profile", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_profile_cb, + N_("Clutter profiling flags to set"), "FLAGS" }, + { "clutter-no-profile", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_no_profile_cb, + N_("Clutter profiling flags to unset"), "FLAGS" }, +#endif /* CLUTTER_ENABLE_PROFILE */ { NULL, }, }; @@ -1600,6 +1639,18 @@ pre_parse_hook (GOptionContext *context, } #endif /* CLUTTER_ENABLE_DEBUG */ +#ifdef CLUTTER_ENABLE_PROFILE + env_string = g_getenv ("CLUTTER_PROFILE"); + if (env_string != NULL) + { + clutter_profile_flags = + g_parse_debug_string (env_string, + clutter_profile_keys, + G_N_ELEMENTS (clutter_profile_keys)); + env_string = NULL; + } +#endif /* CLUTTER_ENABLE_PROFILE */ + env_string = g_getenv ("CLUTTER_SHOW_FPS"); if (env_string) clutter_show_fps = TRUE; diff --git a/clutter/clutter-profile.c b/clutter/clutter-profile.c index f08d37306..f5dff93f5 100644 --- a/clutter/clutter-profile.c +++ b/clutter/clutter-profile.c @@ -155,7 +155,7 @@ print_timers (UProfContext *context) static void __attribute__ ((destructor)) clutter_uprof_destructor (void) { - if (getenv ("CLUTTER_PROFILE_OUTPUT_REPORT")) + if (!(clutter_profile_flags & CLUTTER_PROFILE_DISABLE_REPORT)) { UProfReport *report = uprof_report_new ("Clutter report"); uprof_report_add_context (report, _clutter_uprof_context); diff --git a/clutter/clutter-profile.h b/clutter/clutter-profile.h index a8c57c6ec..53330a893 100644 --- a/clutter/clutter-profile.h +++ b/clutter/clutter-profile.h @@ -26,8 +26,14 @@ #ifndef _CLUTTER_PROFILE_H_ #define _CLUTTER_PROFILE_H_ +#include + G_BEGIN_DECLS +typedef enum { + CLUTTER_PROFILE_DISABLE_REPORT = 1 << 1 +} ClutterProfileFlag; + #ifdef CLUTTER_ENABLE_PROFILE #include @@ -52,6 +58,8 @@ extern UProfContext *_clutter_uprof_context; #endif /* CLUTTER_ENABLE_PROFILE */ +extern guint clutter_profile_flags; + G_END_DECLS #endif /* _CLUTTER_PROFILE_H_ */ From 0b6515a1d53571fea13417d47e7bf0f4749321f8 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 17 Apr 2009 12:15:56 +0100 Subject: [PATCH 61/83] profiling: Allow limiting statisics just to picking This suspends and resumes all uprof timers and counters except while dealing with picking, so as to give more focused statistics. Be aware that there are still some issues with this profile option since there are a few special case counters and timers that shouldn't be suspended; noteably the frame counters are incorrect so the per frame stats can't be trusted. --- clutter/clutter-main.c | 22 ++++++++++++++++++++-- clutter/clutter-profile.c | 29 +++++++++++++++++++++++++++++ clutter/clutter-profile.h | 9 +++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index ac11ccc78..5efa31126 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -168,6 +168,7 @@ static const GDebugKey clutter_debug_keys[] = { #ifdef CLUTTER_ENABLE_PROFILE static const GDebugKey clutter_profile_keys[] = { + {"picking-only", CLUTTER_PROFILE_PICKING_ONLY }, {"disable-report", CLUTTER_PROFILE_DISABLE_REPORT } }; #endif /* CLUTTER_ENABLE_DEBUG */ @@ -574,6 +575,11 @@ _clutter_do_pick (ClutterStage *stage, if (clutter_debug_flags & CLUTTER_DEBUG_NOP_PICKING) return CLUTTER_ACTOR (stage); +#ifdef CLUTTER_ENABLE_PROFILE + if (clutter_profile_flags & CLUTTER_PROFILE_PICKING_ONLY) + _clutter_profile_resume (); +#endif /* CLUTTER_ENABLE_PROFILE */ + CLUTTER_COUNTER_INC (_clutter_uprof_context, do_pick_counter); CLUTTER_TIMER_START (_clutter_uprof_context, pick_timer); @@ -636,15 +642,22 @@ _clutter_do_pick (ClutterStage *stage, if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) { - CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_timer); - return CLUTTER_ACTOR (stage); + actor = CLUTTER_ACTOR (stage); + goto result; } id = _clutter_pixel_to_id (pixel); actor = clutter_get_actor_by_gid (id); +result: + CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_timer); +#ifdef CLUTTER_ENABLE_PROFILE + if (clutter_profile_flags & CLUTTER_PROFILE_PICKING_ONLY) + _clutter_profile_suspend (); +#endif + return actor; } @@ -1528,6 +1541,11 @@ clutter_init_real (GError **error) /* - will call to backend and cogl */ _clutter_feature_init (); +#ifdef CLUTTER_ENABLE_PROFILE + if (clutter_profile_flags & CLUTTER_PROFILE_PICKING_ONLY) + _clutter_profile_suspend (); +#endif + /* * Resolution requires display to be open, so can only be queried after * the post_parse hooks run. diff --git a/clutter/clutter-profile.c b/clutter/clutter-profile.c index f5dff93f5..aa84f4349 100644 --- a/clutter/clutter-profile.c +++ b/clutter/clutter-profile.c @@ -8,6 +8,9 @@ UProfContext *_clutter_uprof_context; #define REPORT_COLUMN0_WIDTH 40 +static gboolean searched_for_gl_uprof_context = FALSE; +static UProfContext *gl_uprof_context = NULL; + typedef struct _ClutterUProfReportState { gulong n_frames; @@ -166,5 +169,31 @@ clutter_uprof_destructor (void) uprof_context_unref (_clutter_uprof_context); } +void +_clutter_profile_suspend (void) +{ + if (G_UNLIKELY (!searched_for_gl_uprof_context)) + { + gl_uprof_context = uprof_find_context ("OpenGL"); + searched_for_gl_uprof_context = TRUE; + } + + if (gl_uprof_context) + uprof_context_suspend (gl_uprof_context); + + /* NB: The Cogl context is linked to this so it will also be suspended... */ + uprof_context_suspend (_clutter_uprof_context); +} + +void +_clutter_profile_resume (void) +{ + if (gl_uprof_context) + uprof_context_resume (gl_uprof_context); + + /* NB: The Cogl context is linked to this so it will also be resumed... */ + uprof_context_resume (_clutter_uprof_context); +} + #endif diff --git a/clutter/clutter-profile.h b/clutter/clutter-profile.h index 53330a893..00ea77dfb 100644 --- a/clutter/clutter-profile.h +++ b/clutter/clutter-profile.h @@ -31,6 +31,7 @@ G_BEGIN_DECLS typedef enum { + CLUTTER_PROFILE_PICKING_ONLY = 1 << 0, CLUTTER_PROFILE_DISABLE_REPORT = 1 << 1 } ClutterProfileFlag; @@ -47,6 +48,11 @@ extern UProfContext *_clutter_uprof_context; #define CLUTTER_TIMER_START UPROF_TIMER_START #define CLUTTER_TIMER_STOP UPROF_TIMER_STOP +void +_clutter_profile_suspend (void); +void +_clutter_profile_resume (void); + #else /* CLUTTER_ENABLE_PROFILE */ #define CLUTTER_STATIC_TIMER(A,B,C,D,E) extern void _clutter_dummy_decl (void) @@ -56,6 +62,9 @@ extern UProfContext *_clutter_uprof_context; #define CLUTTER_TIMER_START(A,B) G_STMT_START{ (void)0; }G_STMT_END #define CLUTTER_TIMER_STOP(A,B) G_STMT_START{ (void)0; }G_STMT_END +#define _clutter_profile_suspend() G_STMT_START {} G_STMT_END +#define _clutter_profile_resume() G_STMT_START {} G_STMT_END + #endif /* CLUTTER_ENABLE_PROFILE */ extern guint clutter_profile_flags; From 30b557c4653d15eeb1311b516ab1f4c0035a5e83 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 3 Jul 2009 16:22:35 +0100 Subject: [PATCH 62/83] profiling: Adds initial UProf accounting to Cogl This adds gives Cogl a dedicated UProf context which will be linked together with Clutter's context during clutter_init_real(). Initial timers cover _cogl_journal_flush and _cogl_journal_log_quad You can explicitly ask for a report of Cogl statistics by exporting COGL_PROFILE_OUTPUT_REPORT=1 but since the context is linked with Clutter's the statisitcs will also be shown in the automatic Clutter reports. --- clutter/clutter-main.c | 7 +++++ clutter/cogl/cogl/Makefile.am | 2 ++ clutter/cogl/cogl/cogl-journal.c | 19 +++++++++++ clutter/cogl/cogl/cogl-profile.c | 30 ++++++++++++++++++ clutter/cogl/cogl/cogl-profile.h | 54 ++++++++++++++++++++++++++++++++ configure.ac | 2 +- 6 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 clutter/cogl/cogl/cogl-profile.c create mode 100644 clutter/cogl/cogl/cogl-profile.h diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 5efa31126..7e9ae5e87 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -1542,6 +1542,13 @@ clutter_init_real (GError **error) _clutter_feature_init (); #ifdef CLUTTER_ENABLE_PROFILE + { + UProfContext *cogl_context; + cogl_context = uprof_find_context ("Cogl"); + if (cogl_context) + uprof_context_link (_clutter_uprof_context, cogl_context); + } + if (clutter_profile_flags & CLUTTER_PROFILE_PICKING_ONLY) _clutter_profile_suspend (); #endif diff --git a/clutter/cogl/cogl/Makefile.am b/clutter/cogl/cogl/Makefile.am index 5ef3c3b9c..7d78106f5 100644 --- a/clutter/cogl/cogl/Makefile.am +++ b/clutter/cogl/cogl/Makefile.am @@ -134,6 +134,8 @@ libclutter_cogl_la_SOURCES = \ $(srcdir)/cogl-framebuffer.c \ $(srcdir)/cogl-matrix-mesa.h \ $(srcdir)/cogl-matrix-mesa.c \ + $(srcdir)/cogl-profile.h \ + $(srcdir)/cogl-profile.c \ $(NULL) EXTRA_DIST += $(cogl_winsys_sources) diff --git a/clutter/cogl/cogl/cogl-journal.c b/clutter/cogl/cogl/cogl-journal.c index db406ea9c..fbb419a90 100644 --- a/clutter/cogl/cogl/cogl-journal.c +++ b/clutter/cogl/cogl/cogl-journal.c @@ -33,6 +33,7 @@ #include "cogl-material-private.h" #include "cogl-vertex-buffer-private.h" #include "cogl-framebuffer-private.h" +#include "cogl-profile.h" #include #include @@ -535,12 +536,19 @@ _cogl_journal_flush (void) (cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE; CoglHandle framebuffer; CoglMatrixStack *modelview_stack; + COGL_STATIC_TIMER (flush_timer, + "Mainloop", /* parent */ + "Journal Flush", + "The time spent flushing the Cogl journal", + 0 /* no application private data */); _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (ctx->journal->len == 0) return; + COGL_TIMER_START (_cogl_uprof_context, flush_timer); + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING)) g_print ("BATCHING: journal len = %d\n", ctx->journal->len); @@ -608,6 +616,8 @@ _cogl_journal_flush (void) g_array_set_size (ctx->journal, 0); g_array_set_size (ctx->logged_vertices, 0); + + COGL_TIMER_STOP (_cogl_uprof_context, flush_timer); } static void @@ -644,9 +654,16 @@ _cogl_journal_log_quad (float x_1, int next_entry; guint32 disable_layers; CoglJournalEntry *entry; + COGL_STATIC_TIMER (log_timer, + "Mainloop", /* parent */ + "Journal Log", + "The time spent logging in the Cogl journal", + 0 /* no application private data */); _COGL_GET_CONTEXT (ctx, NO_RETVAL); + COGL_TIMER_START (_cogl_uprof_context, log_timer); + if (ctx->logged_vertices->len == 0) _cogl_journal_init (); @@ -765,5 +782,7 @@ _cogl_journal_log_quad (float x_1, if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING || cogl_debug_flags & COGL_DEBUG_RECTANGLES)) _cogl_journal_flush (); + + COGL_TIMER_STOP (_cogl_uprof_context, log_timer); } diff --git a/clutter/cogl/cogl/cogl-profile.c b/clutter/cogl/cogl/cogl-profile.c new file mode 100644 index 000000000..e17acda38 --- /dev/null +++ b/clutter/cogl/cogl/cogl-profile.c @@ -0,0 +1,30 @@ + +#ifdef COGL_ENABLE_PROFILE + +#include "cogl-profile.h" + +#include + +UProfContext *_cogl_uprof_context; + + +static void __attribute__ ((constructor)) +cogl_uprof_constructor (void) +{ + _cogl_uprof_context = uprof_context_new ("Cogl"); +} + +static void __attribute__ ((destructor)) +cogl_uprof_destructor (void) +{ + if (getenv ("COGL_PROFILE_OUTPUT_REPORT")) + { + UProfReport *report = uprof_report_new ("Cogl report"); + uprof_report_add_context (report, _cogl_uprof_context); + uprof_report_print (report); + uprof_report_unref (report); + } + uprof_context_unref (_cogl_uprof_context); +} + +#endif diff --git a/clutter/cogl/cogl/cogl-profile.h b/clutter/cogl/cogl/cogl-profile.h new file mode 100644 index 000000000..302b146c1 --- /dev/null +++ b/clutter/cogl/cogl/cogl-profile.h @@ -0,0 +1,54 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2007,2008,2009 Intel Corporation. + * + * 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_PROFILE_H__ +#define __COGL_PROFILE_H__ + + +#ifdef COGL_ENABLE_PROFILE + +#include + +extern UProfContext *_cogl_uprof_context; + +#define COGL_STATIC_TIMER UPROF_STATIC_TIMER +#define COGL_STATIC_COUNTER UPROF_STATIC_COUNTER +#define COGL_COUNTER_INC UPROF_COUNTER_INC +#define COGL_COUNTER_DEC UPROF_COUNTER_DEC +#define COGL_TIMER_START UPROF_TIMER_START +#define COGL_TIMER_STOP UPROF_TIMER_STOP + +#else + +#define COGL_STATIC_TIMER(A,B,C,D,E) extern void _cogl_dummy_decl (void) +#define COGL_STATIC_COUNTER(A,B,C,D) extern void _cogl_dummy_decl (void) +#define COGL_COUNTER_INC(A,B) G_STMT_START{ (void)0; }G_STMT_END +#define COGL_COUNTER_DEC(A,B) G_STMT_START{ (void)0; }G_STMT_END +#define COGL_TIMER_START(A,B) G_STMT_START{ (void)0; }G_STMT_END +#define COGL_TIMER_STOP(A,B) G_STMT_START{ (void)0; }G_STMT_END + + +#endif + +#endif /* __COGL_PROFILE_H__ */ + diff --git a/configure.ac b/configure.ac index bc681f9e6..93f0b5473 100644 --- a/configure.ac +++ b/configure.ac @@ -701,7 +701,7 @@ AS_CASE([$enable_profile], [yes], [ if test "x$GCC" = "xyes"; then PKG_CHECK_MODULES([PROFILE_DEP], [uprof-0.2]) - CLUTTER_PROFILE_CFLAGS=" -DCLUTTER_ENABLE_PROFILE $PROFILE_DEP_CFLAGS" + CLUTTER_PROFILE_CFLAGS=" -DCLUTTER_ENABLE_PROFILE -DCOGL_ENABLE_PROFILE $PROFILE_DEP_CFLAGS" CLUTTER_PROFILE_LDFLAGS=" $PROFILE_DEP_LIBS" if test "x$enable_debug" = "xyes"; then CLUTTER_PROFILE_CFLAGS+=" -DUPROF_DEBUG" From d9b91d61f5730e9ce29627f467c89070b78fb6b6 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 25 Nov 2009 14:26:32 +0000 Subject: [PATCH 63/83] framebuffers: cogl_offscreen_new_to_texture should take a ref on the texture We weren't taking a reference on the texture to be used as the color buffer for offscreen rendering, so it was possible to free the texture leaving the framebuffer in an inconsistent state. --- clutter/cogl/cogl/cogl-framebuffer-private.h | 1 + clutter/cogl/cogl/cogl-framebuffer.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/clutter/cogl/cogl/cogl-framebuffer-private.h b/clutter/cogl/cogl/cogl-framebuffer-private.h index eb94c6f7f..946adfe25 100644 --- a/clutter/cogl/cogl/cogl-framebuffer-private.h +++ b/clutter/cogl/cogl/cogl-framebuffer-private.h @@ -57,6 +57,7 @@ typedef struct _CoglOffscreen CoglFramebuffer _parent; GLuint fbo_handle; GLuint gl_stencil_handle; + CoglHandle texture; } CoglOffscreen; #define COGL_OFFSCREEN(X) ((CoglOffscreen *)(X)) diff --git a/clutter/cogl/cogl/cogl-framebuffer.c b/clutter/cogl/cogl/cogl-framebuffer.c index 585b67663..a34eaaa5c 100644 --- a/clutter/cogl/cogl/cogl-framebuffer.c +++ b/clutter/cogl/cogl/cogl-framebuffer.c @@ -339,6 +339,7 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle) offscreen->fbo_handle = fbo_gl_handle; offscreen->gl_stencil_handle = gl_stencil_handle; + offscreen->texture = cogl_handle_ref (texhandle); /* XXX: Can we get a away with removing this? It wasn't documented, and most * users of the API are hopefully setting up the modelview from scratch @@ -362,6 +363,8 @@ _cogl_offscreen_free (CoglOffscreen *offscreen) if (offscreen->gl_stencil_handle) GE (glDeleteRenderbuffers (1, &offscreen->gl_stencil_handle)); + if (offscreen->texture != COGL_INVALID_HANDLE) + cogl_handle_unref (offscreen->texture); GE (glDeleteFramebuffers (1, &offscreen->fbo_handle)); g_free (offscreen); } From 8e9f56c41160452166be68202227b206a690472d Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sun, 10 Jan 2010 11:35:26 +0000 Subject: [PATCH 64/83] build: Clean up private header/source files Some source files should not be passed through the introspection parser, as they are fully private and do not expose any valuable API. Also the clutter-profile.h header is private and should not be installed. --- clutter/Makefile.am | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 196d6ce43..1a23d9978 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -106,7 +106,6 @@ source_h = \ $(srcdir)/clutter-types.h \ $(srcdir)/clutter-units.h \ $(srcdir)/clutter-util.h \ - $(srcdir)/clutter-profile.h \ $(top_builddir)/clutter/clutter-version.h \ $(NULL) @@ -153,7 +152,6 @@ source_c = \ $(srcdir)/clutter-flow-layout.c \ $(srcdir)/clutter-frame-source.c \ $(srcdir)/clutter-group.c \ - $(srcdir)/clutter-id-pool.c \ $(srcdir)/clutter-interval.c \ $(srcdir)/clutter-layout-manager.c \ $(srcdir)/clutter-layout-meta.c \ @@ -177,11 +175,15 @@ source_c = \ $(srcdir)/clutter-texture.c \ $(srcdir)/clutter-text.c \ $(srcdir)/clutter-timeline.c \ - $(srcdir)/clutter-timeout-interval.c \ $(srcdir)/clutter-timeout-pool.c \ $(srcdir)/clutter-units.c \ $(srcdir)/clutter-util.c \ - $(srcdir)/clutter-profile.c \ + $(NULL) + +source_c_priv = \ + $(srcdir)/clutter-id-pool.c \ + $(srcdir)/clutter-profile.c \ + $(srcdir)/clutter-timeout-interval.c \ $(NULL) source_h_priv = \ @@ -194,6 +196,7 @@ source_h_priv = \ $(srcdir)/clutter-id-pool.h \ $(srcdir)/clutter-script-private.h \ $(srcdir)/clutter-timeout-interval.h \ + $(srcdir)/clutter-profile.h \ $(NULL) libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_LIBADD = \ @@ -214,6 +217,7 @@ libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_DEPENDENCIES = \ libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_SOURCES = \ $(source_c) \ $(source_h) \ + $(source_c_priv) \ $(source_h_priv) libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_LDFLAGS = $(LDADD) From 4c1231c7fe545a7764d06754d7d6be0906686e0a Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Wed, 6 Jan 2010 11:48:46 +0000 Subject: [PATCH 65/83] media: Add an API to specify which font should be used for subtitles Allow the user of the ClutterMedia interface to specify a Pango font description to display subtitles. Even if the underlying implementation of the interface does not natively use Pange, it must be capable of parsing the grammar that pango_font_description_from_string() accepts. --- clutter/clutter-media.c | 64 ++++++++++++++++++++++ clutter/clutter-media.h | 45 ++++++++------- doc/reference/clutter/clutter-sections.txt | 2 + 3 files changed, 90 insertions(+), 21 deletions(-) diff --git a/clutter/clutter-media.c b/clutter/clutter-media.c index 30e51bec6..ce50cf27a 100644 --- a/clutter/clutter-media.c +++ b/clutter/clutter-media.c @@ -123,6 +123,22 @@ clutter_media_base_init (gpointer g_iface) CLUTTER_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); + /** + * ClutterMedia:subtitle-font-name: + * + * The font used to display subtitles. The font description has to + * follow the same grammar as the one recognized by + * pango_font_description_from_string(). + * + * Since: 1.2 + */ + pspec = g_param_spec_string ("subtitle-font-name", + "Subtitle Font Name", + "The font used to display subtitles", + NULL, + CLUTTER_PARAM_READWRITE); + g_object_interface_install_property (g_iface, pspec); + /** * ClutterMedia:audio-volume: * @@ -403,6 +419,54 @@ clutter_media_get_subtitle_uri (ClutterMedia *media) return retval; } +/** + * clutter_media_set_subtitle_font_name: + * @media: a #ClutterMedia + * @font_name: a font name, or %NULL to set the default font name + * + * Sets the font used by the subtitle renderer. The @font_name string must be + * either %NULL, which means that the default font name of the underlying + * implementation will be used; or must follow the grammar recognized by + * pango_font_description_from_string() like: + * + * |[ + * clutter_media_set_subtitle_font_name (media, "Sans 24pt"); + * ]| + * + * Since: 1.2 + */ +void +clutter_media_set_subtitle_font_name (ClutterMedia *media, + const char *font_name) +{ + g_return_if_fail (CLUTTER_IS_MEDIA (media)); + + g_object_set (G_OBJECT (media), "subtitle-font-name", font_name, NULL); +} + +/** + * clutter_media_get_subtitle_font_name: + * @media: a #ClutterMedia + * + * Retrieves the font name currently used. + * + * Return value: a string containing the font name. Use g_free() + * to free the returned string + * + * Since: 1.2 + */ +gchar * +clutter_media_get_subtitle_font_name (ClutterMedia *media) +{ + gchar *retval = NULL; + + g_return_val_if_fail (CLUTTER_IS_MEDIA(media), NULL); + + g_object_get (G_OBJECT (media), "subtitle-font-name", &retval, NULL); + + return retval; +} + /** * clutter_media_set_audio_volume: * @media: a #ClutterMedia diff --git a/clutter/clutter-media.h b/clutter/clutter-media.h index c776c647c..95fe42713 100644 --- a/clutter/clutter-media.h +++ b/clutter/clutter-media.h @@ -62,29 +62,32 @@ struct _ClutterMediaIface const GError *error); }; -GType clutter_media_get_type (void) G_GNUC_CONST; +GType clutter_media_get_type (void) G_GNUC_CONST; -void clutter_media_set_uri (ClutterMedia *media, - const gchar *uri); -gchar * clutter_media_get_uri (ClutterMedia *media); -void clutter_media_set_filename (ClutterMedia *media, - const gchar *filename); +void clutter_media_set_uri (ClutterMedia *media, + const gchar *uri); +gchar * clutter_media_get_uri (ClutterMedia *media); +void clutter_media_set_filename (ClutterMedia *media, + const gchar *filename); -void clutter_media_set_playing (ClutterMedia *media, - gboolean playing); -gboolean clutter_media_get_playing (ClutterMedia *media); -void clutter_media_set_progress (ClutterMedia *media, - gdouble progress); -gdouble clutter_media_get_progress (ClutterMedia *media); -void clutter_media_set_subtitle_uri (ClutterMedia *media, - const gchar *uri); -gchar * clutter_media_get_subtitle_uri (ClutterMedia *media); -void clutter_media_set_audio_volume (ClutterMedia *media, - gdouble volume); -gdouble clutter_media_get_audio_volume (ClutterMedia *media); -gboolean clutter_media_get_can_seek (ClutterMedia *media); -gdouble clutter_media_get_buffer_fill (ClutterMedia *media); -gdouble clutter_media_get_duration (ClutterMedia *media); +void clutter_media_set_playing (ClutterMedia *media, + gboolean playing); +gboolean clutter_media_get_playing (ClutterMedia *media); +void clutter_media_set_progress (ClutterMedia *media, + gdouble progress); +gdouble clutter_media_get_progress (ClutterMedia *media); +void clutter_media_set_subtitle_uri (ClutterMedia *media, + const gchar *uri); +gchar * clutter_media_get_subtitle_uri (ClutterMedia *media); +void clutter_media_set_subtitle_font_name (ClutterMedia *media, + const char *font_name); +gchar * clutter_media_get_subtitle_font_name (ClutterMedia *media); +void clutter_media_set_audio_volume (ClutterMedia *media, + gdouble volume); +gdouble clutter_media_get_audio_volume (ClutterMedia *media); +gboolean clutter_media_get_can_seek (ClutterMedia *media); +gdouble clutter_media_get_buffer_fill (ClutterMedia *media); +gdouble clutter_media_get_duration (ClutterMedia *media); G_END_DECLS diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 0da1f24bb..a1a7b7581 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -11,6 +11,8 @@ clutter_media_set_progress clutter_media_get_progress clutter_media_set_subtitle_uri clutter_media_get_subtitle_uri +clutter_media_set_subtitle_font_name +clutter_media_get_subtitle_font_name clutter_media_set_audio_volume clutter_media_get_audio_volume clutter_media_get_can_seek From 755cce33a729817aebe4cf5c08d97c31a327c863 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 30 Nov 2009 20:04:41 +0000 Subject: [PATCH 66/83] cogl: Support multiple fallbacks in cogl_offscreen_new_to_texture() The Intel drivers in Mesa 7.6 (and possibly earlier versions) don't support creating FBOs with a stencil buffer but without a depth buffer. This reworks framebuffer allocation so that we try a number of fallback options before failing. The options we try in order are: - the same options that were sucessful last time if available - combined depth and stencil - separate depth and stencil - just stencil, no depth - just depth, no stencil - neither depth or stencil --- clutter/cogl/cogl/cogl-framebuffer-private.h | 2 +- clutter/cogl/cogl/cogl-framebuffer.c | 242 ++++++++++++------- 2 files changed, 158 insertions(+), 86 deletions(-) diff --git a/clutter/cogl/cogl/cogl-framebuffer-private.h b/clutter/cogl/cogl/cogl-framebuffer-private.h index 946adfe25..1f21a670a 100644 --- a/clutter/cogl/cogl/cogl-framebuffer-private.h +++ b/clutter/cogl/cogl/cogl-framebuffer-private.h @@ -56,7 +56,7 @@ typedef struct _CoglOffscreen { CoglFramebuffer _parent; GLuint fbo_handle; - GLuint gl_stencil_handle; + GSList *renderbuffers; CoglHandle texture; } CoglOffscreen; diff --git a/clutter/cogl/cogl/cogl-framebuffer.c b/clutter/cogl/cogl/cogl-framebuffer.c index a34eaaa5c..a6b62bed8 100644 --- a/clutter/cogl/cogl/cogl-framebuffer.c +++ b/clutter/cogl/cogl/cogl-framebuffer.c @@ -71,6 +71,15 @@ #ifndef GL_STENCIL_INDEX8 #define GL_STENCIL_INDEX8 0x8D48 #endif +#ifndef GL_DEPTH_STENCIL +#define GL_DEPTH_STENCIL 0x84F9 +#endif + +typedef enum { + _TRY_DEPTH_STENCIL = 1L<<0, + _TRY_DEPTH = 1L<<1, + _TRY_STENCIL = 1L<<2 +} TryFBOFlags; static void _cogl_framebuffer_free (CoglFramebuffer *framebuffer); static void _cogl_onscreen_free (CoglOnscreen *onscreen); @@ -232,17 +241,123 @@ _cogl_framebuffer_get_projection_stack (CoglHandle handle) return framebuffer->projection_stack; } +static TryFBOFlags +try_creating_fbo (CoglOffscreen *offscreen, + TryFBOFlags flags, + CoglHandle texture) +{ + GLuint gl_depth_stencil_handle; + GLuint gl_depth_handle; + GLuint gl_stencil_handle; + GLuint tex_gl_handle; + GLenum tex_gl_target; + GLuint fbo_gl_handle; + GLenum status; + + _COGL_GET_CONTEXT (ctx, FALSE); + + if (!cogl_texture_get_gl_texture (texture, &tex_gl_handle, &tex_gl_target)) + return 0; + + if (tex_gl_target != GL_TEXTURE_2D +#ifdef HAVE_COGL_GL + && tex_gl_target != GL_TEXTURE_RECTANGLE_ARB +#endif + ) + return 0; + + /* We are about to generate and bind a new fbo, so when next flushing the + * journal, we will need to rebind the current framebuffer... */ + ctx->dirty_bound_framebuffer = 1; + + /* Generate framebuffer */ + glGenFramebuffers (1, &fbo_gl_handle); + GE (glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle)); + offscreen->fbo_handle = fbo_gl_handle; + + GE (glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + tex_gl_target, tex_gl_handle, 0)); + + if (flags & _TRY_DEPTH_STENCIL) + { + /* Create a renderbuffer for depth and stenciling */ + GE (glGenRenderbuffers (1, &gl_depth_stencil_handle)); + GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_stencil_handle)); + GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_STENCIL, + cogl_texture_get_width (texture), + cogl_texture_get_height (texture))); + GE (glBindRenderbuffer (GL_RENDERBUFFER, 0)); + GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, gl_depth_stencil_handle)); + GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, gl_depth_stencil_handle)); + offscreen->renderbuffers = + g_slist_prepend (offscreen->renderbuffers, + GUINT_TO_POINTER (gl_depth_stencil_handle)); + } + + if (flags & _TRY_DEPTH) + { + GE (glGenRenderbuffers (1, &gl_depth_handle)); + GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_handle)); + /* For now we just ask for GL_DEPTH_COMPONENT16 since this is all that's + * available under GLES */ + GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, + cogl_texture_get_width (texture), + cogl_texture_get_height (texture))); + GE (glBindRenderbuffer (GL_RENDERBUFFER, 0)); + GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, gl_depth_handle)); + offscreen->renderbuffers = + g_slist_prepend (offscreen->renderbuffers, + GUINT_TO_POINTER (gl_depth_handle)); + } + + if (flags & _TRY_STENCIL) + { + GE (glGenRenderbuffers (1, &gl_stencil_handle)); + GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle)); + GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8, + cogl_texture_get_width (texture), + cogl_texture_get_height (texture))); + GE (glBindRenderbuffer (GL_RENDERBUFFER, 0)); + GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, gl_stencil_handle)); + offscreen->renderbuffers = + g_slist_prepend (offscreen->renderbuffers, + GUINT_TO_POINTER (gl_stencil_handle)); + } + + /* Make sure it's complete */ + status = glCheckFramebufferStatus (GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + GSList *l; + + GE (glDeleteFramebuffers (1, &fbo_gl_handle)); + + for (l = offscreen->renderbuffers; l; l = l->next) + { + GLuint renderbuffer = GPOINTER_TO_UINT (l->data); + GE (glDeleteRenderbuffers (1, &renderbuffer)); + } + return 0; + } + + return flags; +} + CoglHandle cogl_offscreen_new_to_texture (CoglHandle texhandle) { - CoglOffscreen *offscreen; - int width; - int height; - GLuint tex_gl_handle; - GLenum tex_gl_target; - GLuint fbo_gl_handle; - GLuint gl_stencil_handle; - GLenum status; + CoglOffscreen *offscreen; + TryFBOFlags flags; + static TryFBOFlags last_working_flags = 0; _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); @@ -257,42 +372,6 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle) if (cogl_texture_is_sliced (texhandle)) return COGL_INVALID_HANDLE; - /* Pick the single texture slice width, height and GL id */ - - width = cogl_texture_get_width (texhandle); - height = cogl_texture_get_height (texhandle); - - if (!cogl_texture_get_gl_texture (texhandle, &tex_gl_handle, &tex_gl_target)) - return COGL_INVALID_HANDLE; - - if (tex_gl_target != GL_TEXTURE_2D -#ifdef HAVE_COGL_GL - && tex_gl_target != GL_TEXTURE_RECTANGLE_ARB -#endif - ) - return COGL_INVALID_HANDLE; - - /* Create a renderbuffer for stenciling */ - GE (glGenRenderbuffers (1, &gl_stencil_handle)); - GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle)); - GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8, - cogl_texture_get_width (texhandle), - cogl_texture_get_height (texhandle))); - GE (glBindRenderbuffer (GL_RENDERBUFFER, 0)); - - /* We are about to generate and bind a new fbo, so when next flushing the - * journal, we will need to rebind the current framebuffer... */ - ctx->dirty_bound_framebuffer = 1; - - /* Generate framebuffer */ - glGenFramebuffers (1, &fbo_gl_handle); - GE (glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle)); - GE (glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - tex_gl_target, tex_gl_handle, 0)); - GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER, - GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, gl_stencil_handle)); - /* XXX: The framebuffer_object spec isn't clear in defining whether attaching * a texture as a renderbuffer with mipmap filtering enabled while the * mipmaps have not been uploaded should result in an incomplete framebuffer @@ -305,50 +384,34 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle) */ _cogl_texture_set_filters (texhandle, GL_NEAREST, GL_NEAREST); - /* Make sure it's complete */ - status = glCheckFramebufferStatus (GL_FRAMEBUFFER); + offscreen = g_new0 (CoglOffscreen, 1); + offscreen->texture = cogl_handle_ref (texhandle); - if (status != GL_FRAMEBUFFER_COMPLETE) + if (!(last_working_flags && + (flags = try_creating_fbo (offscreen, last_working_flags, + texhandle))) && + !(flags = try_creating_fbo (offscreen, _TRY_DEPTH_STENCIL, + texhandle)) && + !(flags = try_creating_fbo (offscreen, _TRY_DEPTH | _TRY_STENCIL, + texhandle)) && + !(flags = try_creating_fbo (offscreen, _TRY_STENCIL, texhandle)) && + !(flags = try_creating_fbo (offscreen, _TRY_DEPTH, texhandle)) && + !(flags = try_creating_fbo (offscreen, 0, texhandle))) { - /* Stencil renderbuffers aren't always supported. Try again - without the stencil buffer */ - GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER, - GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, - 0)); - GE (glDeleteRenderbuffers (1, &gl_stencil_handle)); - gl_stencil_handle = 0; - - status = glCheckFramebufferStatus (GL_FRAMEBUFFER); - - if (status != GL_FRAMEBUFFER_COMPLETE) - { - /* Still failing, so give up */ - GE (glDeleteFramebuffers (1, &fbo_gl_handle)); - GE (glBindFramebuffer (GL_FRAMEBUFFER, 0)); - return COGL_INVALID_HANDLE; - } + g_free (offscreen); + last_working_flags = 0; + /* XXX: This API should probably have been defined to take a GError */ + g_warning ("%s: Failed to create an OpenGL framebuffer", G_STRLOC); + return COGL_INVALID_HANDLE; } - - offscreen = g_new0 (CoglOffscreen, 1); + /* Save the final set of flags that worked so we can hopefully construct + * subsequent buffers faster. */ + last_working_flags = flags; _cogl_framebuffer_init (COGL_FRAMEBUFFER (offscreen), COGL_FRAMEBUFFER_TYPE_OFFSCREEN, - width, - height); - - offscreen->fbo_handle = fbo_gl_handle; - offscreen->gl_stencil_handle = gl_stencil_handle; - offscreen->texture = cogl_handle_ref (texhandle); - - /* XXX: Can we get a away with removing this? It wasn't documented, and most - * users of the API are hopefully setting up the modelview from scratch - * anyway */ -#if 0 - cogl_matrix_translate (&framebuffer->modelview, -1.0f, -1.0f, 0.0f); - cogl_matrix_scale (&framebuffer->modelview, - 2.0f / framebuffer->width, 2.0f / framebuffer->height, 1.0f); -#endif + cogl_texture_get_width (texhandle), + cogl_texture_get_height (texhandle)); return _cogl_offscreen_handle_new (offscreen); } @@ -356,16 +419,25 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle) static void _cogl_offscreen_free (CoglOffscreen *offscreen) { + GSList *l; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Chain up to parent */ _cogl_framebuffer_free (COGL_FRAMEBUFFER (offscreen)); - if (offscreen->gl_stencil_handle) - GE (glDeleteRenderbuffers (1, &offscreen->gl_stencil_handle)); + for (l = offscreen->renderbuffers; l; l = l->next) + { + GLuint renderbuffer = GPOINTER_TO_UINT (l->data); + GE (glDeleteRenderbuffers (1, &renderbuffer)); + } + g_slist_free (offscreen->renderbuffers); + + GE (glDeleteFramebuffers (1, &offscreen->fbo_handle)); + if (offscreen->texture != COGL_INVALID_HANDLE) cogl_handle_unref (offscreen->texture); - GE (glDeleteFramebuffers (1, &offscreen->fbo_handle)); + g_free (offscreen); } From 5c14538c1406b1358a2747428c73fe0cf1198e94 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 11 Jan 2010 17:06:52 +0000 Subject: [PATCH 67/83] Remove obsolete code from test-text The test-text "fake scrolling" code stopped working somewhere between 0.8 and 0.9, with the new layout code. Instead of the ::cursor-event signal it should use an approach similar to the Scrollable interface in the Moblin User Experience toolkit. Right now, it makes sense to ignore this code entirely. --- tests/interactive/test-text.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/tests/interactive/test-text.c b/tests/interactive/test-text.c index d9bf879a7..e868ebae7 100644 --- a/tests/interactive/test-text.c +++ b/tests/interactive/test-text.c @@ -3,33 +3,13 @@ #include #include -#define FONT "Mono Bold 22px" +#define FONT "Mono Bold 24px" -static gchar *runes = +static const gchar *runes = "ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ\n" "ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ\n" "ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬\n"; - -static void -cursor_event (ClutterText *text, - ClutterGeometry *geometry) -{ - gint y; - - y = clutter_actor_get_y (CLUTTER_ACTOR (text)); - - if (y + geometry->y < 50) - { - clutter_actor_set_y (CLUTTER_ACTOR (text), y + 100); - } - else if (y + geometry->y > 720) - { - clutter_actor_set_y (CLUTTER_ACTOR (text), y - 100); - } - -} - G_MODULE_EXPORT gint test_text_main (gint argc, gchar **argv) @@ -78,8 +58,6 @@ test_text_main (gint argc, else clutter_text_set_text (CLUTTER_TEXT (text), runes); - g_signal_connect (text, "cursor-event", G_CALLBACK (cursor_event), NULL); - clutter_actor_set_size (stage, 1024, 768); clutter_actor_show (stage); From bc8a80fee53c9eed17fab6d821846c1a0fa3460f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 11 Jan 2010 17:08:42 +0000 Subject: [PATCH 68/83] text: Zero out the cursor_pos member Do not trust the zero-ing done by GObject on the private data structure, and use memset() instead to zero the ClutterGeometry structure. --- clutter/clutter-text.c | 1 + 1 file changed, 1 insertion(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4cf56b5ba..40c69603a 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2835,6 +2835,7 @@ clutter_text_init (ClutterText *self) priv->max_length = 0; priv->cursor_size = DEFAULT_CURSOR_SIZE; + memset (&priv->cursor_pos, 0, sizeof (ClutterGeometry)); priv->font_changed_id = g_signal_connect_swapped (clutter_get_default_backend (), From 8b950bdc87fae2626af8a96916b7f51fdea40a17 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 12 Jan 2010 11:02:09 +0000 Subject: [PATCH 69/83] journal: Fixes logging of multiple sets of texture coordinates If a user supplied multiple groups of texture coordinates with cogl_rectangle_with_multitexture_coords() then we would repeatedly log only the first group in the journal. This fixes that bug and adds a conformance test to verify the fix. Thanks to Gord Allott for reporting this bug. --- clutter/cogl/cogl/cogl-journal.c | 8 +- tests/conform/Makefile.am | 1 + tests/conform/test-cogl-multitexture.c | 209 +++++++++++++++++++++++++ tests/conform/test-conform-main.c | 1 + 4 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 tests/conform/test-cogl-multitexture.c diff --git a/clutter/cogl/cogl/cogl-journal.c b/clutter/cogl/cogl/cogl-journal.c index fbb419a90..601d53d9a 100644 --- a/clutter/cogl/cogl/cogl-journal.c +++ b/clutter/cogl/cogl/cogl-journal.c @@ -740,13 +740,13 @@ _cogl_journal_log_quad (float x_1, next_vert + POS_STRIDE + COLOR_STRIDE + TEX_STRIDE * i); - t[0] = tex_coords[0]; t[1] = tex_coords[1]; + t[0] = tex_coords[i * 4 + 0]; t[1] = tex_coords[i * 4 + 1]; t += stride; - t[0] = tex_coords[0]; t[1] = tex_coords[3]; + t[0] = tex_coords[i * 4 + 0]; t[1] = tex_coords[i * 4 + 3]; t += stride; - t[0] = tex_coords[2]; t[1] = tex_coords[3]; + t[0] = tex_coords[i * 4 + 2]; t[1] = tex_coords[i * 4 + 3]; t += stride; - t[0] = tex_coords[2]; t[1] = tex_coords[1]; + t[0] = tex_coords[i * 4 + 2]; t[1] = tex_coords[i * 4 + 1]; } if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_JOURNAL)) diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index a131ee2ee..9b743a8b8 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -24,6 +24,7 @@ test_conformance_SOURCES = \ test-cogl-viewport.c \ test-cogl-offscreen.c \ test-cogl-readpixels.c \ + test-cogl-multitexture.c \ test-path.c \ test-pick.c \ test-clutter-rectangle.c \ diff --git a/tests/conform/test-cogl-multitexture.c b/tests/conform/test-cogl-multitexture.c new file mode 100644 index 000000000..367368aad --- /dev/null +++ b/tests/conform/test-cogl-multitexture.c @@ -0,0 +1,209 @@ +#include +#include +#include + +#include "test-conform-common.h" + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +#define QUAD_WIDTH 20 + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define ALPHA 3 + +typedef struct _TestState +{ + guint frame; +} TestState; + +static void +assert_region_color (int x, + int y, + int width, + int height, + guint8 red, + guint8 green, + guint8 blue, + guint8 alpha) +{ + guint8 *data = g_malloc0 (width * height * 4); + cogl_read_pixels (x, y, width, height, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + data); + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + guint8 *pixel = &data[y * width * 4 + x * 4]; +#if 1 + g_assert (pixel[RED] == red && + pixel[GREEN] == green && + pixel[BLUE] == blue && + pixel[ALPHA] == alpha); +#endif + } + g_free (data); +} + +/* Creates a texture divided into 4 quads with colors arranged as follows: + * (The same value are used in all channels for each texel) + * + * |-----------| + * |0x11 |0x00 | + * |+ref | | + * |-----------| + * |0x00 |0x33 | + * | |+ref | + * |-----------| + * + * + */ +static CoglHandle +make_texture (guchar ref) +{ + int x; + int y; + guchar *tex_data, *p; + CoglHandle tex; + guchar val; + + tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 16); + + for (y = 0; y < QUAD_WIDTH * 2; y++) + for (x = 0; x < QUAD_WIDTH * 2; x++) + { + p = tex_data + (QUAD_WIDTH * 8 * y) + x * 4; + if (x < QUAD_WIDTH && y < QUAD_WIDTH) + val = 0x11 + ref; + else if (x >= QUAD_WIDTH && y >= QUAD_WIDTH) + val = 0x33 + ref; + else + val = 0x00; + p[0] = p[1] = p[2] = p[3] = val; + } + + /* Note: we don't use COGL_PIXEL_FORMAT_ANY for the internal format here + * since we don't want to allow Cogl to premultiply our data. */ + tex = cogl_texture_new_from_data (QUAD_WIDTH * 2, + QUAD_WIDTH * 2, + COGL_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888, + COGL_PIXEL_FORMAT_RGBA_8888, + QUAD_WIDTH * 8, + tex_data); + + g_free (tex_data); + + return tex; +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + CoglHandle tex0, tex1; + CoglHandle material; + gboolean status; + GError *error = NULL; + float tex_coords[] = { + 0, 0, 0.5, 0.5, /* tex0 */ + 0.5, 0.5, 1, 1 /* tex1 */ + }; + + /* XXX: + * We haven't always had good luck with GL drivers implementing glReadPixels + * reliably and skipping the first two frames improves our chances... */ + if (state->frame++ <= 2) + { + g_usleep (G_USEC_PER_SEC); + return; + } + + tex0 = make_texture (0x00); + tex1 = make_texture (0x11); + + material = cogl_material_new (); + + /* An arbitrary color which should be replaced by the first texture layer */ + cogl_material_set_color4ub (material, 0x80, 0x80, 0x80, 0x80); + cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL); + + cogl_material_set_layer (material, 0, tex0); + cogl_material_set_layer_combine (material, 0, + "RGBA = REPLACE (TEXTURE)", NULL); + + cogl_material_set_layer (material, 1, tex1); + status = cogl_material_set_layer_combine (material, 1, + "RGBA = ADD (PREVIOUS, TEXTURE)", + &error); + if (!status) + { + /* It's not strictly a test failure; you need a more capable GPU or + * driver to test this texture combine string. */ + g_debug ("Failed to setup texture combine string " + "RGBA = ADD (PREVIOUS, TEXTURE): %s", + error->message); + } + + cogl_set_source (material); + cogl_rectangle_with_multitexture_coords (0, 0, QUAD_WIDTH, QUAD_WIDTH, + tex_coords, 8); + cogl_material_unref (material); + + cogl_handle_unref (tex0); + cogl_handle_unref (tex1); + + /* See what we got... */ + + assert_region_color (0, 0, QUAD_WIDTH, QUAD_WIDTH, + 0x55, 0x55, 0x55, 0x55); + + /* Comment this out if you want visual feedback for what this test paints */ +#if 1 + clutter_main_quit (); +#endif +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_cogl_multitexture (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + TestState state; + ClutterActor *stage; + ClutterActor *group; + guint idle_source; + + state.frame = 0; + + stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + + group = clutter_group_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + + g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (stage); + + clutter_main (); + + g_source_remove (idle_source); + + if (g_test_verbose ()) + g_print ("OK\n"); +} diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 6b2ca0239..26c045172 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -185,6 +185,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/cogl", test_cogl_readpixels); TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_npot_texture); + TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_multitexture); TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_contiguous); TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_interleved); From 1c6ffc8a238e5e7de429f35f7653695d91d9d26d Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 12 Jan 2010 15:44:28 +0000 Subject: [PATCH 70/83] stage: Add the delete-event signal Using the ::event signal to match the CLUTTER_DELETE event type (and block the stage destruction) can be costly, since it means checking every single event. The ::delete-event signal is similar in spirit to any other specialized signal handler dealing with events, and retains the same semantics. --- clutter/clutter-main.c | 9 +------- clutter/clutter-stage.c | 49 ++++++++++++++++++++++++++++++++++++++++- clutter/clutter-stage.h | 6 ++++- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 7e9ae5e87..ea5e19d14 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2375,14 +2375,7 @@ _clutter_process_event_details (ClutterActor *stage, case CLUTTER_DELETE: event->any.source = stage; /* the stage did not handle the event, so we just quit */ - if (!clutter_stage_event (CLUTTER_STAGE (stage), event)) - { - if (stage == clutter_stage_get_default()) - clutter_main_quit (); - else - clutter_actor_destroy (stage); - } - + clutter_stage_event (CLUTTER_STAGE (stage), event); break; case CLUTTER_KEY_PRESS: diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 66b7d0eba..518d181c7 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -122,6 +122,7 @@ enum UNFULLSCREEN, ACTIVATE, DEACTIVATE, + DELETE_EVENT, LAST_SIGNAL }; @@ -625,6 +626,18 @@ clutter_stage_real_queue_redraw (ClutterActor *actor, CLUTTER_CONTEXT ()->redraw_count += 1; } +static gboolean +clutter_stage_real_delete_event (ClutterStage *stage, + ClutterEvent *event) +{ + if (clutter_stage_is_default (stage)) + clutter_main_quit (); + else + clutter_actor_destroy (CLUTTER_ACTOR (stage)); + + return TRUE; +} + static void clutter_stage_set_property (GObject *object, guint prop_id, @@ -797,7 +810,6 @@ clutter_stage_finalize (GObject *object) G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object); } - static void clutter_stage_class_init (ClutterStageClass *klass) { @@ -1050,9 +1062,41 @@ clutter_stage_class_init (ClutterStageClass *klass) clutter_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * ClutterStage::delete-event: + * @stage: the stage that received the event + * @event: a #ClutterEvent of type %CLUTTER_DELETE + * + * The ::delete-event signal is emitted when the user closes a + * #ClutterStage window using the window controls. + * + * Clutter by default will call clutter_main_quit() if @stage is + * the default stage, and clutter_actor_destroy() for any other + * stage. + * + * It is possible to override the default behaviour by connecting + * a new handler and returning %TRUE there. + * + * This signal is emitted only on Clutter backends that + * embed #ClutterStage in native windows. It is not emitted for + * backends that use a static frame buffer. + * + * Since: 1.2 + */ + stage_signals[DELETE_EVENT] = + g_signal_new (I_("delete-event"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterStageClass, delete_event), + _clutter_boolean_handled_accumulator, NULL, + clutter_marshal_BOOLEAN__BOXED, + G_TYPE_BOOLEAN, 1, + CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + klass->fullscreen = clutter_stage_real_fullscreen; klass->activate = clutter_stage_real_activate; klass->deactivate = clutter_stage_real_deactivate; + klass->delete_event = clutter_stage_real_delete_event; g_type_class_add_private (gobject_class, sizeof (ClutterStagePrivate)); } @@ -1531,6 +1575,9 @@ clutter_stage_event (ClutterStage *stage, g_signal_emit_by_name (stage, "event", event, &retval); + if (!retval) + g_signal_emit_by_name (stage, "delete-event", event, &retval); + return retval; } diff --git a/clutter/clutter-stage.h b/clutter/clutter-stage.h index f6fa0cdba..a4e3bfbb1 100644 --- a/clutter/clutter-stage.h +++ b/clutter/clutter-stage.h @@ -114,6 +114,7 @@ struct _ClutterStage * @unfullscreen: handler for the #ClutterStage::unfullscreen signal * @activate: handler for the #ClutterStage::activate signal * @deactivate: handler for the #ClutterStage::deactive signal + * @delete_event: handler for the #ClutterStage::delete-event signal * * The #ClutterStageClass structure contains only private data * @@ -132,9 +133,12 @@ struct _ClutterStageClass void (* activate) (ClutterStage *stage); void (* deactivate) (ClutterStage *stage); + gboolean (* delete_event) (ClutterStage *stage, + ClutterEvent *event); + /*< private >*/ /* padding for future expansion */ - gpointer _padding_dummy[32]; + gpointer _padding_dummy[31]; }; From bb8352ca95f18ca5582abb97e20e78f69300adde Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 12 Jan 2010 14:43:36 +0000 Subject: [PATCH 71/83] cogl: Remove the CGL_* defines These macros used to define Cogl wrappers for the GLenum values. There are now Cogl enums everywhere in the API where these were required so we shouldn't need them anymore. They were in the public headers but as they are not neccessary and were not in the API docs for Clutter 1.0 it should be safe to remove them. --- clutter/cogl/cogl/cogl-material.c | 10 +- clutter/cogl/cogl/cogl-primitives.c | 2 +- clutter/cogl/cogl/cogl-shader.h | 2 +- clutter/cogl/cogl/cogl-texture.h | 7 +- clutter/cogl/cogl/driver/gl/cogl-defines.h.in | 655 ------------------ .../cogl/cogl/driver/gl/cogl-texture-driver.c | 2 +- .../cogl/cogl/driver/gles/cogl-defines.h.in | 597 ---------------- .../cogl/driver/gles/cogl-gles2-wrapper.c | 4 +- .../cogl/driver/gles/cogl-gles2-wrapper.h | 65 +- clutter/glx/clutter-glx-texture-pixmap.c | 8 +- 10 files changed, 55 insertions(+), 1297 deletions(-) diff --git a/clutter/cogl/cogl/cogl-material.c b/clutter/cogl/cogl/cogl-material.c index 6c292ac67..00c376fb5 100644 --- a/clutter/cogl/cogl/cogl-material.c +++ b/clutter/cogl/cogl/cogl-material.c @@ -48,6 +48,12 @@ #include "../gles/cogl-gles2-wrapper.h" #endif +#ifdef HAVE_COGL_GLES +#define COGL_MATERIAL_MAX_TEXTURE_UNITS GL_MAX_TEXTURE_UNITS +#else +#define COGL_MATERIAL_MAX_TEXTURE_UNITS GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS +#endif + #ifdef HAVE_COGL_GL #define glActiveTexture ctx->drv.pf_glActiveTexture #define glClientActiveTexture ctx->drv.pf_glClientActiveTexture @@ -822,7 +828,7 @@ cogl_material_set_layer (CoglHandle material_handle, _cogl_material_pre_change_notify (material, FALSE, NULL); material->n_layers = g_list_length (material->layers); - if (material->n_layers >= CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS) + if (material->n_layers >= COGL_MATERIAL_MAX_TEXTURE_UNITS) { if (!(material->flags & COGL_MATERIAL_FLAG_SHOWN_SAMPLER_WARNING)) { @@ -1531,7 +1537,7 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, layer->flags &= ~COGL_MATERIAL_LAYER_FLAG_DIRTY; - if ((i+1) >= CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS) + if ((i+1) >= COGL_MATERIAL_MAX_TEXTURE_UNITS) break; } diff --git a/clutter/cogl/cogl/cogl-primitives.c b/clutter/cogl/cogl/cogl-primitives.c index e4fdf6a99..60a452dea 100644 --- a/clutter/cogl/cogl/cogl-primitives.c +++ b/clutter/cogl/cogl/cogl-primitives.c @@ -915,7 +915,7 @@ cogl_polygon (const CoglTextureVertex *vertices, { g_warning ("cogl_texture_polygon does not work for sliced textures " "when the minification and magnification filters are not " - "CGL_NEAREST"); + "COGL_MATERIAL_FILTER_NEAREST"); warning_seen = TRUE; } return; diff --git a/clutter/cogl/cogl/cogl-shader.h b/clutter/cogl/cogl/cogl-shader.h index 4e2ccb97b..f92b85fae 100644 --- a/clutter/cogl/cogl/cogl-shader.h +++ b/clutter/cogl/cogl/cogl-shader.h @@ -59,7 +59,7 @@ typedef enum { /** * cogl_create_shader: - * @shader_type: CGL_VERTEX_SHADER or CGL_FRAGMENT_SHADER. + * @shader_type: COGL_SHADER_TYPE_VERTEX or COGL_SHADER_TYPE_FRAGMENT. * * Create a new shader handle, use #cogl_shader_source to set the source code * to be used on it. diff --git a/clutter/cogl/cogl/cogl-texture.h b/clutter/cogl/cogl/cogl-texture.h index eaf3e5482..93ead5436 100644 --- a/clutter/cogl/cogl/cogl-texture.h +++ b/clutter/cogl/cogl/cogl-texture.h @@ -461,9 +461,10 @@ void cogl_rectangles (const float *verts, * 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. + * 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 COGL_MATERIAL_FILTER_NEAREST. * * Since: 1.0 */ diff --git a/clutter/cogl/cogl/driver/gl/cogl-defines.h.in b/clutter/cogl/cogl/driver/gl/cogl-defines.h.in index 3196095b7..e8035b054 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-defines.h.in +++ b/clutter/cogl/cogl/driver/gl/cogl-defines.h.in @@ -29,661 +29,6 @@ G_BEGIN_DECLS -/* FIXME + DOCUMENT */ - -#define COPENSTEP OPENSTEP -#define CGLAPI GLAPI -#define CGLAPI GLAPI -#define CGLAPI GLAPI -#define CGLAPIENTRY GLAPIENTRY -#define CGLAPI GLAPI -#define CGLAPIENTRY GLAPIENTRY -#define CGLAPI GLAPI -#define CGLAPIENTRY GLAPIENTRY -#define CPRAGMA_EXPORT_SUPPORTED PRAGMA_EXPORT_SUPPORTED -#define CGLAPI GLAPI -#define CGLAPIENTRY GLAPIENTRY -#define CAPIENTRY APIENTRY -#define CAPIENTRYP APIENTRYP -#define CGLAPIENTRYP GLAPIENTRYP -#define CGL_FALSE GL_FALSE -#define CGL_TRUE GL_TRUE -#define CGL_BYTE GL_BYTE -#define CGL_UNSIGNED_BYTE GL_UNSIGNED_BYTE -#define CGL_SHORT GL_SHORT -#define CGL_UNSIGNED_SHORT GL_UNSIGNED_SHORT -#define CGL_INT GL_INT -#define CGL_UNSIGNED_INT GL_UNSIGNED_INT -#define CGL_FLOAT GL_FLOAT -#define CGL_DOUBLE GL_DOUBLE -#define CGL_POINTS GL_POINTS -#define CGL_LINES GL_LINES -#define CGL_LINE_LOOP GL_LINE_LOOP -#define CGL_LINE_STRIP GL_LINE_STRIP -#define CGL_TRIANGLES GL_TRIANGLES -#define CGL_TRIANGLE_STRIP GL_TRIANGLE_STRIP -#define CGL_TRIANGLE_FAN GL_TRIANGLE_FAN -#define CGL_QUADS GL_QUADS -#define CGL_QUAD_STRIP GL_QUAD_STRIP -#define CGL_POLYGON GL_POLYGON -#define CGL_VERTEX_ARRAY GL_VERTEX_ARRAY -#define CGL_NORMAL_ARRAY GL_NORMAL_ARRAY -#define CGL_COLOR_ARRAY GL_COLOR_ARRAY -#define CGL_INDEX_ARRAY GL_INDEX_ARRAY -#define CGL_TEXTURE_COORD_ARRAY GL_TEXTURE_COORD_ARRAY -#define CGL_EDGE_FLAG_ARRAY GL_EDGE_FLAG_ARRAY -#define CGL_VERTEX_ARRAY_SIZE GL_VERTEX_ARRAY_SIZE -#define CGL_VERTEX_ARRAY_TYPE GL_VERTEX_ARRAY_TYPE -#define CGL_VERTEX_ARRAY_STRIDE GL_VERTEX_ARRAY_STRIDE -#define CGL_NORMAL_ARRAY_TYPE GL_NORMAL_ARRAY_TYPE -#define CGL_NORMAL_ARRAY_STRIDE GL_NORMAL_ARRAY_STRIDE -#define CGL_COLOR_ARRAY_SIZE GL_COLOR_ARRAY_SIZE -#define CGL_COLOR_ARRAY_TYPE GL_COLOR_ARRAY_TYPE -#define CGL_COLOR_ARRAY_STRIDE GL_COLOR_ARRAY_STRIDE -#define CGL_INDEX_ARRAY_TYPE GL_INDEX_ARRAY_TYPE -#define CGL_INDEX_ARRAY_STRIDE GL_INDEX_ARRAY_STRIDE -#define CGL_TEXTURE_COORD_ARRAY_SIZE GL_TEXTURE_COORD_ARRAY_SIZE -#define CGL_TEXTURE_COORD_ARRAY_TYPE GL_TEXTURE_COORD_ARRAY_TYPE -#define CGL_TEXTURE_COORD_ARRAY_STRIDE GL_TEXTURE_COORD_ARRAY_STRIDE -#define CGL_EDGE_FLAG_ARRAY_STRIDE GL_EDGE_FLAG_ARRAY_STRIDE -#define CGL_VERTEX_ARRAY_POINTER GL_VERTEX_ARRAY_POINTER -#define CGL_NORMAL_ARRAY_POINTER GL_NORMAL_ARRAY_POINTER -#define CGL_COLOR_ARRAY_POINTER GL_COLOR_ARRAY_POINTER -#define CGL_INDEX_ARRAY_POINTER GL_INDEX_ARRAY_POINTER -#define CGL_TEXTURE_COORD_ARRAY_POINTER GL_TEXTURE_COORD_ARRAY_POINTER -#define CGL_EDGE_FLAG_ARRAY_POINTER GL_EDGE_FLAG_ARRAY_POINTER -#define CGL_MATRIX_MODE GL_MATRIX_MODE -#define CGL_MODELVIEW GL_MODELVIEW -#define CGL_PROJECTION GL_PROJECTION -#define CGL_TEXTURE GL_TEXTURE -#define CGL_POINT_SMOOTH GL_POINT_SMOOTH -#define CGL_POINT_SIZE GL_POINT_SIZE -#define CGL_POINT_SIZE_GRANULARITY GL_POINT_SIZE_GRANULARITY -#define CGL_POINT_SIZE_RANGE GL_POINT_SIZE_RANGE -#define CGL_LINE_SMOOTH GL_LINE_SMOOTH -#define CGL_LINE_STIPPLE GL_LINE_STIPPLE -#define CGL_LINE_STIPPLE_PATTERN GL_LINE_STIPPLE_PATTERN -#define CGL_LINE_STIPPLE_REPEAT GL_LINE_STIPPLE_REPEAT -#define CGL_LINE_WIDTH GL_LINE_WIDTH -#define CGL_LINE_WIDTH_GRANULARITY GL_LINE_WIDTH_GRANULARITY -#define CGL_LINE_WIDTH_RANGE GL_LINE_WIDTH_RANGE -#define CGL_POINT GL_POINT -#define CGL_LINE GL_LINE -#define CGL_FILL GL_FILL -#define CGL_CW GL_CW -#define CGL_CCW GL_CCW -#define CGL_FRONT GL_FRONT -#define CGL_BACK GL_BACK -#define CGL_POLYGON_MODE GL_POLYGON_MODE -#define CGL_POLYGON_SMOOTH GL_POLYGON_SMOOTH -#define CGL_POLYGON_STIPPLE GL_POLYGON_STIPPLE -#define CGL_EDGE_FLAG GL_EDGE_FLAG -#define CGL_CULL_FACE GL_CULL_FACE -#define CGL_CULL_FACE_MODE GL_CULL_FACE_MODE -#define CGL_FRONT_FACE GL_FRONT_FACE -#define CGL_POLYGON_OFFSET_FACTOR GL_POLYGON_OFFSET_FACTOR -#define CGL_POLYGON_OFFSET_UNITS GL_POLYGON_OFFSET_UNITS -#define CGL_POLYGON_OFFSET_POINT GL_POLYGON_OFFSET_POINT -#define CGL_POLYGON_OFFSET_LINE GL_POLYGON_OFFSET_LINE -#define CGL_POLYGON_OFFSET_FILL GL_POLYGON_OFFSET_FILL -#define CGL_COMPILE GL_COMPILE -#define CGL_COMPILE_AND_EXECUTE GL_COMPILE_AND_EXECUTE -#define CGL_LIST_BASE GL_LIST_BASE -#define CGL_LIST_INDEX GL_LIST_INDEX -#define CGL_LIST_MODE GL_LIST_MODE -#define CGL_NEVER GL_NEVER -#define CGL_LESS GL_LESS -#define CGL_EQUAL GL_EQUAL -#define CGL_LEQUAL GL_LEQUAL -#define CGL_GREATER GL_GREATER -#define CGL_NOTEQUAL GL_NOTEQUAL -#define CGL_GEQUAL GL_GEQUAL -#define CGL_ALWAYS GL_ALWAYS -#define CGL_DEPTH_TEST GL_DEPTH_TEST -#define CGL_DEPTH_BITS GL_DEPTH_BITS -#define CGL_DEPTH_CLEAR_VALUE GL_DEPTH_CLEAR_VALUE -#define CGL_DEPTH_FUNC GL_DEPTH_FUNC -#define CGL_DEPTH_RANGE GL_DEPTH_RANGE -#define CGL_DEPTH_WRITEMASK GL_DEPTH_WRITEMASK -#define CGL_DEPTH_COMPONENT GL_DEPTH_COMPONENT -#define CGL_LIGHTING GL_LIGHTING -#define CGL_SPOT_EXPONENT GL_SPOT_EXPONENT -#define CGL_SPOT_CUTOFF GL_SPOT_CUTOFF -#define CGL_CONSTANT_ATTENUATION GL_CONSTANT_ATTENUATION -#define CGL_LINEAR_ATTENUATION GL_LINEAR_ATTENUATION -#define CGL_QUADRATIC_ATTENUATION GL_QUADRATIC_ATTENUATION -#define CGL_AMBIENT GL_AMBIENT -#define CGL_DIFFUSE GL_DIFFUSE -#define CGL_SPECULAR GL_SPECULAR -#define CGL_SHININESS GL_SHININESS -#define CGL_EMISSION GL_EMISSION -#define CGL_POSITION GL_POSITION -#define CGL_SPOT_DIRECTION GL_SPOT_DIRECTION -#define CGL_AMBIENT_AND_DIFFUSE GL_AMBIENT_AND_DIFFUSE -#define CGL_COLOR_INDEXES GL_COLOR_INDEXES -#define CGL_LIGHT_MODEL_TWO_SIDE GL_LIGHT_MODEL_TWO_SIDE -#define CGL_LIGHT_MODEL_LOCAL_VIEWER GL_LIGHT_MODEL_LOCAL_VIEWER -#define CGL_LIGHT_MODEL_AMBIENT GL_LIGHT_MODEL_AMBIENT -#define CGL_FRONT_AND_BACK GL_FRONT_AND_BACK -#define CGL_SHADE_MODEL GL_SHADE_MODEL -#define CGL_FLAT GL_FLAT -#define CGL_SMOOTH GL_SMOOTH -#define CGL_COLOR_MATERIAL GL_COLOR_MATERIAL -#define CGL_COLOR_MATERIAL_FACE GL_COLOR_MATERIAL_FACE -#define CGL_COLOR_MATERIAL_PARAMETER GL_COLOR_MATERIAL_PARAMETER -#define CGL_NORMALIZE GL_NORMALIZE -#define CGL_ACCUM_RED_BITS GL_ACCUM_RED_BITS -#define CGL_ACCUM_GREEN_BITS GL_ACCUM_GREEN_BITS -#define CGL_ACCUM_BLUE_BITS GL_ACCUM_BLUE_BITS -#define CGL_ACCUM_ALPHA_BITS GL_ACCUM_ALPHA_BITS -#define CGL_ACCUM_CLEAR_VALUE GL_ACCUM_CLEAR_VALUE -#define CGL_ACCUM GL_ACCUM -#define CGL_ADD GL_ADD -#define CGL_LOAD GL_LOAD -#define CGL_MULT GL_MULT -#define CGL_RETURN GL_RETURN -#define CGL_ALPHA_TEST GL_ALPHA_TEST -#define CGL_ALPHA_TEST_REF GL_ALPHA_TEST_REF -#define CGL_ALPHA_TEST_FUNC GL_ALPHA_TEST_FUNC -#define CGL_BLEND GL_BLEND -#define CGL_BLEND_SRC GL_BLEND_SRC -#define CGL_BLEND_DST GL_BLEND_DST -#define CGL_ZERO GL_ZERO -#define CGL_ONE GL_ONE -#define CGL_SRC_COLOR GL_SRC_COLOR -#define CGL_ONE_MINUS_SRC_COLOR GL_ONE_MINUS_SRC_COLOR -#define CGL_SRC_ALPHA GL_SRC_ALPHA -#define CGL_ONE_MINUS_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA -#define CGL_DST_ALPHA GL_DST_ALPHA -#define CGL_ONE_MINUS_DST_ALPHA GL_ONE_MINUS_DST_ALPHA -#define CGL_DST_COLOR GL_DST_COLOR -#define CGL_ONE_MINUS_DST_COLOR GL_ONE_MINUS_DST_COLOR -#define CGL_SRC_ALPHA_SATURATE GL_SRC_ALPHA_SATURATE -#define CGL_FEEDBACK GL_FEEDBACK -#define CGL_RENDER GL_RENDER -#define CGL_SELECT GL_SELECT -#define CGL_POINT_TOKEN GL_POINT_TOKEN -#define CGL_LINE_TOKEN GL_LINE_TOKEN -#define CGL_LINE_RESET_TOKEN GL_LINE_RESET_TOKEN -#define CGL_POLYGON_TOKEN GL_POLYGON_TOKEN -#define CGL_BITMAP_TOKEN GL_BITMAP_TOKEN -#define CGL_DRAW_PIXEL_TOKEN GL_DRAW_PIXEL_TOKEN -#define CGL_COPY_PIXEL_TOKEN GL_COPY_PIXEL_TOKEN -#define CGL_PASS_THROUGH_TOKEN GL_PASS_THROUGH_TOKEN -#define CGL_FEEDBACK_BUFFER_POINTER GL_FEEDBACK_BUFFER_POINTER -#define CGL_FEEDBACK_BUFFER_SIZE GL_FEEDBACK_BUFFER_SIZE -#define CGL_FEEDBACK_BUFFER_TYPE GL_FEEDBACK_BUFFER_TYPE -#define CGL_SELECTION_BUFFER_POINTER GL_SELECTION_BUFFER_POINTER -#define CGL_SELECTION_BUFFER_SIZE GL_SELECTION_BUFFER_SIZE -#define CGL_FOG GL_FOG -#define CGL_FOG_MODE GL_FOG_MODE -#define CGL_FOG_DENSITY GL_FOG_DENSITY -#define CGL_FOG_COLOR GL_FOG_COLOR -#define CGL_FOG_INDEX GL_FOG_INDEX -#define CGL_FOG_START GL_FOG_START -#define CGL_FOG_END GL_FOG_END -#define CGL_EXP GL_EXP -#define CGL_LOGIC_OP GL_LOGIC_OP -#define CGL_INDEX_LOGIC_OP GL_INDEX_LOGIC_OP -#define CGL_COLOR_LOGIC_OP GL_COLOR_LOGIC_OP -#define CGL_LOGIC_OP_MODE GL_LOGIC_OP_MODE -#define CGL_CLEAR GL_CLEAR -#define CGL_SET GL_SET -#define CGL_COPY GL_COPY -#define CGL_COPY_INVERTED GL_COPY_INVERTED -#define CGL_NOOP GL_NOOP -#define CGL_INVERT GL_INVERT -#define CGL_AND GL_AND -#define CGL_NAND GL_NAND -#define CGL_OR GL_OR -#define CGL_NOR GL_NOR -#define CGL_XOR GL_XOR -#define CGL_EQUIV GL_EQUIV -#define CGL_AND_REVERSE GL_AND_REVERSE -#define CGL_AND_INVERTED GL_AND_INVERTED -#define CGL_OR_REVERSE GL_OR_REVERSE -#define CGL_OR_INVERTED GL_OR_INVERTED -#define CGL_STENCIL_BITS GL_STENCIL_BITS -#define CGL_STENCIL_TEST GL_STENCIL_TEST -#define CGL_STENCIL_CLEAR_VALUE GL_STENCIL_CLEAR_VALUE -#define CGL_STENCIL_FUNC GL_STENCIL_FUNC -#define CGL_STENCIL_VALUE_MASK GL_STENCIL_VALUE_MASK -#define CGL_STENCIL_FAIL GL_STENCIL_FAIL -#define CGL_STENCIL_PASS_DEPTH_FAIL GL_STENCIL_PASS_DEPTH_FAIL -#define CGL_STENCIL_PASS_DEPTH_PASS GL_STENCIL_PASS_DEPTH_PASS -#define CGL_STENCIL_REF GL_STENCIL_REF -#define CGL_STENCIL_WRITEMASK GL_STENCIL_WRITEMASK -#define CGL_STENCIL_INDEX GL_STENCIL_INDEX -#define CGL_KEEP GL_KEEP -#define CGL_REPLACE GL_REPLACE -#define CGL_INCR GL_INCR -#define CGL_DECR GL_DECR -#define CGL_NONE GL_NONE -#define CGL_LEFT GL_LEFT -#define CGL_RIGHT GL_RIGHT -#define CGL_FRONT_LEFT GL_FRONT_LEFT -#define CGL_FRONT_RIGHT GL_FRONT_RIGHT -#define CGL_BACK_LEFT GL_BACK_LEFT -#define CGL_BACK_RIGHT GL_BACK_RIGHT -#define CGL_COLOR_INDEX GL_COLOR_INDEX -#define CGL_RED GL_RED -#define CGL_GREEN GL_GREEN -#define CGL_BLUE GL_BLUE -#define CGL_ALPHA GL_ALPHA -#define CGL_LUMINANCE GL_LUMINANCE -#define CGL_LUMINANCE_ALPHA GL_LUMINANCE_ALPHA -#define CGL_ALPHA_BITS GL_ALPHA_BITS -#define CGL_RED_BITS GL_RED_BITS -#define CGL_GREEN_BITS GL_GREEN_BITS -#define CGL_BLUE_BITS GL_BLUE_BITS -#define CGL_INDEX_BITS GL_INDEX_BITS -#define CGL_SUBPIXEL_BITS GL_SUBPIXEL_BITS -#define CGL_AUX_BUFFERS GL_AUX_BUFFERS -#define CGL_READ_BUFFER GL_READ_BUFFER -#define CGL_DRAW_BUFFER GL_DRAW_BUFFER -#define CGL_DOUBLEBUFFER GL_DOUBLEBUFFER -#define CGL_STEREO GL_STEREO -#define CGL_BITMAP GL_BITMAP -#define CGL_COLOR GL_COLOR -#define CGL_DEPTH GL_DEPTH -#define CGL_STENCIL GL_STENCIL -#define CGL_DITHER GL_DITHER -#define CGL_RGB GL_RGB -#define CGL_RGBA GL_RGBA -#define CGL_MAX_LIST_NESTING GL_MAX_LIST_NESTING -#define CGL_MAX_EVAL_ORDER GL_MAX_EVAL_ORDER -#define CGL_MAX_LIGHTS GL_MAX_LIGHTS -#define CGL_MAX_CLIP_PLANES GL_MAX_CLIP_PLANES -#define CGL_MAX_TEXTURE_SIZE GL_MAX_TEXTURE_SIZE -#define CGL_MAX_PIXEL_MAP_TABLE GL_MAX_PIXEL_MAP_TABLE -#define CGL_MAX_ATTRIB_STACK_DEPTH GL_MAX_ATTRIB_STACK_DEPTH -#define CGL_MAX_MODELVIEW_STACK_DEPTH GL_MAX_MODELVIEW_STACK_DEPTH -#define CGL_MAX_NAME_STACK_DEPTH GL_MAX_NAME_STACK_DEPTH -#define CGL_MAX_PROJECTION_STACK_DEPTH GL_MAX_PROJECTION_STACK_DEPTH -#define CGL_MAX_TEXTURE_STACK_DEPTH GL_MAX_TEXTURE_STACK_DEPTH -#define CGL_MAX_VIEWPORT_DIMS GL_MAX_VIEWPORT_DIMS -#define CGL_MAX_CLIENT_ATTRIB_STACK_DEPTH GL_MAX_CLIENT_ATTRIB_STACK_DEPTH -#define CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS GL_MAX_TEXTURE_UNITS -#define CGL_ATTRIB_STACK_DEPTH GL_ATTRIB_STACK_DEPTH -#define CGL_CLIENT_ATTRIB_STACK_DEPTH GL_CLIENT_ATTRIB_STACK_DEPTH -#define CGL_COLOR_CLEAR_VALUE GL_COLOR_CLEAR_VALUE -#define CGL_COLOR_WRITEMASK GL_COLOR_WRITEMASK -#define CGL_CURRENT_INDEX GL_CURRENT_INDEX -#define CGL_CURRENT_COLOR GL_CURRENT_COLOR -#define CGL_CURRENT_NORMAL GL_CURRENT_NORMAL -#define CGL_CURRENT_RASTER_COLOR GL_CURRENT_RASTER_COLOR -#define CGL_CURRENT_RASTER_DISTANCE GL_CURRENT_RASTER_DISTANCE -#define CGL_CURRENT_RASTER_INDEX GL_CURRENT_RASTER_INDEX -#define CGL_CURRENT_RASTER_POSITION GL_CURRENT_RASTER_POSITION -#define CGL_CURRENT_RASTER_TEXTURE_COORDS GL_CURRENT_RASTER_TEXTURE_COORDS -#define CGL_CURRENT_RASTER_POSITION_VALID GL_CURRENT_RASTER_POSITION_VALID -#define CGL_CURRENT_TEXTURE_COORDS GL_CURRENT_TEXTURE_COORDS -#define CGL_INDEX_CLEAR_VALUE GL_INDEX_CLEAR_VALUE -#define CGL_INDEX_MODE GL_INDEX_MODE -#define CGL_INDEX_WRITEMASK GL_INDEX_WRITEMASK -#define CGL_MODELVIEW_MATRIX GL_MODELVIEW_MATRIX -#define CGL_MODELVIEW_STACK_DEPTH GL_MODELVIEW_STACK_DEPTH -#define CGL_NAME_STACK_DEPTH GL_NAME_STACK_DEPTH -#define CGL_PROJECTION_MATRIX GL_PROJECTION_MATRIX -#define CGL_PROJECTION_STACK_DEPTH GL_PROJECTION_STACK_DEPTH -#define CGL_RENDER_MODE GL_RENDER_MODE -#define CGL_RGBA_MODE GL_RGBA_MODE -#define CGL_TEXTURE_MATRIX GL_TEXTURE_MATRIX -#define CGL_TEXTURE_STACK_DEPTH GL_TEXTURE_STACK_DEPTH -#define CGL_VIEWPORT GL_VIEWPORT -#define CGL_AUTO_NORMAL GL_AUTO_NORMAL -#define CGL_COEFF GL_COEFF -#define CGL_ORDER GL_ORDER -#define CGL_DOMAIN GL_DOMAIN -#define CGL_PERSPECTIVE_CORRECTION_HINT GL_PERSPECTIVE_CORRECTION_HINT -#define CGL_POINT_SMOOTH_HINT GL_POINT_SMOOTH_HINT -#define CGL_LINE_SMOOTH_HINT GL_LINE_SMOOTH_HINT -#define CGL_POLYGON_SMOOTH_HINT GL_POLYGON_SMOOTH_HINT -#define CGL_FOG_HINT GL_FOG_HINT -#define CGL_DONT_CARE GL_DONT_CARE -#define CGL_FASTEST GL_FASTEST -#define CGL_NICEST GL_NICEST -#define CGL_SCISSOR_BOX GL_SCISSOR_BOX -#define CGL_SCISSOR_TEST GL_SCISSOR_TEST -#define CGL_MAP_COLOR GL_MAP_COLOR -#define CGL_MAP_STENCIL GL_MAP_STENCIL -#define CGL_INDEX_SHIFT GL_INDEX_SHIFT -#define CGL_INDEX_OFFSET GL_INDEX_OFFSET -#define CGL_RED_SCALE GL_RED_SCALE -#define CGL_RED_BIAS GL_RED_BIAS -#define CGL_GREEN_SCALE GL_GREEN_SCALE -#define CGL_GREEN_BIAS GL_GREEN_BIAS -#define CGL_BLUE_SCALE GL_BLUE_SCALE -#define CGL_BLUE_BIAS GL_BLUE_BIAS -#define CGL_ALPHA_SCALE GL_ALPHA_SCALE -#define CGL_ALPHA_BIAS GL_ALPHA_BIAS -#define CGL_DEPTH_SCALE GL_DEPTH_SCALE -#define CGL_DEPTH_BIAS GL_DEPTH_BIAS -#define CGL_PIXEL_MAP_S_TO_S_SIZE GL_PIXEL_MAP_S_TO_S_SIZE -#define CGL_PIXEL_MAP_I_TO_I_SIZE GL_PIXEL_MAP_I_TO_I_SIZE -#define CGL_PIXEL_MAP_I_TO_R_SIZE GL_PIXEL_MAP_I_TO_R_SIZE -#define CGL_PIXEL_MAP_I_TO_G_SIZE GL_PIXEL_MAP_I_TO_G_SIZE -#define CGL_PIXEL_MAP_I_TO_B_SIZE GL_PIXEL_MAP_I_TO_B_SIZE -#define CGL_PIXEL_MAP_I_TO_A_SIZE GL_PIXEL_MAP_I_TO_A_SIZE -#define CGL_PIXEL_MAP_R_TO_R_SIZE GL_PIXEL_MAP_R_TO_R_SIZE -#define CGL_PIXEL_MAP_G_TO_G_SIZE GL_PIXEL_MAP_G_TO_G_SIZE -#define CGL_PIXEL_MAP_B_TO_B_SIZE GL_PIXEL_MAP_B_TO_B_SIZE -#define CGL_PIXEL_MAP_A_TO_A_SIZE GL_PIXEL_MAP_A_TO_A_SIZE -#define CGL_PIXEL_MAP_S_TO_S GL_PIXEL_MAP_S_TO_S -#define CGL_PIXEL_MAP_I_TO_I GL_PIXEL_MAP_I_TO_I -#define CGL_PIXEL_MAP_I_TO_R GL_PIXEL_MAP_I_TO_R -#define CGL_PIXEL_MAP_I_TO_G GL_PIXEL_MAP_I_TO_G -#define CGL_PIXEL_MAP_I_TO_B GL_PIXEL_MAP_I_TO_B -#define CGL_PIXEL_MAP_I_TO_A GL_PIXEL_MAP_I_TO_A -#define CGL_PIXEL_MAP_R_TO_R GL_PIXEL_MAP_R_TO_R -#define CGL_PIXEL_MAP_G_TO_G GL_PIXEL_MAP_G_TO_G -#define CGL_PIXEL_MAP_B_TO_B GL_PIXEL_MAP_B_TO_B -#define CGL_PIXEL_MAP_A_TO_A GL_PIXEL_MAP_A_TO_A -#define CGL_PACK_ALIGNMENT GL_PACK_ALIGNMENT -#define CGL_PACK_LSB_FIRST GL_PACK_LSB_FIRST -#define CGL_PACK_ROW_LENGTH GL_PACK_ROW_LENGTH -#define CGL_PACK_SKIP_PIXELS GL_PACK_SKIP_PIXELS -#define CGL_PACK_SKIP_ROWS GL_PACK_SKIP_ROWS -#define CGL_PACK_SWAP_BYTES GL_PACK_SWAP_BYTES -#define CGL_UNPACK_ALIGNMENT GL_UNPACK_ALIGNMENT -#define CGL_UNPACK_LSB_FIRST GL_UNPACK_LSB_FIRST -#define CGL_UNPACK_ROW_LENGTH GL_UNPACK_ROW_LENGTH -#define CGL_UNPACK_SKIP_PIXELS GL_UNPACK_SKIP_PIXELS -#define CGL_UNPACK_SKIP_ROWS GL_UNPACK_SKIP_ROWS -#define CGL_UNPACK_SWAP_BYTES GL_UNPACK_SWAP_BYTES -#define CGL_ZOOM_X GL_ZOOM_X -#define CGL_ZOOM_Y GL_ZOOM_Y -#define CGL_TEXTURE_ENV GL_TEXTURE_ENV -#define CGL_TEXTURE_ENV_MODE GL_TEXTURE_ENV_MODE -#define CGL_TEXTURE_WRAP_S GL_TEXTURE_WRAP_S -#define CGL_TEXTURE_WRAP_T GL_TEXTURE_WRAP_T -#define CGL_TEXTURE_MAG_FILTER GL_TEXTURE_MAG_FILTER -#define CGL_TEXTURE_MIN_FILTER GL_TEXTURE_MIN_FILTER -#define CGL_TEXTURE_ENV_COLOR GL_TEXTURE_ENV_COLOR -#define CGL_TEXTURE_GEN_S GL_TEXTURE_GEN_S -#define CGL_TEXTURE_GEN_T GL_TEXTURE_GEN_T -#define CGL_TEXTURE_GEN_MODE GL_TEXTURE_GEN_MODE -#define CGL_TEXTURE_BORDER_COLOR GL_TEXTURE_BORDER_COLOR -#define CGL_TEXTURE_WIDTH GL_TEXTURE_WIDTH -#define CGL_TEXTURE_HEIGHT GL_TEXTURE_HEIGHT -#define CGL_TEXTURE_BORDER GL_TEXTURE_BORDER -#define CGL_TEXTURE_COMPONENTS GL_TEXTURE_COMPONENTS -#define CGL_TEXTURE_RED_SIZE GL_TEXTURE_RED_SIZE -#define CGL_TEXTURE_GREEN_SIZE GL_TEXTURE_GREEN_SIZE -#define CGL_TEXTURE_BLUE_SIZE GL_TEXTURE_BLUE_SIZE -#define CGL_TEXTURE_ALPHA_SIZE GL_TEXTURE_ALPHA_SIZE -#define CGL_TEXTURE_LUMINANCE_SIZE GL_TEXTURE_LUMINANCE_SIZE -#define CGL_TEXTURE_INTENSITY_SIZE GL_TEXTURE_INTENSITY_SIZE -#define CGL_OBJECT_LINEAR GL_OBJECT_LINEAR -#define CGL_OBJECT_PLANE GL_OBJECT_PLANE -#define CGL_EYE_LINEAR GL_EYE_LINEAR -#define CGL_EYE_PLANE GL_EYE_PLANE -#define CGL_SPHERE_MAP GL_SPHERE_MAP -#define CGL_DECAL GL_DECAL -#define CGL_MODULATE GL_MODULATE -#define CGL_REPEAT GL_REPEAT -#define CGL_CLAMP GL_CLAMP -#define CGL_S GL_S -#define CGL_T GL_T -#define CGL_R GL_R -#define CGL_Q GL_Q -#define CGL_TEXTURE_GEN_R GL_TEXTURE_GEN_R -#define CGL_TEXTURE_GEN_Q GL_TEXTURE_GEN_Q -#define CGL_VENDOR GL_VENDOR -#define CGL_RENDERER GL_RENDERER -#define CGL_VERSION GL_VERSION -#define CGL_EXTENSIONS GL_EXTENSIONS -#define CGL_NO_ERROR GL_NO_ERROR -#define CGL_INVALID_ENUM GL_INVALID_ENUM -#define CGL_INVALID_VALUE GL_INVALID_VALUE -#define CGL_INVALID_OPERATION GL_INVALID_OPERATION -#define CGL_STACK_OVERFLOW GL_STACK_OVERFLOW -#define CGL_STACK_UNDERFLOW GL_STACK_UNDERFLOW -#define CGL_OUT_OF_MEMORY GL_OUT_OF_MEMORY -#define CGL_CURRENT_BIT GL_CURRENT_BIT -#define CGL_POINT_BIT GL_POINT_BIT -#define CGL_LINE_BIT GL_LINE_BIT -#define CGL_POLYGON_BIT GL_POLYGON_BIT -#define CGL_POLYGON_STIPPLE_BIT GL_POLYGON_STIPPLE_BIT -#define CGL_PIXEL_MODE_BIT GL_PIXEL_MODE_BIT -#define CGL_LIGHTING_BIT GL_LIGHTING_BIT -#define CGL_FOG_BIT GL_FOG_BIT -#define CGL_DEPTH_BUFFER_BIT GL_DEPTH_BUFFER_BIT -#define CGL_ACCUM_BUFFER_BIT GL_ACCUM_BUFFER_BIT -#define CGL_STENCIL_BUFFER_BIT GL_STENCIL_BUFFER_BIT -#define CGL_VIEWPORT_BIT GL_VIEWPORT_BIT -#define CGL_TRANSFORM_BIT GL_TRANSFORM_BIT -#define CGL_ENABLE_BIT GL_ENABLE_BIT -#define CGL_COLOR_BUFFER_BIT GL_COLOR_BUFFER_BIT -#define CGL_HINT_BIT GL_HINT_BIT -#define CGL_EVAL_BIT GL_EVAL_BIT -#define CGL_LIST_BIT GL_LIST_BIT -#define CGL_TEXTURE_BIT GL_TEXTURE_BIT -#define CGL_SCISSOR_BIT GL_SCISSOR_BIT -#define CGL_ALL_ATTRIB_BITS GL_ALL_ATTRIB_BITS -#define CGL_TEXTURE_PRIORITY GL_TEXTURE_PRIORITY -#define CGL_TEXTURE_RESIDENT GL_TEXTURE_RESIDENT -#define CGL_TEXTURE_INTERNAL_FORMAT GL_TEXTURE_INTERNAL_FORMAT -#define CGL_INTENSITY GL_INTENSITY -#define CGL_CLIENT_PIXEL_STORE_BIT GL_CLIENT_PIXEL_STORE_BIT -#define CGL_CLIENT_VERTEX_ARRAY_BIT GL_CLIENT_VERTEX_ARRAY_BIT -#define CGL_ALL_CLIENT_ATTRIB_BITS GL_ALL_CLIENT_ATTRIB_BITS -#define CGL_CLIENT_ALL_ATTRIB_BITS GL_CLIENT_ALL_ATTRIB_BITS -#define CGL_RESCALE_NORMAL GL_RESCALE_NORMAL -#define CGL_CLAMP_TO_EDGE GL_CLAMP_TO_EDGE -#define CGL_MAX_ELEMENTS_VERTICES GL_MAX_ELEMENTS_VERTICES -#define CGL_MAX_ELEMENTS_INDICES GL_MAX_ELEMENTS_INDICES -#define CGL_BGR GL_BGR -#define CGL_BGRA GL_BGRA -#define CGL_LIGHT_MODEL_COLOR_CONTROL GL_LIGHT_MODEL_COLOR_CONTROL -#define CGL_SINGLE_COLOR GL_SINGLE_COLOR -#define CGL_SEPARATE_SPECULAR_COLOR GL_SEPARATE_SPECULAR_COLOR -#define CGL_TEXTURE_MIN_LOD GL_TEXTURE_MIN_LOD -#define CGL_TEXTURE_MAX_LOD GL_TEXTURE_MAX_LOD -#define CGL_TEXTURE_BASE_LEVEL GL_TEXTURE_BASE_LEVEL -#define CGL_TEXTURE_MAX_LEVEL GL_TEXTURE_MAX_LEVEL -#define CGL_SMOOTH_POINT_SIZE_RANGE GL_SMOOTH_POINT_SIZE_RANGE -#define CGL_SMOOTH_POINT_SIZE_GRANULARITY GL_SMOOTH_POINT_SIZE_GRANULARITY -#define CGL_SMOOTH_LINE_WIDTH_RANGE GL_SMOOTH_LINE_WIDTH_RANGE -#define CGL_SMOOTH_LINE_WIDTH_GRANULARITY GL_SMOOTH_LINE_WIDTH_GRANULARITY -#define CGL_ALIASED_POINT_SIZE_RANGE GL_ALIASED_POINT_SIZE_RANGE -#define CGL_ALIASED_LINE_WIDTH_RANGE GL_ALIASED_LINE_WIDTH_RANGE -#define CGL_PACK_SKIP_IMAGES GL_PACK_SKIP_IMAGES -#define CGL_PACK_IMAGE_HEIGHT GL_PACK_IMAGE_HEIGHT -#define CGL_UNPACK_SKIP_IMAGES GL_UNPACK_SKIP_IMAGES -#define CGL_UNPACK_IMAGE_HEIGHT GL_UNPACK_IMAGE_HEIGHT -#define CGL_TEXTURE_DEPTH GL_TEXTURE_DEPTH -#define CGL_TEXTURE_WRAP_R GL_TEXTURE_WRAP_R -#define CGL_CONSTANT_COLOR GL_CONSTANT_COLOR -#define CGL_ONE_MINUS_CONSTANT_COLOR GL_ONE_MINUS_CONSTANT_COLOR -#define CGL_CONSTANT_ALPHA GL_CONSTANT_ALPHA -#define CGL_ONE_MINUS_CONSTANT_ALPHA GL_ONE_MINUS_CONSTANT_ALPHA -#define CGL_COLOR_TABLE GL_COLOR_TABLE -#define CGL_POST_CONVOLUTION_COLOR_TABLE GL_POST_CONVOLUTION_COLOR_TABLE -#define CGL_POST_COLOR_MATRIX_COLOR_TABLE GL_POST_COLOR_MATRIX_COLOR_TABLE -#define CGL_PROXY_COLOR_TABLE GL_PROXY_COLOR_TABLE -#define CGL_PROXY_POST_CONVOLUTION_COLOR_TABLE GL_PROXY_POST_CONVOLUTION_COLOR_TABLE -#define CGL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE -#define CGL_COLOR_TABLE_SCALE GL_COLOR_TABLE_SCALE -#define CGL_COLOR_TABLE_BIAS GL_COLOR_TABLE_BIAS -#define CGL_COLOR_TABLE_FORMAT GL_COLOR_TABLE_FORMAT -#define CGL_COLOR_TABLE_WIDTH GL_COLOR_TABLE_WIDTH -#define CGL_COLOR_TABLE_RED_SIZE GL_COLOR_TABLE_RED_SIZE -#define CGL_COLOR_TABLE_GREEN_SIZE GL_COLOR_TABLE_GREEN_SIZE -#define CGL_COLOR_TABLE_BLUE_SIZE GL_COLOR_TABLE_BLUE_SIZE -#define CGL_COLOR_TABLE_ALPHA_SIZE GL_COLOR_TABLE_ALPHA_SIZE -#define CGL_COLOR_TABLE_LUMINANCE_SIZE GL_COLOR_TABLE_LUMINANCE_SIZE -#define CGL_COLOR_TABLE_INTENSITY_SIZE GL_COLOR_TABLE_INTENSITY_SIZE -#define CGL_CONVOLUTION_BORDER_MODE GL_CONVOLUTION_BORDER_MODE -#define CGL_CONVOLUTION_FILTER_SCALE GL_CONVOLUTION_FILTER_SCALE -#define CGL_CONVOLUTION_FILTER_BIAS GL_CONVOLUTION_FILTER_BIAS -#define CGL_REDUCE GL_REDUCE -#define CGL_CONVOLUTION_FORMAT GL_CONVOLUTION_FORMAT -#define CGL_CONVOLUTION_WIDTH GL_CONVOLUTION_WIDTH -#define CGL_CONVOLUTION_HEIGHT GL_CONVOLUTION_HEIGHT -#define CGL_MAX_CONVOLUTION_WIDTH GL_MAX_CONVOLUTION_WIDTH -#define CGL_MAX_CONVOLUTION_HEIGHT GL_MAX_CONVOLUTION_HEIGHT -#define CGL_POST_CONVOLUTION_RED_SCALE GL_POST_CONVOLUTION_RED_SCALE -#define CGL_POST_CONVOLUTION_GREEN_SCALE GL_POST_CONVOLUTION_GREEN_SCALE -#define CGL_POST_CONVOLUTION_BLUE_SCALE GL_POST_CONVOLUTION_BLUE_SCALE -#define CGL_POST_CONVOLUTION_ALPHA_SCALE GL_POST_CONVOLUTION_ALPHA_SCALE -#define CGL_POST_CONVOLUTION_RED_BIAS GL_POST_CONVOLUTION_RED_BIAS -#define CGL_POST_CONVOLUTION_GREEN_BIAS GL_POST_CONVOLUTION_GREEN_BIAS -#define CGL_POST_CONVOLUTION_BLUE_BIAS GL_POST_CONVOLUTION_BLUE_BIAS -#define CGL_POST_CONVOLUTION_ALPHA_BIAS GL_POST_CONVOLUTION_ALPHA_BIAS -#define CGL_CONSTANT_BORDER GL_CONSTANT_BORDER -#define CGL_REPLICATE_BORDER GL_REPLICATE_BORDER -#define CGL_CONVOLUTION_BORDER_COLOR GL_CONVOLUTION_BORDER_COLOR -#define CGL_COLOR_MATRIX GL_COLOR_MATRIX -#define CGL_COLOR_MATRIX_STACK_DEPTH GL_COLOR_MATRIX_STACK_DEPTH -#define CGL_MAX_COLOR_MATRIX_STACK_DEPTH GL_MAX_COLOR_MATRIX_STACK_DEPTH -#define CGL_POST_COLOR_MATRIX_RED_SCALE GL_POST_COLOR_MATRIX_RED_SCALE -#define CGL_POST_COLOR_MATRIX_GREEN_SCALE GL_POST_COLOR_MATRIX_GREEN_SCALE -#define CGL_POST_COLOR_MATRIX_BLUE_SCALE GL_POST_COLOR_MATRIX_BLUE_SCALE -#define CGL_POST_COLOR_MATRIX_ALPHA_SCALE GL_POST_COLOR_MATRIX_ALPHA_SCALE -#define CGL_POST_COLOR_MATRIX_RED_BIAS GL_POST_COLOR_MATRIX_RED_BIAS -#define CGL_POST_COLOR_MATRIX_GREEN_BIAS GL_POST_COLOR_MATRIX_GREEN_BIAS -#define CGL_POST_COLOR_MATRIX_BLUE_BIAS GL_POST_COLOR_MATRIX_BLUE_BIAS -#define CGL_POST_COLOR_MATRIX_ALPHA_BIAS GL_POST_COLOR_MATRIX_ALPHA_BIAS -#define CGL_HISTOGRAM GL_HISTOGRAM -#define CGL_PROXY_HISTOGRAM GL_PROXY_HISTOGRAM -#define CGL_HISTOGRAM_WIDTH GL_HISTOGRAM_WIDTH -#define CGL_HISTOGRAM_FORMAT GL_HISTOGRAM_FORMAT -#define CGL_HISTOGRAM_RED_SIZE GL_HISTOGRAM_RED_SIZE -#define CGL_HISTOGRAM_GREEN_SIZE GL_HISTOGRAM_GREEN_SIZE -#define CGL_HISTOGRAM_BLUE_SIZE GL_HISTOGRAM_BLUE_SIZE -#define CGL_HISTOGRAM_ALPHA_SIZE GL_HISTOGRAM_ALPHA_SIZE -#define CGL_HISTOGRAM_LUMINANCE_SIZE GL_HISTOGRAM_LUMINANCE_SIZE -#define CGL_HISTOGRAM_SINK GL_HISTOGRAM_SINK -#define CGL_MINMAX GL_MINMAX -#define CGL_MINMAX_FORMAT GL_MINMAX_FORMAT -#define CGL_MINMAX_SINK GL_MINMAX_SINK -#define CGL_TABLE_TOO_LARGE GL_TABLE_TOO_LARGE -#define CGL_BLEND_EQUATION GL_BLEND_EQUATION -#define CGL_MIN GL_MIN -#define CGL_MAX GL_MAX -#define CGL_FUNC_ADD GL_FUNC_ADD -#define CGL_FUNC_SUBTRACT GL_FUNC_SUBTRACT -#define CGL_FUNC_REVERSE_SUBTRACT GL_FUNC_REVERSE_SUBTRACT -#define CGL_BLEND_COLOR GL_BLEND_COLOR -#define CGL_ACTIVE_TEXTURE GL_ACTIVE_TEXTURE -#define CGL_CLIENT_ACTIVE_TEXTURE GL_CLIENT_ACTIVE_TEXTURE -#define CGL_MAX_TEXTURE_UNITS GL_MAX_TEXTURE_UNITS -#define CGL_NORMAL_MAP GL_NORMAL_MAP -#define CGL_REFLECTION_MAP GL_REFLECTION_MAP -#define CGL_TEXTURE_CUBE_MAP GL_TEXTURE_CUBE_MAP -#define CGL_TEXTURE_BINDING_CUBE_MAP GL_TEXTURE_BINDING_CUBE_MAP -#define CGL_TEXTURE_CUBE_MAP_POSITIVE_X GL_TEXTURE_CUBE_MAP_POSITIVE_X -#define CGL_TEXTURE_CUBE_MAP_NEGATIVE_X GL_TEXTURE_CUBE_MAP_NEGATIVE_X -#define CGL_TEXTURE_CUBE_MAP_POSITIVE_Y GL_TEXTURE_CUBE_MAP_POSITIVE_Y -#define CGL_TEXTURE_CUBE_MAP_NEGATIVE_Y GL_TEXTURE_CUBE_MAP_NEGATIVE_Y -#define CGL_TEXTURE_CUBE_MAP_POSITIVE_Z GL_TEXTURE_CUBE_MAP_POSITIVE_Z -#define CGL_TEXTURE_CUBE_MAP_NEGATIVE_Z GL_TEXTURE_CUBE_MAP_NEGATIVE_Z -#define CGL_PROXY_TEXTURE_CUBE_MAP GL_PROXY_TEXTURE_CUBE_MAP -#define CGL_MAX_CUBE_MAP_TEXTURE_SIZE GL_MAX_CUBE_MAP_TEXTURE_SIZE -#define CGL_COMPRESSED_ALPHA GL_COMPRESSED_ALPHA -#define CGL_COMPRESSED_LUMINANCE GL_COMPRESSED_LUMINANCE -#define CGL_COMPRESSED_LUMINANCE_ALPHA GL_COMPRESSED_LUMINANCE_ALPHA -#define CGL_COMPRESSED_INTENSITY GL_COMPRESSED_INTENSITY -#define CGL_COMPRESSED_RGB GL_COMPRESSED_RGB -#define CGL_COMPRESSED_RGBA GL_COMPRESSED_RGBA -#define CGL_TEXTURE_COMPRESSION_HINT GL_TEXTURE_COMPRESSION_HINT -#define CGL_TEXTURE_COMPRESSED_IMAGE_SIZE GL_TEXTURE_COMPRESSED_IMAGE_SIZE -#define CGL_TEXTURE_COMPRESSED GL_TEXTURE_COMPRESSED -#define CGL_NUM_COMPRESSED_TEXTURE_FORMATS GL_NUM_COMPRESSED_TEXTURE_FORMATS -#define CGL_COMPRESSED_TEXTURE_FORMATS GL_COMPRESSED_TEXTURE_FORMATS -#define CGL_MULTISAMPLE GL_MULTISAMPLE -#define CGL_SAMPLE_ALPHA_TO_COVERAGE GL_SAMPLE_ALPHA_TO_COVERAGE -#define CGL_SAMPLE_ALPHA_TO_ONE GL_SAMPLE_ALPHA_TO_ONE -#define CGL_SAMPLE_COVERAGE GL_SAMPLE_COVERAGE -#define CGL_SAMPLE_BUFFERS GL_SAMPLE_BUFFERS -#define CGL_SAMPLES GL_SAMPLES -#define CGL_SAMPLE_COVERAGE_VALUE GL_SAMPLE_COVERAGE_VALUE -#define CGL_SAMPLE_COVERAGE_INVERT GL_SAMPLE_COVERAGE_INVERT -#define CGL_MULTISAMPLE_BIT GL_MULTISAMPLE_BIT -#define CGL_TRANSPOSE_MODELVIEW_MATRIX GL_TRANSPOSE_MODELVIEW_MATRIX -#define CGL_TRANSPOSE_PROJECTION_MATRIX GL_TRANSPOSE_PROJECTION_MATRIX -#define CGL_TRANSPOSE_TEXTURE_MATRIX GL_TRANSPOSE_TEXTURE_MATRIX -#define CGL_TRANSPOSE_COLOR_MATRIX GL_TRANSPOSE_COLOR_MATRIX -#define CGL_COMBINE GL_COMBINE -#define CGL_COMBINE_RGB GL_COMBINE_RGB -#define CGL_COMBINE_ALPHA GL_COMBINE_ALPHA -#define CGL_RGB_SCALE GL_RGB_SCALE -#define CGL_ADD_SIGNED GL_ADD_SIGNED -#define CGL_INTERPOLATE GL_INTERPOLATE -#define CGL_SUBTRACT GL_SUBTRACT -#define CGL_CONSTANT GL_CONSTANT -#define CGL_PRIMARY_COLOR GL_PRIMARY_COLOR -#define CGL_PREVIOUS GL_PREVIOUS -#define CGL_CLAMP_TO_BORDER GL_CLAMP_TO_BORDER -#define CGL_ACTIVE_TEXTURE_ARB GL_ACTIVE_TEXTURE_ARB -#define CGL_CLIENT_ACTIVE_TEXTURE_ARB GL_CLIENT_ACTIVE_TEXTURE_ARB -#define CGL_MAX_TEXTURE_UNITS_ARB GL_MAX_TEXTURE_UNITS_ARB -#define CGL_DEBUG_OBJECT_MESA GL_DEBUG_OBJECT_MESA -#define CGL_DEBUG_PRINT_MESA GL_DEBUG_PRINT_MESA -#define CGL_DEBUG_ASSERT_MESA GL_DEBUG_ASSERT_MESA -#define CGL_TRACE_ALL_BITS_MESA GL_TRACE_ALL_BITS_MESA -#define CGL_TRACE_OPERATIONS_BIT_MESA GL_TRACE_OPERATIONS_BIT_MESA -#define CGL_TRACE_PRIMITIVES_BIT_MESA GL_TRACE_PRIMITIVES_BIT_MESA -#define CGL_TRACE_ARRAYS_BIT_MESA GL_TRACE_ARRAYS_BIT_MESA -#define CGL_TRACE_TEXTURES_BIT_MESA GL_TRACE_TEXTURES_BIT_MESA -#define CGL_TRACE_PIXELS_BIT_MESA GL_TRACE_PIXELS_BIT_MESA -#define CGL_TRACE_ERRORS_BIT_MESA GL_TRACE_ERRORS_BIT_MESA -#define CGL_TRACE_MASK_MESA GL_TRACE_MASK_MESA -#define CGL_TRACE_NAME_MESA GL_TRACE_NAME_MESA -#define CGL_DEPTH_STENCIL_MESA GL_DEPTH_STENCIL_MESA -#define CGL_FRAGMENT_PROGRAM_POSITION_MESA GL_FRAGMENT_PROGRAM_POSITION_MESA -#define CGL_FRAGMENT_PROGRAM_CALLBACK_MESA GL_FRAGMENT_PROGRAM_CALLBACK_MESA -#define CGL_FRAGMENT_PROGRAM_CALLBACK_FUNC_MESA GL_FRAGMENT_PROGRAM_CALLBACK_FUNC_MESA -#define CGL_FRAGMENT_PROGRAM_CALLBACK_DATA_MESA GL_FRAGMENT_PROGRAM_CALLBACK_DATA_MESA -#define CGL_VERTEX_PROGRAM_POSITION_MESA GL_VERTEX_PROGRAM_POSITION_MESA -#define CGL_VERTEX_PROGRAM_CALLBACK_MESA GL_VERTEX_PROGRAM_CALLBACK_MESA -#define CGL_VERTEX_PROGRAM_CALLBACK_FUNC_MESA GL_VERTEX_PROGRAM_CALLBACK_FUNC_MESA -#define CGL_VERTEX_PROGRAM_CALLBACK_DATA_MESA GL_VERTEX_PROGRAM_CALLBACK_DATA_MESA -#define CGL_ALPHA_BLEND_EQUATION_ATI GL_ALPHA_BLEND_EQUATION_ATI -#define CGL_TIME_ELAPSED_EXT GL_TIME_ELAPSED_EXT -#define CGL_READ_FRAMEBUFFER_EXT GL_READ_FRAMEBUFFER_EXT -#define CGL_DRAW_FRAMEBUFFER_EXT GL_DRAW_FRAMEBUFFER_EXT -#define CGL_DRAW_FRAMEBUFFER_BINDING_EXT GL_DRAW_FRAMEBUFFER_BINDING_EXT -#define CGL_READ_FRAMEBUFFER_BINDING_EXT GL_READ_FRAMEBUFFER_BINDING_EXT -#define CGL_DEPTH_STENCIL_EXT GL_DEPTH_STENCIL_EXT -#define CGL_TEXTURE_STENCIL_SIZE_EXT GL_TEXTURE_STENCIL_SIZE_EXT -#define CGL_SRGB_EXT GL_SRGB_EXT -#define CGL_SRGB_ALPHA_EXT GL_SRGB_ALPHA_EXT -#define CGL_SLUMINANCE_ALPHA_EXT GL_SLUMINANCE_ALPHA_EXT -#define CGL_SLUMINANCE_EXT GL_SLUMINANCE_EXT -#define CGL_COMPRESSED_SRGB_EXT GL_COMPRESSED_SRGB_EXT -#define CGL_COMPRESSED_SRGB_ALPHA_EXT GL_COMPRESSED_SRGB_ALPHA_EXT -#define CGL_COMPRESSED_SLUMINANCE_EXT GL_COMPRESSED_SLUMINANCE_EXT -#define CGL_COMPRESSED_SLUMINANCE_ALPHA_EXT GL_COMPRESSED_SLUMINANCE_ALPHA_EXT - -#define CGL_DOT3_RGB GL_DOT3_RGB -#define CGL_DOT3_RGBA GL_DOT3_RGBA - -/* extras */ - -#define CGL_TEXTURE_2D GL_TEXTURE_2D -#define CGL_ARGB GL_ARGB -#define CGL_UNSIGNED_INT_8_8_8_8_REV GL_UNSIGNED_INT_8_8_8_8_REV - -#ifdef GL_TEXTURE_RECTANGLE_ARB -#define CGL_TEXTURE_RECTANGLE_ARB GL_TEXTURE_RECTANGLE_ARB -#else -#define CGL_TEXTURE_RECTANGLE_ARB 0 -#endif - -#ifdef GL_YCBCR_MESA -#define CGL_YCBCR_MESA GL_YCBCR_MESA -#define CGL_UNSIGNED_SHORT_8_8_REV_MESA GL_UNSIGNED_SHORT_8_8_REV_MESA -#define CGL_UNSIGNED_SHORT_8_8_MESA GL_UNSIGNED_SHORT_8_8_MESA -#else -#define CGL_YCBCR_MESA 0 -#define CGL_UNSIGNED_SHORT_8_8_REV_MESA 0 -#define CGL_UNSIGNED_SHORT_8_8_MESA 0 -#endif - -#define CGL_OBJECT_COMPILE_STATUS GL_OBJECT_COMPILE_STATUS_ARB - #define CLUTTER_COGL_HAS_GL 1 #define COGL_HAS_GL 1 diff --git a/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c b/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c index 742fab807..4e54babd2 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c +++ b/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c @@ -346,7 +346,7 @@ _cogl_texture_driver_allows_foreign_gl_target (GLenum gl_target) (for example, no mipmapping and complicated shader support) */ /* Allow 2-dimensional or rectangle textures only */ - if (gl_target != GL_TEXTURE_2D && gl_target != CGL_TEXTURE_RECTANGLE_ARB) + if (gl_target != GL_TEXTURE_2D && gl_target != GL_TEXTURE_RECTANGLE_ARB) return FALSE; return TRUE; diff --git a/clutter/cogl/cogl/driver/gles/cogl-defines.h.in b/clutter/cogl/cogl/driver/gles/cogl-defines.h.in index 97045df94..b7e76529d 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-defines.h.in +++ b/clutter/cogl/cogl/driver/gles/cogl-defines.h.in @@ -33,606 +33,9 @@ G_BEGIN_DECLS #define @COGL_GLES_VERSION@ 1 -/* ClearBufferMask */ -#define CGL_DEPTH_BUFFER_BIT GL_DEPTH_BUFFER_BIT -#define CGL_STENCIL_BUFFER_BIT GL_STENCIL_BUFFER_BIT -#define CGL_COLOR_BUFFER_BIT GL_COLOR_BUFFER_BIT - -/* Boolean */ -#define CGL_FALSE GL_FALSE -#define CGL_TRUE GL_TRUE - -/* BeginMode */ -#define CGL_POINTS GL_POINTS -#define CGL_LINES GL_LINES -#define CGL_LINE_LOOP GL_LINE_LOOP -#define CGL_LINE_STRIP GL_LINE_STRIP -#define CGL_TRIANGLES GL_TRIANGLES -#define CGL_TRIANGLE_STRIP GL_TRIANGLE_STRIP -#define CGL_TRIANGLE_FAN GL_TRIANGLE_FAN - -/* AlphaFunction */ -#define CGL_NEVER GL_NEVER -#define CGL_LESS GL_LESS -#define CGL_EQUAL GL_EQUAL -#define CGL_LEQUAL GL_LEQUAL -#define CGL_GREATER GL_GREATER -#define CGL_NOTEQUAL GL_NOTEQUAL -#define CGL_GEQUAL GL_GEQUAL -#define CGL_ALWAYS GL_ALWAYS - -/* BlendingFactorDest */ -#define CGL_ZERO GL_ZERO -#define CGL_ONE GL_ONE -#define CGL_SRC_COLOR GL_SRC_COLOR -#define CGL_ONE_MINUS_SRC_COLOR GL_ONE_MINUS_SRC_COLOR -#define CGL_SRC_ALPHA GL_SRC_ALPHA -#define CGL_ONE_MINUS_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA -#define CGL_DST_ALPHA GL_DST_ALPHA -#define CGL_ONE_MINUS_DST_ALPHA GL_ONE_MINUS_DST_ALPHA - -/* BlendingFactorSrc */ -#define CGL_DST_COLOR GL_DST_COLOR -#define CGL_ONE_MINUS_DST_COLOR GL_ONE_MINUS_DST_COLOR -#define CGL_SRC_ALPHA_SATURATE GL_SRC_ALPHA_SATURATE -/* Missing; */ -/* GL_ZERO */ -/* GL_ONE */ -/* GL_SRC_ALPHA */ -/* GL_ONE_MINUS_SRC_ALPHA */ -/* GL_DST_ALPHA */ -/* GL_ONE_MINUS_DST_ALPHA */ - -/* CullFaceMode */ -#define CGL_FRONT GL_FRONT -#define CGL_BACK GL_BACK -#define CGL_FRONT_AND_BACK GL_FRONT_AND_BACK - -/* EnableCap */ -#define CGL_FOG GL_FOG -#define CGL_LIGHTING GL_LIGHTING -#define CGL_CULL_FACE GL_CULL_FACE -#define CGL_ALPHA_TEST GL_ALPHA_TEST -#define CGL_BLEND GL_BLEND -#define CGL_COLOR_LOGIC_OP GL_COLOR_LOGIC_OP -#define CGL_DITHER GL_DITHER -#define CGL_STENCIL_TEST GL_STENCIL_TEST -#define CGL_DEPTH_TEST GL_DEPTH_TEST -#define CGL_POINT_SMOOTH GL_POINT_SMOOTH -#define CGL_LINE_SMOOTH GL_LINE_SMOOTH -#define CGL_SCISSOR_TEST GL_SCISSOR_TEST -#define CGL_COLOR_MATERIAL GL_COLOR_MATERIAL -#define CGL_NORMALIZE GL_NORMALIZE -#define CGL_RESCALE_NORMAL GL_RESCALE_NORMAL -#define CGL_POLYGON_OFFSET_FILL GL_POLYGON_OFFSET_FILL -#define CGL_VERTEX_ARRAY GL_VERTEX_ARRAY -#define CGL_NORMAL_ARRAY GL_NORMAL_ARRAY -#define CGL_COLOR_ARRAY GL_COLOR_ARRAY -#define CGL_TEXTURE_COORD_ARRAY GL_TEXTURE_COORD_ARRAY -#define CGL_MULTISAMPLE GL_MULTISAMPLE -#define CGL_SAMPLE_ALPHA_TO_COVERAGE GL_SAMPLE_ALPHA_TO_COVERAGE -#define CGL_SAMPLE_ALPHA_TO_ONE GL_SAMPLE_ALPHA_TO_ONE -#define CGL_SAMPLE_COVERAGE GL_SAMPLE_COVERAGE - -/* Errors */ -#define CGL_NO_ERROR GL_NO_ERROR -#define CGL_INVALID_ENUM GL_INVALID_ENUM -#define CGL_INVALID_VALUE GL_INVALID_VALUE -#define CGL_INVALID_OPERATION GL_INVALID_OPERATION -#define CGL_STACK_OVERFLOW GL_STACK_OVERFLOW -#define CGL_STACK_UNDERFLOW GL_STACK_UNDERFLOW -#define CGL_OUT_OF_MEMORY GL_OUT_OF_MEMORY - -/* Fog mode */ -#define CGL_EXP GL_EXP -#define CGL_EXP2 GL_EXP2 -#define CGL_FOG_DENSITY GL_FOG_DENSITY - -/* FogParameter */ -#define CGL_FOG_START GL_FOG_START -#define CGL_FOG_END GL_FOG_END -#define CGL_FOG_MODE GL_FOG_MODE -#define CGL_FOG_COLOR GL_FOG_COLOR -#define CGL_CW GL_CW -#define CGL_CCW GL_CCW - -/* GetPName */ -#define CGL_CURRENT_COLOR GL_CURRENT_COLOR -#define CGL_CURRENT_NORMAL GL_CURRENT_NORMAL -#define CGL_CURRENT_TEXTURE_COORDS GL_CURRENT_TEXTURE_COORDS -#define CGL_POINT_SIZE GL_POINT_SIZE -#define CGL_POINT_SIZE_MIN GL_POINT_SIZE_MIN -#define CGL_POINT_SIZE_MAX GL_POINT_SIZE_MAX -#define CGL_POINT_FADE_THRESHOLD_SIZE GL_POINT_FADE_THRESHOLD_SIZE -#define CGL_POINT_DISTANCE_ATTENUATION GL_POINT_DISTANCE_ATTENUATION -#define CGL_SMOOTH_POINT_SIZE_RANGE GL_SMOOTH_POINT_SIZE_RANGE -#define CGL_LINE_WIDTH GL_LINE_WIDTH -#define CGL_SMOOTH_LINE_WIDTH_RANGE GL_SMOOTH_LINE_WIDTH_RANGE -#define CGL_ALIASED_POINT_SIZE_RANGE GL_ALIASED_POINT_SIZE_RANGE -#define CGL_ALIASED_LINE_WIDTH_RANGE GL_ALIASED_LINE_WIDTH_RANGE -#define CGL_CULL_FACE_MODE GL_CULL_FACE_MODE -#define CGL_FRONT_FACE GL_FRONT_FACE -#define CGL_SHADE_MODEL GL_SHADE_MODEL -#define CGL_DEPTH_RANGE GL_DEPTH_RANGE -#define CGL_DEPTH_WRITEMASK GL_DEPTH_WRITEMASK -#define CGL_DEPTH_CLEAR_VALUE GL_DEPTH_CLEAR_VALUE -#define CGL_DEPTH_FUNC GL_DEPTH_FUNC -#define CGL_STENCIL_CLEAR_VALUE GL_STENCIL_CLEAR_VALUE -#define CGL_STENCIL_FUNC GL_STENCIL_FUNC -#define CGL_STENCIL_VALUE_MASK GL_STENCIL_VALUE_MASK -#define CGL_STENCIL_FAIL GL_STENCIL_FAIL -#define CGL_STENCIL_PASS_DEPTH_FAIL GL_STENCIL_PASS_DEPTH_FAIL -#define CGL_STENCIL_PASS_DEPTH_PASS GL_STENCIL_PASS_DEPTH_PASS -#define CGL_STENCIL_REF GL_STENCIL_REF -#define CGL_STENCIL_WRITEMASK GL_STENCIL_WRITEMASK -#ifdef COGL_HAS_GLES2 -#define CGL_MATRIX_MODE 0x0BA0 /* bad style but works for now */ -#else -#define CGL_MATRIX_MODE GL_MATRIX_MODE -#endif -#define CGL_VIEWPORT GL_VIEWPORT -#define CGL_MODELVIEW_STACK_DEPTH GL_MODELVIEW_STACK_DEPTH -#define CGL_PROJECTION_STACK_DEPTH GL_PROJECTION_STACK_DEPTH -#define CGL_TEXTURE_STACK_DEPTH GL_TEXTURE_STACK_DEPTH -#define CGL_MODELVIEW_MATRIX GL_MODELVIEW_MATRIX -#define CGL_PROJECTION_MATRIX GL_PROJECTION_MATRIX -#define CGL_TEXTURE_MATRIX GL_TEXTURE_MATRIX -#define CGL_ALPHA_TEST_FUNC GL_ALPHA_TEST_FUNC -#define CGL_ALPHA_TEST_REF GL_ALPHA_TEST_REF -#define CGL_BLEND_DST GL_BLEND_DST -#define CGL_BLEND_SRC GL_BLEND_SRC -#define CGL_LOGIC_OP_MODE GL_LOGIC_OP_MODE -#define CGL_SCISSOR_BOX GL_SCISSOR_BOX -#define CGL_SCISSOR_TEST GL_SCISSOR_TEST -#define CGL_COLOR_CLEAR_VALUE GL_COLOR_CLEAR_VALUE -#define CGL_COLOR_WRITEMASK GL_COLOR_WRITEMASK -#define CGL_UNPACK_ALIGNMENT GL_UNPACK_ALIGNMENT -#define CGL_PACK_ALIGNMENT GL_PACK_ALIGNMENT -#define CGL_MAX_LIGHTS GL_MAX_LIGHTS -#define CGL_MAX_CLIP_PLANES GL_MAX_CLIP_PLANES -#define CGL_MAX_TEXTURE_SIZE GL_MAX_TEXTURE_SIZE -#define CGL_MAX_MODELVIEW_STACK_DEPTH GL_MAX_MODELVIEW_STACK_DEPTH -#define CGL_MAX_PROJECTION_STACK_DEPTH GL_MAX_PROJECTION_STACK_DEPTH -#define CGL_MAX_TEXTURE_STACK_DEPTH GL_MAX_TEXTURE_STACK_DEPTH -#define CGL_MAX_VIEWPORT_DIMS GL_MAX_VIEWPORT_DIMS -#define CGL_MAX_ELEMENTS_VERTICES GL_MAX_ELEMENTS_VERTICES -#define CGL_MAX_ELEMENTS_INDICES GL_MAX_ELEMENTS_INDICES -#ifdef COGL_HAS_GLES2 -#define CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS -#else -#define CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS GL_MAX_TEXTURE_UNITS -#endif -#define CGL_SUBPIXEL_BITS GL_SUBPIXEL_BITS -#define CGL_RED_BITS GL_RED_BITS -#define CGL_GREEN_BITS GL_GREEN_BITS -#define CGL_BLUE_BITS GL_BLUE_BITS -#define CGL_ALPHA_BITS GL_ALPHA_BITS -#define CGL_DEPTH_BITS GL_DEPTH_BITS -#define CGL_STENCIL_BITS GL_STENCIL_BITS -#define CGL_POLYGON_OFFSET_UNITS GL_POLYGON_OFFSET_UNITS -#define CGL_POLYGON_OFFSET_FILL GL_POLYGON_OFFSET_FILL -#define CGL_POLYGON_OFFSET_FACTOR GL_POLYGON_OFFSET_FACTOR -#define CGL_VERTEX_ARRAY_SIZE GL_VERTEX_ARRAY_SIZE -#define CGL_VERTEX_ARRAY_TYPE GL_VERTEX_ARRAY_TYPE -#define CGL_VERTEX_ARRAY_STRIDE GL_VERTEX_ARRAY_STRIDE -#define CGL_NORMAL_ARRAY_TYPE GL_NORMAL_ARRAY_TYPE -#define CGL_NORMAL_ARRAY_STRIDE GL_NORMAL_ARRAY_STRIDE -#define CGL_COLOR_ARRAY_SIZE GL_COLOR_ARRAY_SIZE -#define CGL_COLOR_ARRAY_TYPE GL_COLOR_ARRAY_TYPE -#define CGL_COLOR_ARRAY_STRIDE GL_COLOR_ARRAY_STRIDE -#define CGL_TEXTURE_COORD_ARRAY_SIZE GL_TEXTURE_COORD_ARRAY_SIZE -#define CGL_TEXTURE_COORD_ARRAY_TYPE GL_TEXTURE_COORD_ARRAY_TYPE -#define CGL_TEXTURE_COORD_ARRAY_STRIDE GL_TEXTURE_COORD_ARRAY_STRIDE -#define CGL_VERTEX_ARRAY_POINTER GL_VERTEX_ARRAY_POINTER -#define CGL_NORMAL_ARRAY_POINTER GL_NORMAL_ARRAY_POINTER -#define CGL_COLOR_ARRAY_POINTER GL_COLOR_ARRAY_POINTER -#define CGL_TEXTURE_COORD_ARRAY_POINTER GL_TEXTURE_COORD_ARRAY_POINTER -#define CGL_SAMPLE_BUFFERS GL_SAMPLE_BUFFERS -#define CGL_SAMPLES GL_SAMPLES -#define CGL_SAMPLE_COVERAGE_VALUE GL_SAMPLE_COVERAGE_VALUE -#define CGL_SAMPLE_COVERAGE_INVERT GL_SAMPLE_COVERAGE_INVERT - -/* GetTextureParameter - missing */ -/* GL_TEXTURE_MAG_FILTER */ -/* GL_TEXTURE_MIN_FILTER */ -/* GL_TEXTURE_WRAP_S */ -/* GL_TEXTURE_WRAP_T */ - - -#define CGL_IMPLEMENTATION_COLOR_READ_TYPE_OES GL_IMPLEMENTATION_COLOR_READ_TYPE_OES -#define CGL_IMPLEMENTATION_COLOR_READ_FORMAT_OES GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES -#define CGL_NUM_COMPRESSED_TEXTURE_FORMATS GL_NUM_COMPRESSED_TEXTURE_FORMATS -#define CGL_COMPRESSED_TEXTURE_FORMATS GL_COMPRESSED_TEXTURE_FORMATS - -/* OES_matrix_get */ -#define CGL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS -#define CGL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS -#define CGL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS -/* HintMode */ -#define CGL_DONT_CARE GL_DONT_CARE -#define CGL_FASTEST GL_FASTEST -#define CGL_NICEST GL_NICEST - -/* HintTarget */ -#define CGL_PERSPECTIVE_CORRECTION_HINT GL_PERSPECTIVE_CORRECTION_HINT -#define CGL_POINT_SMOOTH_HINT GL_POINT_SMOOTH_HINT -#define CGL_LINE_SMOOTH_HINT GL_LINE_SMOOTH_HINT -#define CGL_POLYGON_SMOOTH_HINT GL_POLYGON_SMOOTH_HINT -#define CGL_FOG_HINT GL_FOG_HINT -#define CGL_GENERATE_MIPMAP_HINT GL_GENERATE_MIPMAP_HINT - -/* LightModelParameter */ -#define CGL_LIGHT_MODEL_AMBIENT GL_LIGHT_MODEL_AMBIENT -#define CGL_LIGHT_MODEL_TWO_SIDE GL_LIGHT_MODEL_TWO_SIDE - -/* LightParameter */ -#define CGL_POSITION GL_POSITION -#define CGL_SPOT_DIRECTION GL_SPOT_DIRECTION -#define CGL_SPOT_EXPONENT GL_SPOT_EXPONENT -#define CGL_SPOT_CUTOFF GL_SPOT_CUTOFF -#define CGL_CONSTANT_ATTENUATION GL_CONSTANT_ATTENUATION -#define CGL_LINEAR_ATTENUATION GL_LINEAR_ATTENUATION -#define CGL_QUADRATIC_ATTENUATION GL_QUADRATIC_ATTENUATION - -/* DataType */ -#define CGL_BYTE GL_BYTE -#define CGL_UNSIGNED_BYTE GL_UNSIGNED_BYTE -#define CGL_SHORT GL_SHORT -#define CGL_UNSIGNED_SHORT GL_UNSIGNED_SHORT -#define CGL_FLOAT GL_FLOAT -#define CGL_FIXED GL_FIXED - -/* LogicOp */ -#define CGL_CLEAR GL_CLEAR -#define CGL_AND GL_AND -#define CGL_AND_REVERSE GL_AND_REVERSE -#define CGL_COPY GL_COPY -#define CGL_AND_INVERTED GL_AND_INVERTED -#define CGL_NOOP GL_NOOP -#define CGL_XOR GL_XOR -#define CGL_OR GL_OR -#define CGL_NOR GL_NOR -#define CGL_EQUIV GL_EQUIV -#define CGL_INVERT GL_INVERT -#define CGL_OR_REVERSE GL_OR_REVERSE -#define CGL_COPY_INVERTED GL_COPY_INVERTED -#define CGL_OR_INVERTED GL_OR_INVERTED -#define CGL_NAND GL_NAND -#define CGL_SET GL_SET - -/* MaterialParameter */ -#define CGL_AMBIENT_AND_DIFFUSE GL_AMBIENT_AND_DIFFUSE - -/* MatrixMode */ -#define CGL_MODELVIEW GL_MODELVIEW -#define CGL_PROJECTION GL_PROJECTION -#define CGL_TEXTURE GL_TEXTURE - -/* PixelFormat */ -#define CGL_ALPHA GL_ALPHA -#define CGL_RGB GL_RGB -#define CGL_RGBA GL_RGBA -#define CGL_LUMINANCE GL_LUMINANCE -#define CGL_LUMINANCE_ALPHA GL_LUMINANCE_ALPHA - -/* PixelStoreParameter */ -#define CGL_UNPACK_ALIGNMENT GL_UNPACK_ALIGNMENT -#define CGL_PACK_ALIGNMENT GL_PACK_ALIGNMENT - -/* PixelType */ -/* GL_UNSIGNED_BYTE */ -#define CGL_UNSIGNED_SHORT_4_4_4_4 GL_UNSIGNED_SHORT_4_4_4_4 -#define CGL_UNSIGNED_SHORT_5_5_5_1 GL_UNSIGNED_SHORT_5_5_5_1 -#define CGL_UNSIGNED_SHORT_5_6_5 CGL_UNSIGNED_SHORT_5_6_5 - -/* ShadingModel */ -#define CGL_FLAT GL_FLAT -#define CGL_SMOOTH GL_SMOOTH - -/* StencilFunction */ -/* GL_NEVER */ -/* GL_LESS */ -/* GL_EQUAL */ -/* GL_LEQUAL */ -/* GL_GREATER */ -/* GL_NOTEQUAL */ -/* GL_GEQUAL */ -/* GL_ALWAYS */ - -/* StencilOp */ -#define CGL_KEEP GL_KEEP -#define CGL_REPLACE GL_REPLACE -#define CGL_INCR GL_INCR -#define CGL_DECR GL_DECR - -/* StringName */ -#define CGL_VENDOR GL_VENDOR -#define CGL_RENDERER GL_RENDERER -#define CGL_VERSION GL_VERSION -#define CGL_EXTENSIONS GL_EXTENSIONS - -/* TextureEnvMode */ -#define CGL_DECAL GL_DECAL -/* GL_BLEND */ -/* GL_REPLACE */ - -/* TextureEnvParameter */ -#define CGL_TEXTURE_ENV_MODE GL_TEXTURE_ENV_MODE -#define CGL_TEXTURE_ENV_COLOR GL_TEXTURE_ENV_COLOR - -/* TextureEnvTarget */ -#define CGL_TEXTURE_ENV GL_TEXTURE_ENV - -/* TextureMagFilter */ -#define CGL_NEAREST GL_NEAREST -#define CGL_LINEAR GL_LINEAR - -/* TextureMinFilter */ -/* GL_NEAREST */ -/* GL_LINEAR */ -#define CGL_NEAREST_MIPMAP_NEAREST GL_NEAREST_MIPMAP_NEAREST -#define CGL_LINEAR_MIPMAP_NEAREST GL_LINEAR_MIPMAP_NEAREST -#define CGL_NEAREST_MIPMAP_LINEAR GL_NEAREST_MIPMAP_LINEAR -#define CGL_LINEAR_MIPMAP_LINEAR GL_LINEAR_MIPMAP_LINEAR - -/* TextureParameterName */ -#define CGL_TEXTURE_MAG_FILTER GL_TEXTURE_MAG_FILTER -#define CGL_TEXTURE_MIN_FILTER GL_TEXTURE_MIN_FILTER -#define CGL_TEXTURE_WRAP_S GL_TEXTURE_WRAP_S -#define CGL_TEXTURE_WRAP_T GL_TEXTURE_WRAP_T -#define CGL_GENERATE_MIPMAP GL_GENERATE_MIPMAP - -#define CGL_ACTIVE_TEXTURE GL_ACTIVE_TEXTURE -#define CGL_CLIENT_ACTIVE_TEXTURE GL_CLIENT_ACTIVE_TEXTURE - -/* TextureWrapMode */ -#define CGL_REPEAT GL_REPEAT -#define CGL_CLAMP_TO_EDGE GL_CLAMP_TO_EDGE - -/* PixelInternalFormat */ - -/* Buffer Objects */ -#define CGL_ARRAY_BUFFER GL_ARRAY_BUFFER -#define CGL_ELEMENT_ARRAY_BUFFER GL_ELEMENT_ARRAY_BUFFER -#define CGL_ARRAY_BUFFER_BINDING GL_ARRAY_BUFFER_BINDING -#define CGL_ELEMENT_ARRAY_BUFFER_BINDING GL_ELEMENT_ARRAY_BUFFER_BINDING -#define CGL_VERTEX_ARRAY_BUFFER_BINDING GL_VERTEX_ARRAY_BUFFER_BINDING -#define CGL_NORMAL_ARRAY_BUFFER_BINDING GL_NORMAL_ARRAY_BUFFER_BINDING -#define CGL_COLOR_ARRAY_BUFFER_BINDING GL_COLOR_ARRAY_BUFFER_BINDING -#define CGL_TEXTURE_COORD_ARRAY_BUFFER_BINDING GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING -#define CGL_STATIC_DRAW GL_STATIC_DRAW -#define CGL_DYNAMIC_DRAW GL_DYNAMIC_DRAW -#define CGL_WRITE_ONLY GL_WRITE_ONLY -#define CGL_BUFFER_SIZE GL_BUFFER_SIZE -#define CGL_BUFFER_USAGE GL_BUFFER_USAGE -#define CGL_BUFFER_ACCESS GL_BUFFER_ACCESS -#define CGL_RGB_SCALE GL_RGB_SCALE -#define CGL_ALPHA_SCALE GL_ALPHA_SCALE -#define CGL_POINT_SPRITE_OES GL_POINT_SPRITE_OES -#define CGL_COORD_REPLACE_OES GL_COORD_REPLACE_OES -#define CGL_POINT_SIZE_ARRAY_OES GL_POINT_SIZE_ARRAY_OES -#define CGL_POINT_SIZE_ARRAY_TYPE_OES GL_POINT_SIZE_ARRAY_TYPE_OES -#define CGL_POINT_SIZE_ARRAY_STRIDE_OES GL_POINT_SIZE_ARRAY_STRIDE_OES -#define CGL_POINT_SIZE_ARRAY_POINTER_OES GL_POINT_SIZE_ARRAY_POINTER_OES -#define CGL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES -#define CGL_MAX_VERTEX_UNITS_OES GL_MAX_VERTEX_UNITS_OES -#define CGL_MAX_PALETTE_MATRICES_OES GL_MAX_PALETTE_MATRICES_OES -#define CGL_MATRIX_PALETTE_OES GL_MATRIX_PALETTE_OES -#define CGL_MATRIX_INDEX_ARRAY_OES GL_MATRIX_INDEX_ARRAY_OES -#define CGL_WEIGHT_ARRAY_OES GL_WEIGHT_ARRAY_OES -#define CGL_MATRIX_INDEX_ARRAY_SIZE_OES GL_MATRIX_INDEX_ARRAY_SIZE_OES -#define CGL_MATRIX_INDEX_ARRAY_TYPE_OES GL_MATRIX_INDEX_ARRAY_TYPE_OES -#define CGL_MATRIX_INDEX_ARRAY_STRIDE_OES GL_MATRIX_INDEX_ARRAY_STRIDE_OES -#define CGL_MATRIX_INDEX_ARRAY_POINTER_OES GL_MATRIX_INDEX_ARRAY_POINTER_OES -#define CGL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES -#define CGL_WEIGHT_ARRAY_SIZE_OES GL_WEIGHT_ARRAY_SIZE_OES -#define CGL_WEIGHT_ARRAY_TYPE_OES GL_WEIGHT_ARRAY_TYPE_OES -#define CGL_WEIGHT_ARRAY_STRIDE_OES GL_WEIGHT_ARRAY_STRIDE_OES -#define CGL_WEIGHT_ARRAY_POINTER_OES GL_WEIGHT_ARRAY_POINTER_OES -#define CGL_WEIGHT_ARRAY_BUFFER_BINDING_OES GL_WEIGHT_ARRAY_BUFFER_BINDING_OES -#define CGL_TEXTURE_CROP_RECT_OES GL_TEXTURE_CROP_RECT_OES - -/* extras */ - -/* YUV textures also unsupported */ -#define CGL_YCBCR_MESA 0 -#define CGL_UNSIGNED_SHORT_8_8_REV_MESA 0 -#define CGL_UNSIGNED_SHORT_8_8_MESA 0 - -#if defined(GL_OBJECT_COMPILE_STATUS) -#define CGL_OBJECT_COMPILE_STATUS GL_OBJECT_COMPILE_STATUS -#elif defined(GL_COMPILE_STATUS) -#define CGL_OBJECT_COMPILE_STATUS GL_COMPILE_STATUS -#else -#define CGL_OBJECT_COMPILE_STATUS 0 -#endif - #define CLUTTER_COGL_HAS_GLES 1 #define COGL_HAS_GLES 1 -/* These aren't always defined under GLES 2 but if they are then we - should probably use the GL_* macro instead of assuming the - number */ -#ifdef GL_MODULATE -#define CGL_MODULATE GL_MODULATE -#else -#define CGL_MODULATE 0x2100 -#endif - -#ifdef GL_ADD -#define CGL_ADD GL_ADD -#else -#define CGL_ADD 0x0104 -#endif - -#ifdef GL_ADD_SIGNED -#define CGL_ADD_SIGNED GL_ADD_SIGNED -#else -#define CGL_ADD_SIGNED 0x8574 -#endif - -#ifdef GL_INTERPOLATE -#define CGL_INTERPOLATE GL_INTERPOLATE -#else -#define CGL_INTERPOLATE 0x8575 -#endif - -#ifdef GL_SUBTRACT -#define CGL_SUBTRACT GL_SUBTRACT -#else -#define CGL_SUBTRACT 0x84e7 -#endif - -#ifdef GL_DOT3_RGB -#define CGL_DOT3_RGB GL_DOT3_RGB -#else -#define CGL_DOT3_RGB 0x86ae -#endif - -#ifdef GL_DOT3_RGBA -#define CGL_DOT3_RGBA GL_DOT3_RGBA -#else -#define CGL_DOT3_RGBA 0x86af -#endif - -#ifdef GL_CONSTANT -#define CGL_CONSTANT GL_CONSTANT -#else -#define CGL_CONSTANT 0x8576 -#endif - -#ifdef GL_PRIMARY_COLOR -#define CGL_PRIMARY_COLOR GL_PRIMARY_COLOR -#else -#define CGL_PRIMARY_COLOR 0x8577 -#endif - -#ifdef GL_PREVIOUS -#define CGL_PREVIOUS GL_PREVIOUS -#else -#define CGL_PREVIOUS 0x8578 -#endif - -#ifdef GL_COMBINE -#define CGL_COMBINE GL_COMBINE -#else -#define CGL_COMBINE 0x8570 -#endif - -#ifdef GL_COMBINE_RGB -#define CGL_COMBINE_RGB GL_COMBINE_RGB -#else -#define CGL_COMBINE_RGB 0x8571 -#endif - -#ifdef GL_COMBINE_ALPHA -#define CGL_COMBINE_ALPHA GL_COMBINE_ALPHA -#else -#define CGL_COMBINE_ALPHA 0x8572 -#endif - -#ifdef GL_SRC0_RGB -#define CGL_SRC0_RGB GL_SRC0_RGB -#else -#define CGL_SRC0_RGB 0x8580 -#endif - -#ifdef GL_OPERAND0_RGB -#define CGL_OPERAND0_RGB GL_OPERAND0_RGB -#else -#define CGL_OPERAND0_RGB 0x8590 -#endif - -#ifdef GL_SRC1_RGB -#define CGL_SRC1_RGB GL_SRC1_RGB -#else -#define CGL_SRC1_RGB 0x8581 -#endif - -#ifdef GL_OPERAND1_RGB -#define CGL_OPERAND1_RGB GL_OPERAND1_RGB -#else -#define CGL_OPERAND1_RGB 0x8591 -#endif - -#ifdef GL_SRC2_RGB -#define CGL_SRC2_RGB GL_SRC2_RGB -#else -#define CGL_SRC2_RGB 0x8582 -#endif - -#ifdef GL_OPERAND2_RGB -#define CGL_OPERAND2_RGB GL_OPERAND2_RGB -#else -#define CGL_OPERAND2_RGB 0x8592 -#endif - -#ifdef GL_SRC0_ALPHA -#define CGL_SRC0_ALPHA GL_SRC0_ALPHA -#else -#define CGL_SRC0_ALPHA 0x8588 -#endif - -#ifdef GL_OPERAND0_ALPHA -#define CGL_OPERAND0_ALPHA GL_OPERAND0_ALPHA -#else -#define CGL_OPERAND0_ALPHA 0x8598 -#endif - -#ifdef GL_SRC1_ALPHA -#define CGL_SRC1_ALPHA GL_SRC1_ALPHA -#else -#define CGL_SRC1_ALPHA 0x8589 -#endif - -#ifdef GL_OPERAND1_ALPHA -#define CGL_OPERAND1_ALPHA GL_OPERAND1_ALPHA -#else -#define CGL_OPERAND1_ALPHA 0x8599 -#endif - -#ifdef GL_SRC2_ALPHA -#define CGL_SRC2_ALPHA GL_SRC2_ALPHA -#else -#define CGL_SRC2_ALPHA 0x858a -#endif - -#ifdef GL_OPERAND2_ALPHA -#define CGL_OPERAND2_ALPHA GL_OPERAND2_ALPHA -#else -#define CGL_OPERAND2_ALPHA 0x859a -#endif - -#ifdef GL_AMBIENT -#define CGL_AMBIENT GL_AMBIENT -#else -#define CGL_AMBIENT 0x1200 -#endif - -#ifdef GL_DIFFUSE -#define CGL_DIFFUSE GL_DIFFUSE -#else -#define CGL_DIFFUSE 0x1201 -#endif - -#ifdef GL_SPECULAR -#define CGL_SPECULAR GL_SPECULAR -#else -#define CGL_SPECULAR 0x1202 -#endif - -#ifdef GL_EMISSION -#define CGL_EMISSION GL_EMISSION -#else -#define CGL_EMISSION 0x1600 -#endif - -#ifdef GL_SHININESS -#define CGL_SHININESS GL_SHININESS -#else -#define CGL_SHININESS 0x1601 -#endif - G_END_DECLS #endif diff --git a/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.c b/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.c index 267071f23..65d29956f 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.c +++ b/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.c @@ -118,7 +118,7 @@ initialize_texture_units (CoglGles2Wrapper *w) /* We will need to set the matrix mode to GL_TEXTURE to * initialise any new texture units, so we save the current * mode for restoring afterwards */ - GE( cogl_wrap_glGetIntegerv (CGL_MATRIX_MODE, &prev_mode)); + GE( cogl_wrap_glGetIntegerv (GL_MATRIX_MODE, &prev_mode)); for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++) { @@ -1281,7 +1281,7 @@ cogl_wrap_glGetIntegerv (GLenum pname, GLint *params) *params = 0; break; - case CGL_MATRIX_MODE: + case GL_MATRIX_MODE: *params = w->matrix_mode; break; diff --git a/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.h b/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.h index 4968e251a..52b812385 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.h +++ b/clutter/cogl/cogl/driver/gles/cogl-gles2-wrapper.h @@ -224,12 +224,14 @@ struct _CoglGles2WrapperShader #ifndef GL_MODELVIEW +#define GL_MATRIX_MODE 0x0BA0 + #define GL_MODELVIEW 0x1700 #define GL_PROJECTION 0x1701 #ifdef COGL_ENABLE_DEBUG -#define GL_STACK_OVERFLOW 0x0503 -#define GL_STACK_UNDERFLOW 0x0504 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 #endif #define GL_VERTEX_ARRAY 0x8074 @@ -268,35 +270,36 @@ struct _CoglGles2WrapperShader #define GL_EXP 0x8000 #define GL_EXP2 0x8001 -#define GL_ADD CGL_ADD -#define GL_ADD_SIGNED CGL_ADD_SIGNED -#define GL_INTERPOLATE CGL_INTERPOLATE -#define GL_SUBTRACT CGL_SUBTRACT -#define GL_DOT3_RGB CGL_DOT3_RGB -#define GL_DOT3_RGBA CGL_DOT3_RGBA -#define GL_CONSTANT CGL_CONSTANT -#define GL_PRIMARY_COLOR CGL_PRIMARY_COLOR -#define GL_PREVIOUS CGL_PREVIOUS -#define GL_COMBINE CGL_COMBINE -#define GL_COMBINE_RGB CGL_COMBINE_RGB -#define GL_COMBINE_ALPHA CGL_COMBINE_ALPHA -#define GL_SRC0_RGB CGL_SRC0_RGB -#define GL_OPERAND0_RGB CGL_OPERAND0_RGB -#define GL_SRC1_RGB CGL_SRC1_RGB -#define GL_OPERAND1_RGB CGL_OPERAND1_RGB -#define GL_SRC2_RGB CGL_SRC2_RGB -#define GL_OPERAND2_RGB CGL_OPERAND2_RGB -#define GL_SRC0_ALPHA CGL_SRC0_ALPHA -#define GL_OPERAND0_ALPHA CGL_OPERAND0_ALPHA -#define GL_SRC1_ALPHA CGL_SRC1_ALPHA -#define GL_OPERAND1_ALPHA CGL_OPERAND1_ALPHA -#define GL_SRC2_ALPHA CGL_SRC2_ALPHA -#define GL_OPERAND2_ALPHA CGL_OPERAND2_ALPHA -#define GL_AMBIENT CGL_AMBIENT -#define GL_DIFFUSE CGL_DIFFUSE -#define GL_SPECULAR CGL_SPECULAR -#define GL_EMISSION CGL_EMISSION -#define GL_SHININESS CGL_SHININESS +#define GL_MODULATE 0x2100 +#define GL_ADD 0x0104 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84e7 +#define GL_DOT3_RGB 0x86ae +#define GL_DOT3_RGBA 0x86af +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SRC0_RGB 0x8580 +#define GL_OPERAND0_RGB 0x8590 +#define GL_SRC1_RGB 0x8581 +#define GL_OPERAND1_RGB 0x8591 +#define GL_SRC2_RGB 0x8582 +#define GL_OPERAND2_RGB 0x8592 +#define GL_SRC0_ALPHA 0x8588 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_SRC1_ALPHA 0x8589 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_SRC2_ALPHA 0x858a +#define GL_OPERAND2_ALPHA 0x859a +#define GL_AMBIENT 0x1200 +#define GL_DIFFUSE 0x1201 +#define GL_SPECULAR 0x1202 +#define GL_EMISSION 0x1600 +#define GL_SHININESS 0x1601 #endif /* GL_MODELVIEW */ diff --git a/clutter/glx/clutter-glx-texture-pixmap.c b/clutter/glx/clutter-glx-texture-pixmap.c index f1ac9d318..af0b1fb15 100644 --- a/clutter/glx/clutter-glx-texture-pixmap.c +++ b/clutter/glx/clutter-glx-texture-pixmap.c @@ -250,7 +250,7 @@ clutter_glx_texture_pixmap_free_rectangle (ClutterGLXTexturePixmap *texture) cogl_texture_get_gl_texture (cogl_tex, &gl_handle, &gl_target); - if (gl_target == CGL_TEXTURE_RECTANGLE_ARB) + if (gl_target == GL_TEXTURE_RECTANGLE_ARB) glDeleteTextures (1, &gl_handle); } @@ -351,12 +351,12 @@ create_cogl_texture (ClutterTexture *texture, using_rectangle = TRUE; glGenTextures (1, &tex); - glBindTexture (CGL_TEXTURE_RECTANGLE_ARB, tex); - glTexImage2D (CGL_TEXTURE_RECTANGLE_ARB, 0, + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, tex); + glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, gl_format, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - handle = cogl_texture_new_from_foreign (tex, CGL_TEXTURE_RECTANGLE_ARB, + handle = cogl_texture_new_from_foreign (tex, GL_TEXTURE_RECTANGLE_ARB, width, height, 0, 0, cogl_format | COGL_BGR_BIT | From 778e08e4e24822b0875e6aeaf168cb5cecfc8a7f Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 12 Jan 2010 14:49:55 +0000 Subject: [PATCH 72/83] cogl-framebuffer: Add some missing GL defines Since 755cce33a7 the framebuffer code is using the GL enums GL_DEPTH_ATTACHMENT and GL_DEPTH_COMPONENT16. These aren't available directly under GLES except with the OES suffix so we need to define them manually as we do with the other framebuffer constants. --- clutter/cogl/cogl/cogl-framebuffer.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clutter/cogl/cogl/cogl-framebuffer.c b/clutter/cogl/cogl/cogl-framebuffer.c index a6b62bed8..64f586385 100644 --- a/clutter/cogl/cogl/cogl-framebuffer.c +++ b/clutter/cogl/cogl/cogl-framebuffer.c @@ -74,6 +74,12 @@ #ifndef GL_DEPTH_STENCIL #define GL_DEPTH_STENCIL 0x84F9 #endif +#ifndef GL_DEPTH_ATTACHMENT +#define GL_DEPTH_ATTACHMENT 0x8D00 +#endif +#ifndef GL_DEPTH_COMPONENT16 +#define GL_DEPTH_COMPONENT16 0x81A5 +#endif typedef enum { _TRY_DEPTH_STENCIL = 1L<<0, From 18d96005ec4d1395d70d71f2bef6cc378f4afb43 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 2 Nov 2009 17:08:55 +0000 Subject: [PATCH 73/83] texture: Remove reading the texture data back in ::unrealize() Reading back the texture data in unrealize does not seem like a desirable feature any more, clutter has evolved a lot since it was implemented. What's wrong with it now: * It takes *a lot* of time to read the data back with glReadPixel(), * When several textures share the same CoglTexture, the same data can be read back multiple times, * If the underlying material uses multiple texture units, only the first one was copied back, * In ClutterCairoTexture, we end up having two separate copies of the data, * GL actually manages texture memory accross system/video memory for us! For all the reasons above, let's get rid of the glReadPixel() in Texture::unrealize() Fixes: OHB#1842 --- clutter/clutter-texture.c | 163 ++------------------------------------ 1 file changed, 8 insertions(+), 155 deletions(-) diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index a6edc9827..65b5988c0 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -34,12 +34,6 @@ * clutter_texture_set_from_file() functions are used to copy image * data into texture memory and subsequently realize the texture. * - * If texture reads are supported by underlying GL implementation, - * unrealizing frees image data from texture memory moving to main - * system memory. Re-realizing then performs the opposite operation. - * This process allows basic management of commonly limited available - * texture memory. - * * Note: a ClutterTexture will scale its contents to fit the bounding * box requested using clutter_actor_set_size(). To display an area of * a texture without scaling, you should set the clip area using @@ -86,16 +80,9 @@ struct _ClutterTexturePrivate ClutterActor *fbo_source; CoglHandle fbo_handle; - /* Non video memory copy of image data */ - guint local_data_width, local_data_height; - guint local_data_rowstride; - guint local_data_has_alpha; - guchar *local_data; - guint sync_actor_size : 1; guint repeat_x : 1; guint repeat_y : 1; - guint in_dispose : 1; guint keep_aspect_ratio : 1; guint load_size_async : 1; guint load_data_async : 1; @@ -172,12 +159,6 @@ static GStaticMutex upload_list_mutex = G_STATIC_MUTEX_INIT; static void texture_fbo_free_resources (ClutterTexture *texture); -static void -clutter_texture_save_to_local_data (ClutterTexture *texture); - -static void -clutter_texture_load_from_local_data (ClutterTexture *texture); - GQuark clutter_texture_error_quark (void) { @@ -241,14 +222,6 @@ clutter_texture_unrealize (ClutterActor *actor) if (priv->material == COGL_INVALID_HANDLE) return; - /* there's no need to read the pixels back when unrealizing inside - * a dispose run, and the dispose() call will release the GL - * texture data as well, so we can safely bail out now - */ - if ((CLUTTER_PRIVATE_FLAGS (actor) & CLUTTER_ACTOR_IN_DESTRUCTION) || - priv->in_dispose) - return; - CLUTTER_MARK(); if (priv->fbo_source != NULL) @@ -260,24 +233,6 @@ clutter_texture_unrealize (ClutterActor *actor) return; } - if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_READ_PIXELS)) - { - /* Move image data from video to main memory. - * GL/ES cant do this - it probably makes sense - * to move this kind of thing into a ClutterProxyTexture - * where this behaviour can be better controlled. - * - * Or make it controllable via a property. - */ - if (priv->local_data == NULL) - { - clutter_texture_save_to_local_data (texture); - CLUTTER_NOTE (TEXTURE, "moved pixels into system mem"); - } - - texture_free_gl_resources (texture); - } - CLUTTER_NOTE (TEXTURE, "Texture unrealized"); } @@ -327,25 +282,14 @@ clutter_texture_realize (ClutterActor *actor) return; } - if (priv->local_data != NULL) - { - /* Move any local image data we have from unrealization - * back into video memory. - */ - clutter_texture_load_from_local_data (texture); - } - else - { - /* If we have no data, then realization is a no-op but - * we still want to be in REALIZED state to maintain - * invariants. We may have already created the texture - * if someone set some data earlier, or we may create it - * later if someone sets some data later. The fact that - * we may have created it earlier is really a bug, since - * it means ClutterTexture can have GL resources without - * being realized. - */ - } + /* If the texture is not a FBO, then realization is a no-op but + * we still want to be in REALIZED state to maintain invariants. + * We may have already created the texture if someone set some + * data earlier, or we may create it later if someone sets some + * data later. The fact that we may have created it earlier is + * really a bug, since it means ClutterTexture can have GL + * resources without being realized. + */ CLUTTER_NOTE (TEXTURE, "Texture realized"); } @@ -705,23 +649,9 @@ clutter_texture_dispose (GObject *object) priv = texture->priv; - /* mark that we are in dispose, so when the parent class' - * dispose implementation will call unrealize on us we'll - * not try to copy back the resources from video memory - * to system memory - */ - if (!priv->in_dispose) - priv->in_dispose = TRUE; - texture_free_gl_resources (texture); texture_fbo_free_resources (texture); - if (priv->local_data != NULL) - { - g_free (priv->local_data); - priv->local_data = NULL; - } - clutter_texture_async_load_cancel (texture); G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object); @@ -1177,78 +1107,9 @@ clutter_texture_init (ClutterTexture *self) priv->sync_actor_size = TRUE; priv->material = cogl_material_new (); priv->fbo_handle = COGL_INVALID_HANDLE; - priv->local_data = NULL; priv->keep_aspect_ratio = FALSE; } -static void -clutter_texture_save_to_local_data (ClutterTexture *texture) -{ - ClutterTexturePrivate *priv; - int bpp; - CoglPixelFormat pixel_format; - CoglHandle cogl_texture; - - priv = texture->priv; - - if (priv->local_data) - { - g_free (priv->local_data); - priv->local_data = NULL; - } - - if (priv->material == COGL_INVALID_HANDLE) - return; - - cogl_texture = clutter_texture_get_cogl_texture (texture); - - priv->local_data_width = cogl_texture_get_width (cogl_texture); - priv->local_data_height = cogl_texture_get_height (cogl_texture); - pixel_format = cogl_texture_get_format (cogl_texture); - priv->local_data_has_alpha = pixel_format & COGL_A_BIT; - bpp = priv->local_data_has_alpha ? 4 : 3; - - /* Align to 4 bytes */ - priv->local_data_rowstride = (priv->local_data_width * bpp + 3) & ~3; - - priv->local_data = g_malloc (priv->local_data_rowstride - * priv->local_data_height); - - if (cogl_texture_get_data (cogl_texture, - priv->local_data_has_alpha - ? COGL_PIXEL_FORMAT_RGBA_8888_PRE - : COGL_PIXEL_FORMAT_RGB_888, - priv->local_data_rowstride, - priv->local_data) == 0) - { - g_free (priv->local_data); - priv->local_data = NULL; - } -} - -static void -clutter_texture_load_from_local_data (ClutterTexture *texture) -{ - ClutterTexturePrivate *priv; - - priv = texture->priv; - - if (priv->local_data == NULL) - return; - - clutter_texture_set_from_rgb_data (texture, - priv->local_data, - priv->local_data_has_alpha, - priv->local_data_width, - priv->local_data_height, - priv->local_data_rowstride, - priv->local_data_has_alpha ? 4: 3, - CLUTTER_TEXTURE_RGB_FLAG_PREMULT, NULL); - - g_free (priv->local_data); - priv->local_data = NULL; -} - /** * clutter_texture_get_cogl_material: * @texture: A #ClutterTexture @@ -1393,15 +1254,7 @@ clutter_texture_set_cogl_texture (ClutterTexture *texture, /* Remove old texture */ texture_free_gl_resources (texture); - /* Free any saved data so realization doesn't resend it to GL */ - if (priv->local_data) - { - g_free (priv->local_data); - priv->local_data = NULL; - } - /* Use the new texture */ - cogl_material_set_layer (priv->material, 0, cogl_tex); /* The material now holds a reference to the texture so we can From 5322546a4e2100bcec8aba651f8de7d849d67711 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 13 Jan 2010 15:40:36 +0000 Subject: [PATCH 74/83] build: Clean up COGL build flags --- clutter/cogl/cogl/Makefile.am | 53 +++++++++++++++++------------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/clutter/cogl/cogl/Makefile.am b/clutter/cogl/cogl/Makefile.am index 7d78106f5..59121ab09 100644 --- a/clutter/cogl/cogl/Makefile.am +++ b/clutter/cogl/cogl/Makefile.am @@ -34,6 +34,17 @@ INCLUDES = \ -I$(top_builddir)/clutter/cogl \ $(NULL) +AM_CPPFLAGS = \ + -DG_DISABLE_SINGLE_INCLUDES \ + -DG_DISABLE_DEPRECATED \ + -DG_LOG_DOMAIN=\"Cogl-$(COGL_WINSYS)\" \ + -DCLUTTER_COMPILATION \ + $(COGL_DEBUG_CFLAGS) \ + $(CLUTTER_DEBUG_CFLAGS) \ + $(NULL) + +AM_CFLAGS = $(CLUTTER_CFLAGS) $(MAINTAINER_CFLAGS) + cogl-defines.h: $(top_builddir)/clutter/cogl/cogl/driver/gl/cogl-defines.h $(top_builddir)/clutter/cogl/cogl/driver/gles/cogl-defines.h $(QUIET_GEN)cp -f $(top_builddir)/clutter/cogl/cogl/driver/$(COGL_DRIVER)/cogl-defines.h $(@F) @@ -68,27 +79,7 @@ cogl_winsys_sources = \ $(srcdir)/winsys/cogl-win32.c \ $(NULL) -# glib-mkenums rules -glib_enum_h = cogl-enum-types.h -glib_enum_c = cogl-enum-types.c -glib_enum_headers = $(cogl_public_h) -include $(top_srcdir)/build/autotools/Makefile.am.enums - -noinst_LTLIBRARIES = libclutter-cogl.la -libclutter_cogl_la_CPPFLAGS = \ - $(COGL_DEBUG_CFLAGS) \ - $(CLUTTER_DEBUG_CFLAGS) \ - -DG_DISABLE_SINGLE_INCLUDES \ - -DG_LOG_DOMAIN=\"Cogl-$(COGL_WINSYS)\" \ - -DCLUTTER_COMPILATION - -libclutter_cogl_la_CFLAGS = $(CLUTTER_CFLAGS) $(MAINTAINER_CFLAGS) - -libclutter_cogl_la_LIBADD = -lm $(CLUTTER_LIBS) \ - $(top_builddir)/clutter/cogl/cogl/driver/$(COGL_DRIVER)/libclutter-cogl-driver.la - -libclutter_cogl_la_SOURCES = \ - $(BUILT_SOURCES) \ +cogl_sources_c = \ $(srcdir)/winsys/cogl-winsys.h \ $(srcdir)/winsys/cogl-@COGL_WINSYS@.c \ $(srcdir)/cogl-handle.h \ @@ -138,6 +129,18 @@ libclutter_cogl_la_SOURCES = \ $(srcdir)/cogl-profile.c \ $(NULL) +# glib-mkenums rules +glib_enum_h = cogl-enum-types.h +glib_enum_c = cogl-enum-types.c +glib_enum_headers = $(cogl_public_h) +include $(top_srcdir)/build/autotools/Makefile.am.enums + +noinst_LTLIBRARIES = libclutter-cogl.la + +libclutter_cogl_la_LIBADD = -lm $(CLUTTER_LIBS) $(top_builddir)/clutter/cogl/cogl/driver/$(COGL_DRIVER)/libclutter-cogl-driver.la + +libclutter_cogl_la_SOURCES = $(BUILT_SOURCES) $(cogl_sources_c) + EXTRA_DIST += $(cogl_winsys_sources) EXTRA_DIST += stb_image.c @@ -156,11 +159,8 @@ if HAVE_INTROSPECTION Cogl-@CLUTTER_API_VERSION@.gir: $(INTROSPECTION_SCANNER) libclutter-cogl.la $(QUIET_GEN)$(INTROSPECTION_SCANNER) -v \ --namespace Cogl --nsversion=@CLUTTER_API_VERSION@ \ - -I$(srcdir) \ - -I$(srcdir)/.. \ - -I$(srcdir)/winsys \ - -I$(srcdir)/driver/@COGL_DRIVER@ \ - -DCLUTTER_COMPILATION \ + $(INCLUDES) \ + $(AM_CPPFLAGS) \ --c-include='cogl/cogl.h' \ --include=GL-1.0 \ --include=GObject-2.0 \ @@ -177,4 +177,3 @@ gir_DATA = $(BUILT_GIRSOURCES) CLEANFILES += $(BUILT_GIRSOURCES) endif - From 948db40c875f8eb7032582a32515737be6587067 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 4 Jan 2010 14:53:03 +0000 Subject: [PATCH 75/83] Add gcov support to the build Using gcov it's possible to get a coverage report, that is a break down of how much the exposed API is exercised by the conformance test suite. --- .gitignore | 2 ++ Makefile.am | 3 +++ build/autotools/Makefile.am | 1 + build/autotools/Makefile.am.gcov | 33 ++++++++++++++++++++++++++++++++ clutter/Makefile.am | 20 ++++++++++--------- configure.ac | 26 +++++++++++++++++++++++++ 6 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 build/autotools/Makefile.am.gcov diff --git a/.gitignore b/.gitignore index bdcab8ce2..0e048e5a0 100644 --- a/.gitignore +++ b/.gitignore @@ -11,11 +11,13 @@ clutter.pc *.o *.lo *.la +*.gcov ChangeLog* clutter/clutter-enum-types.[ch] clutter/clutter-marshal.[ch] clutter/clutter-version.h clutter/stamp-* +/clutter/gcov-report.txt /clutter/clutter-json.h /clutter/cogl/cogl/cogl-defines.h /clutter/cogl/cogl/*.pc diff --git a/Makefile.am b/Makefile.am index f003afeb1..fc7ef37d3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,3 +48,6 @@ MAINTAINERCLEANFILES = \ $(NULL) include $(top_srcdir)/build/autotools/Makefile.am.changelog + +gcov: + @( cd clutter && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? diff --git a/build/autotools/Makefile.am b/build/autotools/Makefile.am index bbdcf8955..663c3edf3 100644 --- a/build/autotools/Makefile.am +++ b/build/autotools/Makefile.am @@ -5,6 +5,7 @@ EXTRA_DIST = \ Makefile.am.marshal \ Makefile.am.enums \ Makefile.am.changelog \ + Makefile.am.gcov \ dolt.m4 \ introspection.m4 \ gtk-doc.m4 \ diff --git a/build/autotools/Makefile.am.gcov b/build/autotools/Makefile.am.gcov new file mode 100644 index 000000000..a16edbd76 --- /dev/null +++ b/build/autotools/Makefile.am.gcov @@ -0,0 +1,33 @@ +if GCOV_ENABLED +gcov-report.txt: gcov-clean + $(QUIET_GEN)(rm -f $@; \ + echo -e "Test coverage for Clutter:\n" >> $@; \ + total_covered=0; total_actual=0; \ + for file in $(filter %.c,$(gcov_sources)); do \ + gcov -o .libs/$${file/.c/.gcda} $$file > /dev/null; \ + if test -f $$file.gcov; then \ + actual=`grep -v ' -:' $$file.gcov | wc -l`; \ + uncovered=`grep '#####:' $$file.gcov | wc -l`; \ + covered=$$((actual - uncovered)); \ + total_covered=$$((total_covered + covered)); \ + total_actual=$$((total_actual + actual)); \ + echo -e "$$file: \t$$covered / $$actual\t($$((($$covered * 100) / $$actual))%)"; \ + fi \ + done >> $@; \ + cd $(abs_srcdir); \ + echo -e "\nSource lines: $$total_actual\nCovered statements: $$total_covered\nTotal coverage: $$((($$total_covered * 100) / $$total_actual))%" >> $@) + +gcov: gcov-report.txt + @echo ""; cat gcov-report.txt +gcov-clean: + @find . -name "*.gcda" -o -name "*.gcov" -delete +else +gcov-report.txt: + @true +gcov-clean: + @true +gcov: + @echo "Need to reconfigure with --enable-gcov" +endif # GCOV_ENABLED + +.PHONY: gcov gcov-clean gcov-report.txt diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 1a23d9978..401b3e5e9 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -41,14 +41,7 @@ AM_CPPFLAGS = \ $(CLUTTER_DEBUG_CFLAGS) \ $(NULL) -AM_CFLAGS = $(CLUTTER_CFLAGS) $(MAINTAINER_CFLAGS) - -LDADD = \ - $(CLUTTER_LT_LDFLAGS) \ - -export-dynamic \ - -export-symbols-regex "^(clutter|cogl|json).*" \ - -rpath $(libdir) \ - $(NULL) +AM_CFLAGS = $(CLUTTER_CFLAGS) $(MAINTAINER_CFLAGS) $(GCOV_CFLAGS) # please, keep this sorted alphabetically source_h = \ @@ -220,7 +213,13 @@ libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_SOURCES = \ $(source_c_priv) \ $(source_h_priv) -libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_LDFLAGS = $(LDADD) +libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_LDFLAGS = \ + $(CLUTTER_LT_LDFLAGS) \ + $(GCOV_LDFLAGS) \ + -export-dynamic \ + -export-symbols-regex "^(clutter|cogl|json).*" \ + -rpath $(libdir) \ + $(NULL) lib_LTLIBRARIES = $(CLUTTER_WINSYS_LIB) @@ -322,3 +321,6 @@ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) CLEANFILES += $(BUILT_GIRSOURCES) $(typelibs_DATA) endif # HAVE_INTROSPECTION + +gcov_sources = $(source_c) +include $(top_srcdir)/build/autotools/Makefile.am.gcov diff --git a/configure.ac b/configure.ac index 93f0b5473..302e6686b 100644 --- a/configure.ac +++ b/configure.ac @@ -720,6 +720,31 @@ AM_CONDITIONAL(PROFILE, test "x$enable_profile" != "xno") AC_SUBST(CLUTTER_PROFILE_CFLAGS) AC_SUBST(CLUTTER_PROFILE_LDFLAGS) +dnl === Coverage report ======================================================= + +AC_PATH_PROG([GCOV], [gcov], [enable_gcov=no]) + +AC_MSG_CHECKING([whether to build with gcov testing]) + +AC_ARG_ENABLE([gcov], + [AS_HELP_STRING([--enable-gcov], + [Whether to enable coverage testing (requires gcc +and gcov)])], + [], + [enable_gcov=no]) + +AS_IF([test "x$enable_gcov" = "xyes" && test "x$GCC" = "xyes"], + [ + GCOV_CFLAGS="-g -O0 -fprofile-arcs -ftest-coverage" + GCOV_LDFLAGS="-lgcov" + ] +) + +AM_CONDITIONAL([GCOV_ENABLED], [test "x$enable_gcov" = "xyes"]) +AC_SUBST([GCOV_CFLAGS]) +AC_SUBST([GCOV_LDFLAGS]) +AC_MSG_RESULT([$enable_gcov]) + dnl === Enable strict compiler flags ========================================== # use strict compiler flags only on development releases @@ -908,6 +933,7 @@ echo " Clutter debug level: ${enable_debug}" echo " COGL debug level: ${enable_cogl_debug}" echo " Compiler flags: ${MAINTAINER_CFLAGS}" echo " Profiling enabled: ${enable_profile}" +echo " Enable coverage tests: ${enable_gcov}" # Documentation echo "" From a076e0e11d222ca17dbdbadb3aa2c762d32a5730 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 13 Jan 2010 16:35:53 +0000 Subject: [PATCH 76/83] build: Add a script to format gcov report lines Instead of using echo let's try Perl and the format() built-in. --- build/Makefile.am | 2 ++ build/autotools/Makefile.am.gcov | 2 +- build/gen-gcov.pl | 44 ++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100755 build/gen-gcov.pl diff --git a/build/Makefile.am b/build/Makefile.am index 8c902377b..96b39e1ba 100644 --- a/build/Makefile.am +++ b/build/Makefile.am @@ -1 +1,3 @@ SUBDIRS = autotools + +EXTRA_DIST = gen-gcov.pl diff --git a/build/autotools/Makefile.am.gcov b/build/autotools/Makefile.am.gcov index a16edbd76..1e2cb924e 100644 --- a/build/autotools/Makefile.am.gcov +++ b/build/autotools/Makefile.am.gcov @@ -11,7 +11,7 @@ gcov-report.txt: gcov-clean covered=$$((actual - uncovered)); \ total_covered=$$((total_covered + covered)); \ total_actual=$$((total_actual + actual)); \ - echo -e "$$file: \t$$covered / $$actual\t($$((($$covered * 100) / $$actual))%)"; \ + perl $(top_builddir)/build/gen-gcov.pl $$file.gcov; \ fi \ done >> $@; \ cd $(abs_srcdir); \ diff --git a/build/gen-gcov.pl b/build/gen-gcov.pl new file mode 100755 index 000000000..b4b06b1d8 --- /dev/null +++ b/build/gen-gcov.pl @@ -0,0 +1,44 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +our $gcov_file = $ARGV[0] or undef; + +open my $g, '<', $gcov_file + or die("Unable to open '$gcov_file': $!"); + +my ($actual, $covered, $uncovered, $percent) = (0, 0, 0, 0); + +while (<$g>) { + my $report_line = $_; + + chomp($report_line); + + $actual += 1; + $actual -= 1 if $report_line =~ / -:/; + + $uncovered += 1 if $report_line =~ /#####:/; +} + +close($g); + +$covered = $actual - $uncovered; +$percent = int(($covered * 100) / $actual); + +$gcov_file =~ s/^\.\///g; +$gcov_file =~ s/\.gcov$//g; + +my $cover_file = "$gcov_file:"; +my $cover_literal = "$covered / $actual"; +my $cover_percent = "$percent%"; + +format ReportLine = +@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>> @>>>>> +$cover_file, $cover_literal, $cover_percent +. + +$~ = 'ReportLine'; +write; + +0; From ed735ae730ea2140ef4d1be9d1947dbdce8b1f28 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 13 Jan 2010 16:57:55 +0000 Subject: [PATCH 77/83] Add test-cogl-multitexture to the Git ignore file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0e048e5a0..b63cce8ce 100644 --- a/.gitignore +++ b/.gitignore @@ -243,6 +243,7 @@ TAGS /tests/micro-bench/test-text /tests/micro-bench/test-picking /tests/tools/disable-npots.sh +/tests/conform/test-cogl-multitexture /clutter/x11/clutter-x11-enum-types.[ch] /clutter/x11/stamp-clutter-x11-enum-types.h /po/Makefile.in.in From 6e82d11daf8b1d7259c26e6a0816392a71c726f3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 13 Jan 2010 17:31:13 +0000 Subject: [PATCH 78/83] units: Improve coverage of clutter_units_from_string() Add a unit for an empty string as well as units for the missing unit types like cm, mm and px. --- tests/conform/test-clutter-units.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/conform/test-clutter-units.c b/tests/conform/test-clutter-units.c index 105c454a9..d50a41689 100644 --- a/tests/conform/test-clutter-units.c +++ b/tests/conform/test-clutter-units.c @@ -61,10 +61,21 @@ test_units_string (TestConformSimpleFixture *fixture, ClutterUnits units; gchar *string; + g_assert (clutter_units_from_string (&units, "") == FALSE); + g_assert (clutter_units_from_string (&units, "10") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_PIXEL); g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 10); + g_assert (clutter_units_from_string (&units, "10 px") == TRUE); + g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_PIXEL); + + g_assert (clutter_units_from_string (&units, "10 mm") == TRUE); + g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_MM); + + g_assert (clutter_units_from_string (&units, "10 cm") == TRUE); + g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_CM); + g_assert (clutter_units_from_string (&units, "10 ") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_PIXEL); g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 10); From 4388509a1589dbac7aae0ebe5995ca0e06f7c70c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 14 Jan 2010 12:28:07 +0000 Subject: [PATCH 79/83] master-clock: Add profiling timers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the newly-added profiling timers inside the master clock dispatch function to see how much time we spend: • in the whole function • in the event processing for each stage • in the timeline advancement --- clutter/clutter-master-clock.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/clutter/clutter-master-clock.c b/clutter/clutter-master-clock.c index f278c13db..b61ad436c 100644 --- a/clutter/clutter-master-clock.c +++ b/clutter/clutter-master-clock.c @@ -38,6 +38,7 @@ #include "clutter-master-clock.h" #include "clutter-debug.h" #include "clutter-private.h" +#include "clutter-profile.h" #define CLUTTER_MASTER_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_MASTER_CLOCK, ClutterMasterClockClass)) #define CLUTTER_IS_MASTER_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_MASTER_CLOCK)) @@ -262,6 +263,19 @@ clutter_clock_dispatch (GSource *source, ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); GSList *stages, *l; + CLUTTER_STATIC_TIMER (master_dispatch_timer, + "Mainloop", + "Master Clock", + "Master clock dispatch", + 0); + CLUTTER_STATIC_TIMER (master_event_process, + "Master Clock", + "Event Processing", + "The time spent processing events on all stages", + 0); + + CLUTTER_TIMER_START (_clutter_uprof_context, master_dispatch_timer); + CLUTTER_NOTE (SCHEDULER, "Master clock [tick]"); clutter_threads_enter (); @@ -276,6 +290,8 @@ clutter_clock_dispatch (GSource *source, stages = clutter_stage_manager_list_stages (stage_manager); g_slist_foreach (stages, (GFunc)g_object_ref, NULL); + CLUTTER_TIMER_START (_clutter_uprof_context, master_event_process); + master_clock->updated_stages = FALSE; /* Process queued events @@ -283,7 +299,10 @@ clutter_clock_dispatch (GSource *source, for (l = stages; l != NULL; l = l->next) _clutter_stage_process_queued_events (l->data); + CLUTTER_TIMER_STOP (_clutter_uprof_context, master_event_process); + _clutter_master_clock_advance (master_clock); + _clutter_run_repaint_functions (); /* Update any stage that needs redraw/relayout after the clock @@ -299,6 +318,8 @@ clutter_clock_dispatch (GSource *source, clutter_threads_leave (); + CLUTTER_TIMER_STOP (_clutter_uprof_context, master_dispatch_timer); + return TRUE; } @@ -427,6 +448,12 @@ _clutter_master_clock_advance (ClutterMasterClock *master_clock) { GSList *l, *next; + CLUTTER_STATIC_TIMER (master_timeline_advance, + "Master Clock", + "Timelines Advancement", + "The time spent advancing all timelines", + 0); + g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock)); /* we protect ourselves from timelines being removed during @@ -434,6 +461,8 @@ _clutter_master_clock_advance (ClutterMasterClock *master_clock) */ g_slist_foreach (master_clock->timelines, (GFunc) g_object_ref, NULL); + CLUTTER_TIMER_START (_clutter_uprof_context, master_timeline_advance); + for (l = master_clock->timelines; l != NULL; l = next) { next = l->next; @@ -441,6 +470,8 @@ _clutter_master_clock_advance (ClutterMasterClock *master_clock) clutter_timeline_do_tick (l->data, &master_clock->cur_tick); } + CLUTTER_TIMER_STOP (_clutter_uprof_context, master_timeline_advance); + g_slist_foreach (master_clock->timelines, (GFunc) g_object_unref, NULL); } From c6ce9c338966188248c3c1ae7b10480205fe20bf Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 14 Jan 2010 14:07:04 +0000 Subject: [PATCH 80/83] conform: Add operators tests for ClutterColor The clutter_color_add() and clutter_color_subtract() functions are lacking unit testing to catch eventual regressions. --- .gitignore | 1 + tests/conform/test-color.c | 57 +++++++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 1 + 3 files changed, 59 insertions(+) diff --git a/.gitignore b/.gitignore index b63cce8ce..715337746 100644 --- a/.gitignore +++ b/.gitignore @@ -239,6 +239,7 @@ TAGS /tests/conform/test-script-animation /tests/conform/test-script-named-object /tests/conform/test-actor-destruction +/tests/conform/test-color-operators /tests/micro-bench/test-text-perf /tests/micro-bench/test-text /tests/micro-bench/test-picking diff --git a/tests/conform/test-color.c b/tests/conform/test-color.c index a19323bd9..71b080736 100644 --- a/tests/conform/test-color.c +++ b/tests/conform/test-color.c @@ -166,3 +166,60 @@ test_color_to_string (TestConformSimpleFixture *fixture, g_free (str); } + +void +test_color_operators (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterColor op1, op2; + ClutterColor res; + + clutter_color_from_pixel (&op1, 0xff0000ff); + g_assert_cmpint (op1.red, ==, 0xff); + g_assert_cmpint (op1.green, ==, 0); + g_assert_cmpint (op1.blue, ==, 0); + g_assert_cmpint (op1.alpha, ==, 0xff); + + clutter_color_from_pixel (&op2, 0x00ff00ff); + g_assert_cmpint (op2.red, ==, 0); + g_assert_cmpint (op2.green, ==, 0xff); + g_assert_cmpint (op2.blue, ==, 0); + g_assert_cmpint (op2.alpha, ==, 0xff); + + if (g_test_verbose ()) + g_print ("Adding %x, %x; expected result: %x\n", + clutter_color_to_pixel (&op1), + clutter_color_to_pixel (&op2), + 0xffff00ff); + + clutter_color_add (&op1, &op2, &res); + g_assert_cmpint (clutter_color_to_pixel (&res), ==, 0xffff00ff); + + if (g_test_verbose ()) + g_print ("Checking alpha channel on color add\n"); + + op1.alpha = 0xdd; + op2.alpha = 0xcc; + clutter_color_add (&op1, &op2, &res); + g_assert_cmpint (clutter_color_to_pixel (&res), ==, 0xffff00dd); + + clutter_color_from_pixel (&op1, 0xffffffff); + clutter_color_from_pixel (&op2, 0xff00ffff); + + if (g_test_verbose ()) + g_print ("Subtracting %x, %x; expected result: %x\n", + clutter_color_to_pixel (&op1), + clutter_color_to_pixel (&op2), + 0x00ff00ff); + + clutter_color_subtract (&op1, &op2, &res); + g_assert_cmpint (clutter_color_to_pixel (&res), ==, 0x00ff00ff); + + if (g_test_verbose ()) + g_print ("Checking alpha channel on color subtract\n"); + + op1.alpha = 0xdd; + op2.alpha = 0xcc; + clutter_color_subtract (&op1, &op2, &res); + g_assert_cmpint (clutter_color_to_pixel (&res), ==, 0x00ff00cc); +} diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 26c045172..72d69b92e 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -160,6 +160,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/color", test_color_from_string); TEST_CONFORM_SIMPLE ("/color", test_color_to_string); TEST_CONFORM_SIMPLE ("/color", test_color_hls_roundtrip); + TEST_CONFORM_SIMPLE ("/color", test_color_operators); TEST_CONFORM_SIMPLE ("/units", test_units_constructors); TEST_CONFORM_SIMPLE ("/units", test_units_string); From 8247bdf4f9b940fe69165ae6556511fcc9ca3a5d Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 12 Jan 2010 21:44:40 +0000 Subject: [PATCH 81/83] cogl-framebuffer: Return gboolean from try_creating_fbo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When try_creating_fbo fails it returns 0 to report the error and if it succeeds it returns ‘flags’. However cogl_offscreen_new_to_texture also passes in 0 for the flags as the last fallback to create the fbo with nothing but the color buffer. In that case it will return 0 regardless of whether it succeeded so the last fallback will always be considered a failure. To fix this it now just returns a gboolean to indicate whether it succeeded and the flags used for each attempt is assigned when passing the argument rather than from the return value of the function. Also if the only configuration that succeeded was with flags==0 then it would always try all combinations because last_working_flags would also be zero. To avoid this it now uses a separate gboolean to mark whether we found a successful set of flags. http://bugzilla.openedhand.com/show_bug.cgi?id=1873 --- clutter/cogl/cogl/cogl-framebuffer.c | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/clutter/cogl/cogl/cogl-framebuffer.c b/clutter/cogl/cogl/cogl-framebuffer.c index 64f586385..8124186d0 100644 --- a/clutter/cogl/cogl/cogl-framebuffer.c +++ b/clutter/cogl/cogl/cogl-framebuffer.c @@ -247,7 +247,7 @@ _cogl_framebuffer_get_projection_stack (CoglHandle handle) return framebuffer->projection_stack; } -static TryFBOFlags +static gboolean try_creating_fbo (CoglOffscreen *offscreen, TryFBOFlags flags, CoglHandle texture) @@ -263,14 +263,14 @@ try_creating_fbo (CoglOffscreen *offscreen, _COGL_GET_CONTEXT (ctx, FALSE); if (!cogl_texture_get_gl_texture (texture, &tex_gl_handle, &tex_gl_target)) - return 0; + return FALSE; if (tex_gl_target != GL_TEXTURE_2D #ifdef HAVE_COGL_GL && tex_gl_target != GL_TEXTURE_RECTANGLE_ARB #endif ) - return 0; + return FALSE; /* We are about to generate and bind a new fbo, so when next flushing the * journal, we will need to rebind the current framebuffer... */ @@ -352,18 +352,18 @@ try_creating_fbo (CoglOffscreen *offscreen, GLuint renderbuffer = GPOINTER_TO_UINT (l->data); GE (glDeleteRenderbuffers (1, &renderbuffer)); } - return 0; + return FALSE; } - return flags; + return TRUE; } CoglHandle cogl_offscreen_new_to_texture (CoglHandle texhandle) { CoglOffscreen *offscreen; - TryFBOFlags flags; - static TryFBOFlags last_working_flags = 0; + static TryFBOFlags flags; + static gboolean have_working_flags = FALSE; _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); @@ -393,33 +393,33 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle) offscreen = g_new0 (CoglOffscreen, 1); offscreen->texture = cogl_handle_ref (texhandle); - if (!(last_working_flags && - (flags = try_creating_fbo (offscreen, last_working_flags, - texhandle))) && - !(flags = try_creating_fbo (offscreen, _TRY_DEPTH_STENCIL, - texhandle)) && - !(flags = try_creating_fbo (offscreen, _TRY_DEPTH | _TRY_STENCIL, - texhandle)) && - !(flags = try_creating_fbo (offscreen, _TRY_STENCIL, texhandle)) && - !(flags = try_creating_fbo (offscreen, _TRY_DEPTH, texhandle)) && - !(flags = try_creating_fbo (offscreen, 0, texhandle))) + if ((have_working_flags && + try_creating_fbo (offscreen, flags, texhandle)) || + try_creating_fbo (offscreen, flags = _TRY_DEPTH_STENCIL, texhandle) || + try_creating_fbo (offscreen, flags = _TRY_DEPTH | _TRY_STENCIL, + texhandle) || + try_creating_fbo (offscreen, flags = _TRY_STENCIL, texhandle) || + try_creating_fbo (offscreen, flags = _TRY_DEPTH, texhandle) || + try_creating_fbo (offscreen, flags = 0, texhandle)) + { + /* Record that the last set of flags succeeded so that we can + try that set first next time */ + have_working_flags = TRUE; + + _cogl_framebuffer_init (COGL_FRAMEBUFFER (offscreen), + COGL_FRAMEBUFFER_TYPE_OFFSCREEN, + cogl_texture_get_width (texhandle), + cogl_texture_get_height (texhandle)); + + return _cogl_offscreen_handle_new (offscreen); + } + else { g_free (offscreen); - last_working_flags = 0; /* XXX: This API should probably have been defined to take a GError */ g_warning ("%s: Failed to create an OpenGL framebuffer", G_STRLOC); return COGL_INVALID_HANDLE; } - /* Save the final set of flags that worked so we can hopefully construct - * subsequent buffers faster. */ - last_working_flags = flags; - - _cogl_framebuffer_init (COGL_FRAMEBUFFER (offscreen), - COGL_FRAMEBUFFER_TYPE_OFFSCREEN, - cogl_texture_get_width (texhandle), - cogl_texture_get_height (texhandle)); - - return _cogl_offscreen_handle_new (offscreen); } static void From ba4622ff8bd1abde577fa26637fd1afec1b3abd6 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 14 Jan 2010 15:23:41 +0000 Subject: [PATCH 82/83] Add a notice of deprecation in the pre-Git ChangeLog --- ChangeLog.pre-git-import | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ChangeLog.pre-git-import b/ChangeLog.pre-git-import index 1b69d85ff..8a2962581 100644 --- a/ChangeLog.pre-git-import +++ b/ChangeLog.pre-git-import @@ -1,3 +1,9 @@ +# DO NOT MODIFY THIS FILE +# +# Clutter uses the Git commit log to generate the ChangeLog files when +# creating the tarball for releases and snapshots. This file is maintained +# only for historical reasons. + 2008-12-10 Neil Roberts * clutter/cogl/gl/cogl.c: From a70ebe4f52e192c11f8db6a4fa1f419a5b175ab3 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 15 Jan 2010 12:02:09 +0000 Subject: [PATCH 83/83] tests: Add a simple conformance test for texture mipmaps This adds a test which renders a texture into a 1x1 pixel quad with and without filters that use mipmaps. The pixel without mipmaps will be one of the colors from the texture and the one with will be the average of all the pixels in the texture. --- tests/conform/Makefile.am | 1 + tests/conform/test-cogl-texture-mipmaps.c | 147 ++++++++++++++++++++++ tests/conform/test-conform-main.c | 1 + 3 files changed, 149 insertions(+) create mode 100644 tests/conform/test-cogl-texture-mipmaps.c diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 9b743a8b8..3ae1cc756 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -25,6 +25,7 @@ test_conformance_SOURCES = \ test-cogl-offscreen.c \ test-cogl-readpixels.c \ test-cogl-multitexture.c \ + test-cogl-texture-mipmaps.c \ test-path.c \ test-pick.c \ test-clutter-rectangle.c \ diff --git a/tests/conform/test-cogl-texture-mipmaps.c b/tests/conform/test-cogl-texture-mipmaps.c new file mode 100644 index 000000000..be1b8933b --- /dev/null +++ b/tests/conform/test-cogl-texture-mipmaps.c @@ -0,0 +1,147 @@ +#include +#include +#include + +#include "test-conform-common.h" + +static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; + +#define TEX_SIZE 64 + +typedef struct _TestState +{ + guint frame; +} TestState; + +/* Creates a texture where the pixels are evenly divided between + selecting just one of the R,G and B components */ +static CoglHandle +make_texture (void) +{ + guchar *tex_data = g_malloc (TEX_SIZE * TEX_SIZE * 3), *p = tex_data; + CoglHandle tex; + int x, y; + + for (y = 0; y < TEX_SIZE; y++) + for (x = 0; x < TEX_SIZE; x++) + { + memset (p, 0, 3); + /* Set one of the components to full. The components should be + evenly represented so that each gets a third of the + texture */ + p[(p - tex_data) / (TEX_SIZE * TEX_SIZE * 3 / 3)] = 255; + p += 3; + } + + tex = cogl_texture_new_from_data (TEX_SIZE, TEX_SIZE, COGL_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGB_888, + COGL_PIXEL_FORMAT_ANY, + TEX_SIZE * 3, + tex_data); + + g_free (tex_data); + + return tex; +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + CoglHandle tex; + CoglHandle material; + guint8 pixels[8]; + + /* XXX: + * We haven't always had good luck with GL drivers implementing glReadPixels + * reliably and skipping the first two frames improves our chances... */ + if (state->frame++ <= 2) + { + g_usleep (G_USEC_PER_SEC); + return; + } + + tex = make_texture (); + material = cogl_material_new (); + cogl_material_set_layer (material, 0, tex); + cogl_handle_unref (tex); + + /* Render a 1x1 pixel quad without mipmaps */ + cogl_set_source (material); + cogl_material_set_layer_filters (material, 0, + COGL_MATERIAL_FILTER_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + cogl_rectangle (0, 0, 1, 1); + /* Then with mipmaps */ + cogl_material_set_layer_filters (material, 0, + COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST, + COGL_MATERIAL_FILTER_NEAREST); + cogl_rectangle (1, 0, 2, 1); + + cogl_material_unref (material); + + /* Read back the two pixels we rendered */ + cogl_read_pixels (0, 0, 2, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixels); + + /* The first pixel should be just one of the colors from the + texture. It doesn't matter which one */ + g_assert ((pixels[0] == 255 && pixels[1] == 0 && pixels[2] == 0) || + (pixels[0] == 0 && pixels[1] == 255 && pixels[2] == 0) || + (pixels[0] == 0 && pixels[1] == 0 && pixels[2] == 255)); + /* The second pixel should be more or less the average of all of the + pixels in the texture. Each component gets a third of the image + so each component should be approximately 255/3 */ + g_assert (ABS (pixels[4] - 255 / 3) <= 3 && + ABS (pixels[5] - 255 / 3) <= 3 && + ABS (pixels[6] - 255 / 3) <= 3); + + /* Comment this out if you want visual feedback for what this test paints */ +#if 1 + clutter_main_quit (); +#endif +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_cogl_texture_mipmaps (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + TestState state; + ClutterActor *stage; + ClutterActor *group; + guint idle_source; + + state.frame = 0; + + stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + + group = clutter_group_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + + g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (stage); + + clutter_main (); + + g_source_remove (idle_source); + + if (g_test_verbose ()) + g_print ("OK\n"); +} diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 72d69b92e..a090dc62c 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -187,6 +187,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_npot_texture); TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_multitexture); + TEST_CONFORM_SIMPLE ("/cogl/texture", test_cogl_texture_mipmaps); TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_contiguous); TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_interleved);