From 568d6841871bdfea5d49d2a8143a49a0a7664a7b Mon Sep 17 00:00:00 2001 From: Vladimir Nadvornik Date: Sun, 30 Aug 2009 12:36:11 +0200 Subject: [PATCH] [cogl-texture] Split CoglTexture into an abstract class + CoglTexture2dSliced cogl-texture-2d-sliced provides an implementation of CoglTexture and this seperation lays the foundation for potentially supporting atlas textures, pixmap textures (as in GLX_EXT_texture_from_pixmap) and fast-path GL_TEXTURE_{1D,2D,3D,RECTANGLE} textures in a maintainable fashion. --- clutter/cogl/cogl/Makefile.am | 2 + clutter/cogl/cogl/cogl-context.c | 2 + clutter/cogl/cogl/cogl-context.h | 2 + clutter/cogl/cogl/cogl-handle.h | 4 +- .../cogl/cogl-texture-2d-sliced-private.h | 191 ++ clutter/cogl/cogl/cogl-texture-2d-sliced.c | 1871 ++++++++++++++++ clutter/cogl/cogl/cogl-texture-driver.h | 12 + clutter/cogl/cogl/cogl-texture-private.h | 80 +- clutter/cogl/cogl/cogl-texture.c | 1954 ++++------------- .../cogl/cogl/driver/gl/cogl-texture-driver.c | 97 +- .../cogl/driver/gles/cogl-context-driver.c | 2 - .../cogl/driver/gles/cogl-context-driver.h | 2 - .../cogl/driver/gles/cogl-texture-driver.c | 217 +- 13 files changed, 2565 insertions(+), 1871 deletions(-) create mode 100644 clutter/cogl/cogl/cogl-texture-2d-sliced-private.h create mode 100644 clutter/cogl/cogl/cogl-texture-2d-sliced.c diff --git a/clutter/cogl/cogl/Makefile.am b/clutter/cogl/cogl/Makefile.am index e9be04ab3..fbfbd02af 100644 --- a/clutter/cogl/cogl/Makefile.am +++ b/clutter/cogl/cogl/Makefile.am @@ -114,8 +114,10 @@ libclutter_cogl_la_SOURCES = \ cogl-blend-string.h \ cogl-debug.c \ cogl-texture-private.h \ + cogl-texture-2d-sliced-private.h \ cogl-texture-driver.h \ cogl-texture.c \ + cogl-texture-2d-sliced.c \ $(NULL) EXTRA_DIST += stb_image.c cogl-enum-types.h.in cogl-enum-types.c.in diff --git a/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c index b42e3e508..6812e888d 100644 --- a/clutter/cogl/cogl/cogl-context.c +++ b/clutter/cogl/cogl/cogl-context.c @@ -100,6 +100,8 @@ cogl_create_context (void) _context->quad_indices_short = COGL_INVALID_HANDLE; _context->quad_indices_short_len = 0; + _context->texture_download_material = COGL_INVALID_HANDLE; + /* Initialise the clip stack */ _cogl_clip_stack_state_init (); diff --git a/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h index e51db6e9d..aecf5cc3c 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/cogl/cogl-context.h @@ -115,6 +115,8 @@ typedef struct guint viewport_width; guint viewport_height; + CoglHandle texture_download_material; + CoglContextDriver drv; } CoglContext; diff --git a/clutter/cogl/cogl/cogl-handle.h b/clutter/cogl/cogl/cogl-handle.h index 6caee0e71..8a40222dc 100644 --- a/clutter/cogl/cogl/cogl-handle.h +++ b/clutter/cogl/cogl/cogl-handle.h @@ -81,7 +81,7 @@ typedef struct _CoglHandleObject \ static CoglHandleClass _cogl_##type_name##_class; \ \ -static GQuark \ +GQuark \ _cogl_handle_##type_name##_get_type (void) \ { \ static GQuark type = 0; \ @@ -93,7 +93,7 @@ _cogl_handle_##type_name##_get_type (void) \ static CoglHandle \ _cogl_##type_name##_handle_new (Cogl##TypeName *new_obj) \ { \ - CoglHandleObject *obj = &new_obj->_parent; \ + CoglHandleObject *obj = (CoglHandleObject *)&new_obj->_parent;\ obj->ref_count = 1; \ \ obj->klass = &_cogl_##type_name##_class; \ diff --git a/clutter/cogl/cogl/cogl-texture-2d-sliced-private.h b/clutter/cogl/cogl/cogl-texture-2d-sliced-private.h new file mode 100644 index 000000000..0b1699f57 --- /dev/null +++ b/clutter/cogl/cogl/cogl-texture-2d-sliced-private.h @@ -0,0 +1,191 @@ +/* + * 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_TEXTURE_2D_SLICED_H +#define __COGL_TEXTURE_2D_SLICED_H + +#include "cogl-bitmap-private.h" +#include "cogl-handle.h" +#include "cogl-material-private.h" +#include "cogl-texture-private.h" + +#define COGL_TEXTURE_2D_SLICED(tex) ((CoglTexture2DSliced *)tex) + +typedef struct _CoglTexture2DSliced CoglTexture2DSliced; +typedef struct _CoglTexSliceSpan CoglTexSliceSpan; +typedef struct _CoglSpanIter CoglSpanIter; +typedef struct _CoglTexturePixel CoglTexturePixel; + +struct _CoglTexSliceSpan +{ + int start; + int size; + int waste; +}; + +struct _CoglSpanIter +{ + gint index; + GArray *array; + CoglTexSliceSpan *span; + float pos; + float next_pos; + float origin; + float cover_start; + float cover_end; + float intersect_start; + float intersect_end; + float intersect_start_local; + float intersect_end_local; + gboolean intersects; + gboolean flipped; +}; + +/* This is used to store the first pixel of each slice. This is only + used when glGenerateMipmap is not available */ +struct _CoglTexturePixel +{ + /* We need to store the format of the pixel because we store the + data in the source format which might end up being different for + each slice if a subregion is updated with a different format */ + GLenum gl_format; + GLenum gl_type; + guint8 data[4]; +}; + +struct _CoglTexture2DSliced +{ + CoglTexture _parent; + GArray *slice_x_spans; + GArray *slice_y_spans; + GArray *slice_gl_handles; + gint max_waste; + + /* This holds a copy of the first pixel in each slice. It is only + used to force an automatic update of the mipmaps when + glGenerateMipmap is not available. */ + CoglTexturePixel *first_pixels; +}; + +GQuark +_cogl_handle_texture_2d_sliced_get_type (void); + +CoglHandle +_cogl_texture_2d_sliced_new_with_size (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat internal_format); + +CoglHandle +_cogl_texture_2d_sliced_new_from_file (const gchar *filename, + CoglTextureFlags flags, + CoglPixelFormat internal_format, + GError **error); + +CoglHandle +_cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, + GLenum gl_target, + GLuint width, + GLuint height, + GLuint x_pot_waste, + GLuint y_pot_waste, + CoglPixelFormat format); + +CoglHandle +_cogl_texture_2d_sliced_new_from_data (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat format, + CoglPixelFormat internal_format, + unsigned int rowstride, + const guint8 *data); + + +CoglHandle +_cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, + CoglTextureFlags flags, + CoglPixelFormat internal_format); + +gboolean +_cogl_texture_2d_sliced_set_region (CoglHandle handle, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int dst_width, + unsigned int dst_height, + int width, + int height, + CoglPixelFormat format, + unsigned int rowstride, + const guint8 *data); + +int +_cogl_texture_2d_sliced_get_data (CoglHandle handle, + CoglPixelFormat format, + unsigned int rowstride, + guint8 *data); + +void +_cogl_texture_2d_sliced_foreach_sub_texture_in_region ( + CoglTexture2DSliced *tex_2ds, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglTextureSliceCallback callback, + void *user_data); + +gint +_cogl_texture_2d_sliced_get_max_waste (CoglHandle handle); + +gboolean +_cogl_texture_2d_sliced_is_sliced (CoglHandle handle); + +gboolean +_cogl_texture_2d_sliced_can_hardware_repeat (CoglHandle handle); + +void +_cogl_texture_2d_sliced_transform_coords_to_gl (CoglTexture2DSliced *tex_2ds, + float *s, + float *t); + +gboolean +_cogl_texture_2d_sliced_get_gl_texture (CoglHandle handle, + GLuint *out_gl_handle, + GLenum *out_gl_target); + +void +_cogl_texture_2d_sliced_set_filters (CoglHandle handle, + GLenum min_filter, + GLenum mag_filter); + +void +_cogl_texture_2d_sliced_ensure_mipmaps (CoglHandle handle); + +void +_cogl_texture_2d_sliced_set_wrap_mode_parameter (CoglTexture2DSliced *tex_2ds, + GLenum wrap_mode); + + +#endif /* __COGL_TEXTURE_2D_SLICED_H */ diff --git a/clutter/cogl/cogl/cogl-texture-2d-sliced.c b/clutter/cogl/cogl/cogl-texture-2d-sliced.c new file mode 100644 index 000000000..8f2057f60 --- /dev/null +++ b/clutter/cogl/cogl/cogl-texture-2d-sliced.c @@ -0,0 +1,1871 @@ +/* + * 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. + * + * Authors: + * Matthew Allum + * Neil Roberts + * Robert Bragg + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-bitmap.h" +#include "cogl-bitmap-private.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d-sliced-private.h" +#include "cogl-texture-driver.h" +#include "cogl-material.h" +#include "cogl-context.h" +#include "cogl-handle.h" +#include "cogl-primitives.h" + +#include +#include +#include + +static void _cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds); + +COGL_HANDLE_DEFINE (Texture2DSliced, texture_2d_sliced); + +typedef enum _CoglSpanIterAxis +{ + COGL_SPAN_ITER_AXIS_X, + COGL_SPAN_ITER_AXIS_Y +} CoglSpanIterAxis; + +static void +_cogl_span_iter_update (CoglSpanIter *iter) +{ + /* Pick current span */ + iter->span = &g_array_index (iter->array, + CoglTexSliceSpan, + iter->index); + + /* Offset next position by span size */ + iter->next_pos = iter->pos + + (float)(iter->span->size - iter->span->waste); + + /* Check if span intersects the area to cover */ + if (iter->next_pos <= iter->cover_start || + iter->pos >= iter->cover_end) + { + /* Intersection undefined */ + iter->intersects = FALSE; + return; + } + + iter->intersects = TRUE; + + /* Clip start position to coverage area */ + if (iter->pos < iter->cover_start) + iter->intersect_start = iter->cover_start; + else + iter->intersect_start = iter->pos; + + /* Clip end position to coverage area */ + if (iter->next_pos > iter->cover_end) + iter->intersect_end = iter->cover_end; + else + iter->intersect_end = iter->next_pos; +} + +void +_cogl_span_iter_begin (CoglSpanIter *iter, + GArray *spans, + float normalize_factor, + float cover_start, + float cover_end) +{ + float cover_start_normalized; + + iter->index = 0; + iter->span = NULL; + + iter->array = spans; + + /* We always iterate in a positive direction from the origin. If + * iter->flipped == TRUE that means whoever is using this API should + * interpreted the current span as extending in the opposite direction. I.e. + * it extends to the left if iterating the X axis, or up if the Y axis. */ + if (cover_start > cover_end) + { + float tmp = cover_start; + cover_start = cover_end; + cover_end = tmp; + iter->flipped = TRUE; + } + else + iter->flipped = FALSE; + + /* The texture spans cover the normalized texture coordinate space ranging + * from [0,1] but to help support repeating of sliced textures we allow + * iteration of any range so we need to relate the start of the range to the + * nearest point equivalent to 0. + */ + cover_start_normalized = cover_start / normalize_factor; + iter->origin = floorf (cover_start_normalized) * normalize_factor; + + iter->cover_start = cover_start; + iter->cover_end = cover_end; + iter->pos = iter->origin; + + /* Update intersection */ + _cogl_span_iter_update (iter); +} + +void +_cogl_span_iter_next (CoglSpanIter *iter) +{ + /* Move current position */ + iter->pos = iter->next_pos; + + /* Pick next slice (wrap when last reached) */ + iter->index = (iter->index + 1) % iter->array->len; + + /* Update intersection */ + _cogl_span_iter_update (iter); +} + +gboolean +_cogl_span_iter_end (CoglSpanIter *iter) +{ + /* End reached when whole area covered */ + return iter->pos >= iter->cover_end; +} + +/* To differentiate between texture coordinates of a specific, real, slice + * texture and the texture coordinates of the composite, sliced texture, the + * coordinates of the sliced texture are called "virtual" coordinates and the + * coordinates of slices are called "slice" coordinates. */ +/* This function lets you iterate all the slices that lie within the given + * virtual coordinates of the parent sliced texture. */ +/* Note: no guarantee is given about the order in which the slices will be + * visited */ +void +_cogl_texture_2d_sliced_foreach_sub_texture_in_region ( + CoglTexture2DSliced *tex_2ds, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglTextureSliceCallback callback, + void *user_data) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + float width = tex->bitmap.width; + float height = tex->bitmap.height; + CoglSpanIter iter_x; + CoglSpanIter iter_y; + + g_assert (tex->gl_target == GL_TEXTURE_2D); + + /* Slice spans are stored in denormalized coordinates, and this is what + * the _cogl_span_iter_* funcs expect to be given, so we scale the given + * virtual coordinates by the texture size to denormalize. + */ + /* XXX: I wonder if it's worth changing how we store spans so we can avoid + * the need to denormalize here */ + virtual_tx_1 *= width; + virtual_ty_1 *= height; + virtual_tx_2 *= width; + virtual_ty_2 *= height; + + /* Iterate the y axis of the virtual rectangle */ + for (_cogl_span_iter_begin (&iter_y, + tex_2ds->slice_y_spans, + height, + virtual_ty_1, + virtual_ty_2); + !_cogl_span_iter_end (&iter_y); + _cogl_span_iter_next (&iter_y)) + { + float y_intersect_start = iter_y.intersect_start; + float y_intersect_end = iter_y.intersect_end; + float slice_ty1; + float slice_ty2; + + /* Discard slices out of rectangle early */ + if (!iter_y.intersects) + continue; + + if (iter_y.flipped) + { + y_intersect_start = iter_y.intersect_end; + y_intersect_end = iter_y.intersect_start; + } + + /* Localize slice texture coordinates */ + slice_ty1 = y_intersect_start - iter_y.pos; + slice_ty2 = y_intersect_end - iter_y.pos; + + /* Normalize slice texture coordinates */ + slice_ty1 /= iter_y.span->size; + slice_ty2 /= iter_y.span->size; + + /* Iterate the x axis of the virtual rectangle */ + for (_cogl_span_iter_begin (&iter_x, + tex_2ds->slice_x_spans, + width, + virtual_tx_1, + virtual_tx_2); + !_cogl_span_iter_end (&iter_x); + _cogl_span_iter_next (&iter_x)) + { + float slice_coords[4]; + float virtual_coords[4]; + float x_intersect_start = iter_x.intersect_start; + float x_intersect_end = iter_x.intersect_end; + float slice_tx1; + float slice_tx2; + GLuint gl_handle; + + /* Discard slices out of rectangle early */ + if (!iter_x.intersects) + continue; + + if (iter_x.flipped) + { + x_intersect_start = iter_x.intersect_end; + x_intersect_end = iter_x.intersect_start; + } + + /* Localize slice texture coordinates */ + slice_tx1 = x_intersect_start - iter_x.pos; + slice_tx2 = x_intersect_end - iter_x.pos; + + /* Normalize slice texture coordinates */ + slice_tx1 /= iter_x.span->size; + slice_tx2 /= iter_x.span->size; + + /* Pluck out opengl texture object for this slice */ + gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, + iter_y.index * iter_x.array->len + + iter_x.index); + + slice_coords[0] = slice_tx1; + slice_coords[1] = slice_ty1; + slice_coords[2] = slice_tx2; + slice_coords[3] = slice_ty2; + + virtual_coords[0] = x_intersect_start / width; + virtual_coords[1] = y_intersect_start / height; + virtual_coords[2] = x_intersect_end / width; + virtual_coords[3] = y_intersect_end / height; + + callback (tex, + gl_handle, + tex->gl_target, + slice_coords, + virtual_coords, + user_data); + } + } +} + +static guchar * +_cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds) +{ + CoglTexSliceSpan *last_x_span; + CoglTexSliceSpan *last_y_span; + guchar *waste_buf = NULL; + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + + /* If the texture has any waste then allocate a buffer big enough to + fill the gaps */ + last_x_span = &g_array_index (tex_2ds->slice_x_spans, CoglTexSliceSpan, + tex_2ds->slice_x_spans->len - 1); + last_y_span = &g_array_index (tex_2ds->slice_y_spans, CoglTexSliceSpan, + tex_2ds->slice_y_spans->len - 1); + if (last_x_span->waste > 0 || last_y_span->waste > 0) + { + gint bpp = _cogl_get_format_bpp (tex->bitmap.format); + CoglTexSliceSpan *first_x_span + = &g_array_index (tex_2ds->slice_x_spans, CoglTexSliceSpan, 0); + CoglTexSliceSpan *first_y_span + = &g_array_index (tex_2ds->slice_y_spans, CoglTexSliceSpan, 0); + guint right_size = first_y_span->size * last_x_span->waste; + guint bottom_size = first_x_span->size * last_y_span->waste; + + waste_buf = g_malloc (MAX (right_size, bottom_size) * bpp); + } + + return waste_buf; +} + +static gboolean +_cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) +{ + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint gl_handle; + gint bpp; + gint x,y; + guchar *waste_buf; + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + + bpp = _cogl_get_format_bpp (tex->bitmap.format); + + waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds); + + /* Iterate vertical slices */ + for (y = 0; y < tex_2ds->slice_y_spans->len; ++y) + { + y_span = &g_array_index (tex_2ds->slice_y_spans, CoglTexSliceSpan, y); + + /* Iterate horizontal slices */ + for (x = 0; x < tex_2ds->slice_x_spans->len; ++x) + { + gint slice_num = y * tex_2ds->slice_x_spans->len + x; + + x_span = &g_array_index (tex_2ds->slice_x_spans, CoglTexSliceSpan, x); + + /* Pick the gl texture object handle */ + gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, slice_num); + + _cogl_texture_driver_upload_subregion_to_gl ( + tex, + x_span->start, /* src x */ + y_span->start, /* src y */ + 0, /* dst x */ + 0, /* dst y */ + x_span->size - x_span->waste, /* width */ + y_span->size - y_span->waste, /* height */ + &tex->bitmap, + tex->gl_format, + tex->gl_type, + gl_handle); + + /* Keep a copy of the first pixel if needed */ + if (tex_2ds->first_pixels) + { + memcpy (tex_2ds->first_pixels[slice_num].data, + tex->bitmap.data + x_span->start * bpp + + y_span->start * tex->bitmap.rowstride, + bpp); + tex_2ds->first_pixels[slice_num].gl_format = tex->gl_format; + tex_2ds->first_pixels[slice_num].gl_type = tex->gl_type; + } + + /* Fill the waste with a copies of the rightmost pixels */ + if (x_span->waste > 0) + { + const guchar *src = tex->bitmap.data + + y_span->start * tex->bitmap.rowstride + + (x_span->start + x_span->size - x_span->waste - 1) * bpp; + guchar *dst = waste_buf; + guint wx, wy; + + for (wy = 0; wy < y_span->size - y_span->waste; wy++) + { + for (wx = 0; wx < x_span->waste; wx++) + { + memcpy (dst, src, bpp); + dst += bpp; + } + src += tex->bitmap.rowstride; + } + + _cogl_texture_driver_prep_gl_for_pixels_upload ( + x_span->waste * bpp, + bpp); + + GE( glTexSubImage2D (tex->gl_target, 0, + x_span->size - x_span->waste, + 0, + x_span->waste, + y_span->size - y_span->waste, + tex->gl_format, tex->gl_type, + waste_buf) ); + } + + if (y_span->waste > 0) + { + const guchar *src = tex->bitmap.data + + ((y_span->start + y_span->size - y_span->waste - 1) + * tex->bitmap.rowstride) + + x_span->start * bpp; + guchar *dst = waste_buf; + guint wy, wx; + + for (wy = 0; wy < y_span->waste; wy++) + { + memcpy (dst, src, (x_span->size - x_span->waste) * bpp); + dst += (x_span->size - x_span->waste) * bpp; + + for (wx = 0; wx < x_span->waste; wx++) + { + memcpy (dst, dst - bpp, bpp); + dst += bpp; + } + } + + _cogl_texture_driver_prep_gl_for_pixels_upload ( + x_span->size * bpp, + bpp); + + GE( glTexSubImage2D (tex->gl_target, 0, + 0, + y_span->size - y_span->waste, + x_span->size, + y_span->waste, + tex->gl_format, tex->gl_type, + waste_buf) ); + } + } + } + + if (waste_buf) + g_free (waste_buf); + + tex->mipmaps_dirty = TRUE; + + return TRUE; +} + +static gboolean +_cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + gint width, + gint height, + CoglBitmap *source_bmp, + GLuint source_gl_format, + GLuint source_gl_type) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + gint bpp; + CoglSpanIter x_iter; + CoglSpanIter y_iter; + GLuint gl_handle; + gint source_x = 0, source_y = 0; + gint inter_w = 0, inter_h = 0; + gint local_x = 0, local_y = 0; + guchar *waste_buf; + + bpp = _cogl_get_format_bpp (source_bmp->format); + + waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds); + + /* Iterate vertical spans */ + for (source_y = src_y, + _cogl_span_iter_begin (&y_iter, + tex_2ds->slice_y_spans, + tex->bitmap.height, + dst_y, + dst_y + height); + + !_cogl_span_iter_end (&y_iter); + + _cogl_span_iter_next (&y_iter), + source_y += inter_h ) + { + /* Discard slices out of the subregion early */ + if (!y_iter.intersects) + { + inter_h = 0; + continue; + } + + y_span = &g_array_index (tex_2ds->slice_y_spans, CoglTexSliceSpan, + y_iter.index); + + /* Iterate horizontal spans */ + for (source_x = src_x, + _cogl_span_iter_begin (&x_iter, + tex_2ds->slice_x_spans, + tex->bitmap.width, + dst_x, + dst_x + width); + + !_cogl_span_iter_end (&x_iter); + + _cogl_span_iter_next (&x_iter), + source_x += inter_w ) + { + gint slice_num; + + /* Discard slices out of the subregion early */ + if (!x_iter.intersects) + { + inter_w = 0; + continue; + } + + x_span = &g_array_index (tex_2ds->slice_x_spans, CoglTexSliceSpan, + x_iter.index); + + /* Pick intersection width and height */ + inter_w = (x_iter.intersect_end - x_iter.intersect_start); + inter_h = (y_iter.intersect_end - y_iter.intersect_start); + + /* Localize intersection top-left corner to slice*/ + local_x = (x_iter.intersect_start - x_iter.pos); + local_y = (y_iter.intersect_start - y_iter.pos); + + slice_num = y_iter.index * tex_2ds->slice_x_spans->len + x_iter.index; + + /* Pick slice GL handle */ + gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, slice_num); + + _cogl_texture_driver_upload_subregion_to_gl (tex, + source_x, + source_y, + local_x, /* dst x */ + local_y, /* dst x */ + inter_w, /* width */ + inter_h, /* height */ + source_bmp, + source_gl_format, + source_gl_type, + gl_handle); + + /* Keep a copy of the first pixel if needed */ + if (tex_2ds->first_pixels && local_x == 0 && local_y == 0) + { + memcpy (tex_2ds->first_pixels[slice_num].data, + source_bmp->data + source_x * bpp + + source_y * source_bmp->rowstride, + bpp); + tex_2ds->first_pixels[slice_num].gl_format = source_gl_format; + tex_2ds->first_pixels[slice_num].gl_type = source_gl_type; + } + + /* If the x_span is sliced and the upload touches the + rightmost pixels then fill the waste with copies of the + pixels */ + if (x_span->waste > 0 + && local_x < x_span->size - x_span->waste + && local_x + inter_w >= x_span->size - x_span->waste) + { + const guchar *src; + guchar *dst; + guint wx, wy; + + src = source_bmp->data + + (src_y + ((int)y_iter.intersect_start) + - dst_y) + * source_bmp->rowstride + + (src_x + x_span->start + x_span->size - x_span->waste + - dst_x - 1) + * bpp; + + dst = waste_buf; + + for (wy = 0; wy < inter_h; wy++) + { + for (wx = 0; wx < x_span->waste; wx++) + { + memcpy (dst, src, bpp); + dst += bpp; + } + src += source_bmp->rowstride; + } + + _cogl_texture_driver_prep_gl_for_pixels_upload ( + x_span->waste * bpp, + bpp); + + GE( glTexSubImage2D (tex->gl_target, 0, + x_span->size - x_span->waste, + local_y, + x_span->waste, + inter_h, + source_gl_format, + source_gl_type, + waste_buf) ); + } + + /* same for the bottom-most pixels */ + if (y_span->waste > 0 + && local_y < y_span->size - y_span->waste + && local_y + inter_h >= y_span->size - y_span->waste) + { + const guchar *src; + guchar *dst; + guint wy, wx; + guint copy_width; + + src = source_bmp->data + + (src_x + ((int)x_iter.intersect_start) + - dst_x) + * bpp + + (src_y + y_span->start + y_span->size - y_span->waste + - dst_y - 1) + * source_bmp->rowstride; + + dst = waste_buf; + + if (local_x + inter_w >= x_span->size - x_span->waste) + copy_width = x_span->size - local_x; + else + copy_width = inter_w; + + for (wy = 0; wy < y_span->waste; wy++) + { + memcpy (dst, src, inter_w * bpp); + dst += inter_w * bpp; + + for (wx = inter_w; wx < copy_width; wx++) + { + memcpy (dst, dst - bpp, bpp); + dst += bpp; + } + } + + _cogl_texture_driver_prep_gl_for_pixels_upload ( + copy_width * bpp, + bpp); + + GE( glTexSubImage2D (tex->gl_target, 0, + local_x, + y_span->size - y_span->waste, + copy_width, + y_span->waste, + source_gl_format, + source_gl_type, + waste_buf) ); + } + } + } + + if (waste_buf) + g_free (waste_buf); + + tex->mipmaps_dirty = TRUE; + + return TRUE; +} + +static gint +_cogl_rect_slices_for_size (int size_to_fill, + int max_span_size, + int max_waste, + GArray *out_spans) +{ + int n_spans = 0; + CoglTexSliceSpan span; + + /* Init first slice span */ + span.start = 0; + span.size = max_span_size; + span.waste = 0; + + /* Repeat until whole area covered */ + while (size_to_fill >= span.size) + { + /* Add another slice span of same size */ + if (out_spans) + g_array_append_val (out_spans, span); + span.start += span.size; + size_to_fill -= span.size; + n_spans++; + } + + /* Add one last smaller slice span */ + if (size_to_fill > 0) + { + span.size = size_to_fill; + if (out_spans) + g_array_append_val (out_spans, span); + n_spans++; + } + + return n_spans; +} + +static gint +_cogl_pot_slices_for_size (gint size_to_fill, + gint max_span_size, + gint max_waste, + GArray *out_spans) +{ + gint n_spans = 0; + CoglTexSliceSpan span; + + /* Init first slice span */ + span.start = 0; + span.size = max_span_size; + span.waste = 0; + + /* Fix invalid max_waste */ + if (max_waste < 0) + max_waste = 0; + + while (TRUE) + { + /* Is the whole area covered? */ + if (size_to_fill > span.size) + { + /* Not yet - add a span of this size */ + if (out_spans) + g_array_append_val (out_spans, span); + + span.start += span.size; + size_to_fill -= span.size; + n_spans++; + } + else if (span.size - size_to_fill <= max_waste) + { + /* Yes and waste is small enough */ + span.waste = span.size - size_to_fill; + if (out_spans) + g_array_append_val (out_spans, span); + + return ++n_spans; + } + else + { + /* Yes but waste is too large */ + while (span.size - size_to_fill > max_waste) + { + span.size /= 2; + g_assert (span.size > 0); + } + } + } + + /* Can't get here */ + return 0; +} + +void +_cogl_texture_2d_sliced_set_wrap_mode_parameter (CoglTexture2DSliced *tex_2ds, + GLenum wrap_mode) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + + /* Only set the wrap mode if it's different from the current + value to avoid too many GL calls */ + if (tex->wrap_mode != wrap_mode) + { + int i; + + /* Any queued texture rectangles may be depending on the previous + * wrap mode... */ + _cogl_journal_flush (); + + for (i = 0; i < tex_2ds->slice_gl_handles->len; i++) + { + GLuint texnum = g_array_index (tex_2ds->slice_gl_handles, GLuint, i); + + GE( glBindTexture (tex->gl_target, texnum) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, wrap_mode) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, wrap_mode) ); + } + + tex->wrap_mode = wrap_mode; + } +} + +static gboolean +_cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + gint bpp; + gint max_width; + gint max_height; + GLuint *gl_handles; + gint n_x_slices; + gint n_y_slices; + gint n_slices; + gint x, y; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + const GLfloat transparent_color[4] = { 0x00, 0x00, 0x00, 0x00 }; + + gint (*slices_for_size) (gint, gint, gint, GArray*); + + bpp = _cogl_get_format_bpp (tex->bitmap.format); + + /* Initialize size of largest slice according to supported features */ + if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT)) + { + max_width = tex->bitmap.width; + max_height = tex->bitmap.height; + tex->gl_target = GL_TEXTURE_2D; + slices_for_size = _cogl_rect_slices_for_size; + } + else + { + max_width = cogl_util_next_p2 (tex->bitmap.width); + max_height = cogl_util_next_p2 (tex->bitmap.height); + tex->gl_target = GL_TEXTURE_2D; + slices_for_size = _cogl_pot_slices_for_size; + } + + /* Negative number means no slicing forced by the user */ + if (tex_2ds->max_waste <= -1) + { + CoglTexSliceSpan span; + + /* Check if size supported else bail out */ + if (!_cogl_texture_driver_size_supported (tex->gl_target, + tex->gl_format, + tex->gl_type, + max_width, + max_height)) + { + return FALSE; + } + + n_x_slices = 1; + n_y_slices = 1; + + /* Init span arrays */ + tex_2ds->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + 1); + + tex_2ds->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + 1); + + /* Add a single span for width and height */ + span.start = 0; + span.size = max_width; + span.waste = max_width - tex->bitmap.width; + g_array_append_val (tex_2ds->slice_x_spans, span); + + span.size = max_height; + span.waste = max_height - tex->bitmap.height; + g_array_append_val (tex_2ds->slice_y_spans, span); + } + else + { + /* Decrease the size of largest slice until supported by GL */ + while (!_cogl_texture_driver_size_supported (tex->gl_target, + tex->gl_format, + tex->gl_type, + max_width, + max_height)) + { + /* Alternate between width and height */ + if (max_width > max_height) + max_width /= 2; + else + max_height /= 2; + + if (max_width == 0 || max_height == 0) + return FALSE; + } + + /* Determine the slices required to cover the bitmap area */ + n_x_slices = slices_for_size (tex->bitmap.width, + max_width, tex_2ds->max_waste, + NULL); + + n_y_slices = slices_for_size (tex->bitmap.height, + max_height, tex_2ds->max_waste, + NULL); + + /* Init span arrays with reserved size */ + tex_2ds->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + n_x_slices); + + tex_2ds->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + n_y_slices); + + /* Fill span arrays with info */ + slices_for_size (tex->bitmap.width, + max_width, tex_2ds->max_waste, + tex_2ds->slice_x_spans); + + slices_for_size (tex->bitmap.height, + max_height, tex_2ds->max_waste, + tex_2ds->slice_y_spans); + } + + /* Init and resize GL handle array */ + n_slices = n_x_slices * n_y_slices; + + tex_2ds->slice_gl_handles = g_array_sized_new (FALSE, FALSE, + sizeof (GLuint), + n_slices); + + g_array_set_size (tex_2ds->slice_gl_handles, n_slices); + + /* Allocate some space to store a copy of the first pixel of each + slice. This is only needed if glGenerateMipmap (which is part of + the FBO extension) is not available */ + if (cogl_features_available (COGL_FEATURE_OFFSCREEN)) + tex_2ds->first_pixels = NULL; + else + tex_2ds->first_pixels = g_new (CoglTexturePixel, n_slices); + + /* Wrap mode not yet set */ + tex->wrap_mode = GL_FALSE; + + /* Generate a "working set" of GL texture objects + * (some implementations might supported faster + * re-binding between textures inside a set) */ + gl_handles = (GLuint*) tex_2ds->slice_gl_handles->data; + + GE( glGenTextures (n_slices, gl_handles) ); + + + /* Init each GL texture object */ + for (y = 0; y < n_y_slices; ++y) + { + y_span = &g_array_index (tex_2ds->slice_y_spans, CoglTexSliceSpan, y); + + for (x = 0; x < n_x_slices; ++x) + { + x_span = &g_array_index (tex_2ds->slice_x_spans, CoglTexSliceSpan, x); + + COGL_NOTE (TEXTURE, "CREATE SLICE (%d,%d)\tsize (%d,%d)", + x, y, + x_span->size - x_span->waste, + y_span->size - y_span->waste); + + /* Setup texture parameters */ + GE( _cogl_texture_driver_bind (tex->gl_target, + gl_handles[y * n_x_slices + x], + tex->gl_intformat) ); + + _cogl_texture_driver_try_setting_gl_border_color (tex->gl_target, + transparent_color); + + /* Pass NULL data to init size and internal format */ + GE( glTexImage2D (tex->gl_target, 0, tex->gl_intformat, + x_span->size, y_span->size, 0, + tex->gl_format, tex->gl_type, 0) ); + } + } + + return TRUE; +} + +static void +_cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *tex_2ds) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + + if (tex_2ds->slice_x_spans != NULL) + g_array_free (tex_2ds->slice_x_spans, TRUE); + + if (tex_2ds->slice_y_spans != NULL) + g_array_free (tex_2ds->slice_y_spans, TRUE); + + if (tex_2ds->slice_gl_handles != NULL) + { + if (tex->is_foreign == FALSE) + { + GE( glDeleteTextures (tex_2ds->slice_gl_handles->len, + (GLuint*) tex_2ds->slice_gl_handles->data) ); + } + + g_array_free (tex_2ds->slice_gl_handles, TRUE); + } + + if (tex_2ds->first_pixels != NULL) + g_free (tex_2ds->first_pixels); +} + +static void +_cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds) +{ + /* Chain up to parent */ + _cogl_texture_free (COGL_TEXTURE (tex_2ds)); + + _cogl_texture_2d_sliced_slices_free (tex_2ds); + g_free (tex_2ds); +} + +CoglHandle +_cogl_texture_2d_sliced_new_with_size (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + gint bpp; + gint rowstride; + + /* Since no data, we need some internal format */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + /* Rowstride from width */ + bpp = _cogl_get_format_bpp (internal_format); + rowstride = width * bpp; + + /* Init texture with empty bitmap */ + tex_2ds = g_new (CoglTexture2DSliced, 1); + + tex = COGL_TEXTURE (tex_2ds); + tex->type = COGL_TEXTURE_TYPE_2D_SLICED; + + tex->is_foreign = FALSE; + tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; + tex->mipmaps_dirty = TRUE; + tex_2ds->first_pixels = NULL; + + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.format = internal_format; + tex->bitmap.rowstride = rowstride; + tex->bitmap.data = NULL; + tex->bitmap_owner = FALSE; + + tex_2ds->slice_x_spans = NULL; + tex_2ds->slice_y_spans = NULL; + tex_2ds->slice_gl_handles = NULL; + + if (flags & COGL_TEXTURE_NO_SLICING) + tex_2ds->max_waste = -1; + else + tex_2ds->max_waste = COGL_TEXTURE_MAX_WASTE; + + /* Unknown filter */ + tex->min_filter = GL_FALSE; + tex->mag_filter = GL_FALSE; + + /* Find closest GL format match */ + tex->bitmap.format = + _cogl_pixel_format_to_gl (internal_format, + &tex->gl_intformat, + &tex->gl_format, + &tex->gl_type); + + /* Create slices for the given format and size */ + if (!_cogl_texture_2d_sliced_slices_create (tex_2ds)) + { + _cogl_texture_2d_sliced_free (tex_2ds); + return COGL_INVALID_HANDLE; + } + + return _cogl_texture_2d_sliced_handle_new (tex_2ds); +} + +CoglHandle +_cogl_texture_2d_sliced_new_from_data (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat format, + CoglPixelFormat internal_format, + unsigned int rowstride, + const guint8 *data) +{ + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + int bpp; + + if (format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + if (data == NULL) + return COGL_INVALID_HANDLE; + + /* Rowstride from width if not given */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = width * bpp; + + /* Create new texture and fill with given data */ + tex_2ds = g_new0 (CoglTexture2DSliced, 1); + + tex = COGL_TEXTURE (tex_2ds); + tex->type = COGL_TEXTURE_TYPE_2D_SLICED; + + tex->is_foreign = FALSE; + tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; + tex->mipmaps_dirty = TRUE; + tex_2ds->first_pixels = NULL; + + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.data = (guchar*)data; + tex->bitmap.format = format; + tex->bitmap.rowstride = rowstride; + tex->bitmap_owner = FALSE; + + tex_2ds->slice_x_spans = NULL; + tex_2ds->slice_y_spans = NULL; + tex_2ds->slice_gl_handles = NULL; + + if (flags & COGL_TEXTURE_NO_SLICING) + tex_2ds->max_waste = -1; + else + tex_2ds->max_waste = COGL_TEXTURE_MAX_WASTE; + + /* Unknown filter */ + tex->min_filter = GL_FALSE; + tex->mag_filter = GL_FALSE; + + /* FIXME: If upload fails we should set some kind of + * error flag but still return texture handle (this + * is to keep the behavior equal to _new_from_file; + * see below) */ + + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + { + _cogl_texture_2d_sliced_free (tex_2ds); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_2d_sliced_slices_create (tex_2ds)) + { + _cogl_texture_2d_sliced_free (tex_2ds); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_2d_sliced_upload_to_gl (tex_2ds)) + { + _cogl_texture_2d_sliced_free (tex_2ds); + return COGL_INVALID_HANDLE; + } + + _cogl_texture_bitmap_free (COGL_TEXTURE (tex_2ds)); + + return _cogl_texture_2d_sliced_handle_new (tex_2ds); +} + +CoglHandle +_cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + CoglBitmap *bmp = (CoglBitmap *)bmp_handle; + + g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); + + /* Create new texture and fill with loaded data */ + tex_2ds = g_new0 (CoglTexture2DSliced, 1); + + tex = COGL_TEXTURE (tex_2ds); + tex->type = COGL_TEXTURE_TYPE_2D_SLICED; + + tex->is_foreign = FALSE; + tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; + tex->mipmaps_dirty = TRUE; + tex_2ds->first_pixels = NULL; + + tex->bitmap = *bmp; + tex->bitmap_owner = FALSE; + + tex_2ds->slice_x_spans = NULL; + tex_2ds->slice_y_spans = NULL; + tex_2ds->slice_gl_handles = NULL; + + if (flags & COGL_TEXTURE_NO_SLICING) + tex_2ds->max_waste = -1; + else + tex_2ds->max_waste = COGL_TEXTURE_MAX_WASTE; + + /* Unknown filter */ + tex->min_filter = GL_FALSE; + tex->mag_filter = GL_FALSE; + + /* FIXME: If upload fails we should set some kind of + * error flag but still return texture handle if the + * user decides to destroy another texture and upload + * this one instead (reloading from file is not needed + * in that case). As a rule then, everytime a valid + * CoglHandle is returned, it should also be destroyed + * with cogl_handle_unref at some point! */ + + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + { + _cogl_texture_2d_sliced_free (tex_2ds); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_2d_sliced_slices_create (tex_2ds)) + { + _cogl_texture_2d_sliced_free (tex_2ds); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_2d_sliced_upload_to_gl (tex_2ds)) + { + _cogl_texture_2d_sliced_free (tex_2ds); + return COGL_INVALID_HANDLE; + } + + _cogl_texture_bitmap_free (tex); + + return _cogl_texture_2d_sliced_handle_new (tex_2ds); +} + +CoglHandle +_cogl_texture_2d_sliced_new_from_file ( + const char *filename, + CoglTextureFlags flags, + CoglPixelFormat internal_format, + GError **error) +{ + CoglHandle bmp; + CoglHandle handle; + + g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE); + + bmp = cogl_bitmap_new_from_file (filename, error); + if (bmp == COGL_INVALID_HANDLE) + return COGL_INVALID_HANDLE; + + handle = + _cogl_texture_2d_sliced_new_from_bitmap (bmp, flags, internal_format); + cogl_handle_unref (bmp); + + return handle; +} + +CoglHandle +_cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, + GLenum gl_target, + GLuint width, + GLuint height, + GLuint x_pot_waste, + GLuint y_pot_waste, + CoglPixelFormat format) +{ + /* NOTE: width, height and internal format are not queriable + in GLES, hence such a function prototype. However, for + OpenGL they are still queried from the texture for improved + robustness and for completeness in case one day GLES gains + support for them. + */ + + GLenum gl_error = 0; + GLboolean gl_istexture; + GLint gl_compressed = GL_FALSE; + GLint gl_int_format = 0; + GLint gl_width = 0; + GLint gl_height = 0; + GLint gl_gen_mipmap; + guint bpp; + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + CoglTexSliceSpan x_span; + CoglTexSliceSpan y_span; + + if (!_cogl_texture_driver_allows_foreign_gl_target (gl_target)) + return COGL_INVALID_HANDLE; + +#if HAVE_COGL_GL + /* It shouldn't be necissary to have waste in this case since + * the texture isn't limited to power of two sizes. */ + if (gl_target == GL_TEXTURE_RECTANGLE_ARB && + (x_pot_waste != 0 || y_pot_waste != 0)) + { + g_warning ("You can't create a foreign GL_TEXTURE_RECTANGLE cogl " + "texture with waste\n"); + return COGL_INVALID_HANDLE; + } +#endif + + /* Make sure it is a valid GL texture object */ + gl_istexture = glIsTexture (gl_handle); + if (gl_istexture == GL_FALSE) + return COGL_INVALID_HANDLE; + + /* Make sure binding succeeds */ + gl_error = glGetError (); + glBindTexture (gl_target, gl_handle); + if (glGetError () != GL_NO_ERROR) + return COGL_INVALID_HANDLE; + + /* Obtain texture parameters + (only level 0 we are interested in) */ + +#if HAVE_COGL_GL + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_COMPRESSED, + &gl_compressed) ); + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_INTERNAL_FORMAT, + &gl_int_format) ); + + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_WIDTH, + &gl_width) ); + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_HEIGHT, + &gl_height) ); +#else + gl_width = width + x_pot_waste; + gl_height = height + y_pot_waste; +#endif + + GE( glGetTexParameteriv (gl_target, + GL_GENERATE_MIPMAP, + &gl_gen_mipmap) ); + + /* Validate width and height */ + if (gl_width <= 0 || gl_height <= 0) + return COGL_INVALID_HANDLE; + + /* Validate pot waste */ + if (x_pot_waste < 0 || x_pot_waste >= gl_width || + y_pot_waste < 0 || y_pot_waste >= gl_height) + return COGL_INVALID_HANDLE; + + /* Compressed texture images not supported */ + if (gl_compressed == GL_TRUE) + return COGL_INVALID_HANDLE; + + /* Try and match to a cogl format */ + if (!_cogl_pixel_format_from_gl_internal (gl_int_format, &format)) + return COGL_INVALID_HANDLE; + + /* Create new texture */ + tex_2ds = g_new0 (CoglTexture2DSliced, 1); + + tex = COGL_TEXTURE (tex_2ds); + tex->type = COGL_TEXTURE_TYPE_2D_SLICED; + + /* Setup bitmap info */ + tex->is_foreign = TRUE; + tex->auto_mipmap = (gl_gen_mipmap == GL_TRUE) ? TRUE : FALSE; + tex->mipmaps_dirty = TRUE; + tex_2ds->first_pixels = NULL; + + bpp = _cogl_get_format_bpp (format); + tex->bitmap.format = format; + tex->bitmap.width = gl_width - x_pot_waste; + tex->bitmap.height = gl_height - y_pot_waste; + tex->bitmap.rowstride = tex->bitmap.width * bpp; + tex->bitmap_owner = FALSE; + + tex->gl_target = gl_target; + tex->gl_intformat = gl_int_format; + tex->gl_format = gl_int_format; + tex->gl_type = GL_UNSIGNED_BYTE; + + /* Unknown filter */ + tex->min_filter = GL_FALSE; + tex->mag_filter = GL_FALSE; + tex_2ds->max_waste = 0; + + /* Wrap mode not yet set */ + tex->wrap_mode = GL_FALSE; + + /* Create slice arrays */ + tex_2ds->slice_x_spans = + g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), 1); + + tex_2ds->slice_y_spans = + g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), 1); + + tex_2ds->slice_gl_handles = + g_array_sized_new (FALSE, FALSE, + sizeof (GLuint), 1); + + /* Store info for a single slice */ + x_span.start = 0; + x_span.size = gl_width; + x_span.waste = x_pot_waste; + g_array_append_val (tex_2ds->slice_x_spans, x_span); + + y_span.start = 0; + y_span.size = gl_height; + y_span.waste = y_pot_waste; + g_array_append_val (tex_2ds->slice_y_spans, y_span); + + g_array_append_val (tex_2ds->slice_gl_handles, gl_handle); + + tex_2ds->first_pixels = NULL; + + return _cogl_texture_2d_sliced_handle_new (tex_2ds); +} + +gint +_cogl_texture_2d_sliced_get_max_waste (CoglHandle handle) +{ + CoglTexture2DSliced *tex_2ds; + + tex_2ds = _cogl_texture_2d_sliced_pointer_from_handle (handle); + + return tex_2ds->max_waste; +} + +gboolean +_cogl_texture_2d_sliced_is_sliced (CoglHandle handle) +{ + CoglTexture2DSliced *tex_2ds; + + tex_2ds = _cogl_texture_2d_sliced_pointer_from_handle (handle); + + if (tex_2ds->slice_gl_handles == NULL) + return FALSE; + + if (tex_2ds->slice_gl_handles->len <= 1) + return FALSE; + + return TRUE; +} + +gboolean +_cogl_texture_2d_sliced_can_hardware_repeat (CoglHandle handle) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (handle); + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + + x_span = &g_array_index (tex_2ds->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex_2ds->slice_y_spans, CoglTexSliceSpan, 0); + + return (x_span->waste || y_span->waste) ? FALSE : TRUE; +} + +void +_cogl_texture_2d_sliced_transform_coords_to_gl (CoglTexture2DSliced *tex_2ds, + float *s, + float *t) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + + g_assert (!_cogl_texture_2d_sliced_is_sliced (tex_2ds)); + + /* Don't include the waste in the texture coordinates */ + x_span = &g_array_index (tex_2ds->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex_2ds->slice_y_spans, CoglTexSliceSpan, 0); + + *s *= tex->bitmap.width / (float)x_span->size; + *t *= tex->bitmap.height / (float)y_span->size; + +#if HAVE_COGL_GL + /* Denormalize texture coordinates for rectangle textures */ + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + *s *= x_span->size; + *t *= y_span->size; + } +#endif +} + +gboolean +_cogl_texture_2d_sliced_get_gl_texture (CoglHandle handle, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return FALSE; + + tex_2ds = _cogl_texture_2d_sliced_pointer_from_handle (handle); + tex = COGL_TEXTURE (tex_2ds); + + if (tex_2ds->slice_gl_handles == NULL) + return FALSE; + + if (tex_2ds->slice_gl_handles->len < 1) + return FALSE; + + if (out_gl_handle != NULL) + *out_gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, 0); + + if (out_gl_target != NULL) + *out_gl_target = tex->gl_target; + + return TRUE; +} + +void +_cogl_texture_2d_sliced_set_filters (CoglHandle handle, + GLenum min_filter, + GLenum mag_filter) +{ + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + GLuint gl_handle; + int i; + + if (!cogl_is_texture (handle)) + return; + + tex_2ds = _cogl_texture_2d_sliced_pointer_from_handle (handle); + tex = COGL_TEXTURE (tex_2ds); + + /* Make sure slices were created */ + if (tex_2ds->slice_gl_handles == NULL) + return; + + if (min_filter == tex->min_filter + && mag_filter == tex->mag_filter) + return; + + /* Store new values */ + tex->min_filter = min_filter; + tex->mag_filter = mag_filter; + + /* Apply new filters to every slice */ + for (i=0; islice_gl_handles->len; ++i) + { + gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, i); + GE( glBindTexture (tex->gl_target, gl_handle) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, + tex->mag_filter) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, + tex->min_filter) ); + } +} + +void +_cogl_texture_2d_sliced_ensure_mipmaps (CoglHandle handle) +{ + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + int i; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!cogl_is_texture (handle)) + return; + + tex_2ds = _cogl_texture_2d_sliced_pointer_from_handle (handle); + tex = COGL_TEXTURE (tex_2ds); + + /* Only update if the mipmaps are dirty */ + if (!tex->auto_mipmap || !tex->mipmaps_dirty) + return; + + /* Make sure slices were created */ + if (tex_2ds->slice_gl_handles == NULL) + return; + + /* Regenerate the mipmaps on every slice */ + for (i = 0; i < tex_2ds->slice_gl_handles->len; i++) + { + GLuint gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, i); + GE( glBindTexture (tex->gl_target, gl_handle) ); + + /* glGenerateMipmap is defined in the FBO extension */ + if (cogl_features_available (COGL_FEATURE_OFFSCREEN)) + _cogl_texture_driver_gl_generate_mipmaps (tex->gl_target); + else if (tex_2ds->first_pixels) + { + CoglTexturePixel *pixel = tex_2ds->first_pixels + i; + /* Temporarily enable automatic mipmap generation and + re-upload the first pixel to cause a regeneration */ + GE( glTexParameteri (tex->gl_target, GL_GENERATE_MIPMAP, GL_TRUE) ); + GE( glTexSubImage2D (tex->gl_target, 0, 0, 0, 1, 1, + pixel->gl_format, pixel->gl_type, + pixel->data) ); + GE( glTexParameteri (tex->gl_target, GL_GENERATE_MIPMAP, GL_FALSE) ); + } + } + + tex->mipmaps_dirty = FALSE; +} + +gboolean +_cogl_texture_2d_sliced_set_region (CoglHandle handle, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int dst_width, + unsigned int dst_height, + int width, + int height, + CoglPixelFormat format, + unsigned int rowstride, + const guint8 *data) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (handle); + CoglTexture *tex = COGL_TEXTURE (handle); + gint bpp; + CoglBitmap source_bmp; + CoglBitmap temp_bmp; + gboolean source_bmp_owner = FALSE; + CoglPixelFormat closest_format; + GLenum closest_gl_format; + GLenum closest_gl_type; + gboolean success; + + /* Check for valid format */ + if (format == COGL_PIXEL_FORMAT_ANY) + return FALSE; + + /* Shortcut out early if the image is empty */ + if (width == 0 || height == 0) + return TRUE; + + /* Init source bitmap */ + source_bmp.width = width; + source_bmp.height = height; + source_bmp.format = format; + source_bmp.data = (guchar*)data; + + /* Rowstride from width if none specified */ + bpp = _cogl_get_format_bpp (format); + source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride; + + /* Find closest format to internal that's supported by GL */ + closest_format = _cogl_pixel_format_to_gl (tex->bitmap.format, + NULL, /* don't need */ + &closest_gl_format, + &closest_gl_type); + + /* If no direct match, convert */ + if (closest_format != format) + { + /* Convert to required format */ + success = _cogl_bitmap_convert_and_premult (&source_bmp, + &temp_bmp, + closest_format); + + /* Swap bitmaps if succeeded */ + if (!success) return FALSE; + source_bmp = temp_bmp; + source_bmp_owner = TRUE; + } + + /* Send data to GL */ + _cogl_texture_2d_sliced_upload_subregion_to_gl (tex_2ds, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + &source_bmp, + closest_gl_format, + closest_gl_type); + + /* Free data if owner */ + if (source_bmp_owner) + g_free (source_bmp.data); + + return TRUE; +} + +gboolean +_cogl_texture_2d_sliced_download_from_gl ( + CoglTexture2DSliced *tex_2ds, + CoglBitmap *target_bmp, + GLuint target_gl_format, + GLuint target_gl_type) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint gl_handle; + gint bpp; + gint x,y; + CoglBitmap slice_bmp; + + bpp = _cogl_get_format_bpp (target_bmp->format); + + /* Iterate vertical slices */ + for (y = 0; y < tex_2ds->slice_y_spans->len; ++y) + { + y_span = &g_array_index (tex_2ds->slice_y_spans, CoglTexSliceSpan, y); + + /* Iterate horizontal slices */ + for (x = 0; x < tex_2ds->slice_x_spans->len; ++x) + { + /*if (x != 0 || y != 1) continue;*/ + x_span = &g_array_index (tex_2ds->slice_x_spans, CoglTexSliceSpan, x); + + /* Pick the gl texture object handle */ + gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, + y * tex_2ds->slice_x_spans->len + x); + + /* If there's any waste we need to copy manually + (no glGetTexSubImage) */ + + if (y_span->waste != 0 || x_span->waste != 0) + { + /* Setup temp bitmap for slice subregion */ + slice_bmp.format = target_bmp->format; + slice_bmp.width = x_span->size; + slice_bmp.height = y_span->size; + slice_bmp.rowstride = bpp * slice_bmp.width; + slice_bmp.data = (guchar*) g_malloc (slice_bmp.rowstride * + slice_bmp.height); + + /* Setup gl alignment to 0,0 top-left corner */ + _cogl_texture_driver_prep_gl_for_pixels_download ( + slice_bmp.rowstride, + bpp); + + /* Download slice image data into temp bmp */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + if (!_cogl_texture_driver_gl_get_tex_image (tex->gl_target, + target_gl_format, + target_gl_type, + slice_bmp.data)) + { + /* Free temp bitmap */ + g_free (slice_bmp.data); + return FALSE; + } + + /* Copy portion of slice from temp to target bmp */ + _cogl_bitmap_copy_subregion (&slice_bmp, + target_bmp, + 0, 0, + x_span->start, + y_span->start, + x_span->size - x_span->waste, + y_span->size - y_span->waste); + /* Free temp bitmap */ + g_free (slice_bmp.data); + } + else + { + GLvoid *dst = target_bmp->data + + x_span->start * bpp + + y_span->start * target_bmp->rowstride; + + _cogl_texture_driver_prep_gl_for_pixels_download ( + target_bmp->rowstride, + bpp); + + /* Download slice image data */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + if (!_cogl_texture_driver_gl_get_tex_image (tex->gl_target, + target_gl_format, + target_gl_type, + dst)) + { + return FALSE; + } + } + } + } + + return TRUE; +} + +int +_cogl_texture_2d_sliced_get_data (CoglHandle handle, + CoglPixelFormat format, + unsigned int rowstride, + guint8 *data) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (handle); + CoglTexture *tex = COGL_TEXTURE (handle); + gint bpp; + gint byte_size; + CoglPixelFormat closest_format; + gint closest_bpp; + GLenum closest_gl_format; + GLenum closest_gl_type; + CoglBitmap target_bmp; + CoglBitmap new_bmp; + gboolean success; + guchar *src; + guchar *dst; + gint y; + + /* Default to internal format if none specified */ + if (format == COGL_PIXEL_FORMAT_ANY) + format = tex->bitmap.format; + + /* Rowstride from texture width if none specified */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = tex->bitmap.width * bpp; + + /* Return byte size if only that requested */ + byte_size = tex->bitmap.height * rowstride; + if (data == NULL) return byte_size; + + closest_format = + _cogl_texture_driver_find_best_gl_get_data_format (format, + &closest_gl_format, + &closest_gl_type); + closest_bpp = _cogl_get_format_bpp (closest_format); + + /* Is the requested format supported? */ + if (closest_format == format) + { + /* Target user data directly */ + target_bmp = tex->bitmap; + target_bmp.format = format; + target_bmp.rowstride = rowstride; + target_bmp.data = data; + } + else + { + /* Target intermediate buffer */ + target_bmp = tex->bitmap; + target_bmp.format = closest_format; + target_bmp.rowstride = target_bmp.width * closest_bpp; + target_bmp.data = (guchar*) g_malloc (target_bmp.height + * target_bmp.rowstride); + } + + /* Retrieve data from slices */ + if (!_cogl_texture_2d_sliced_download_from_gl (tex_2ds, &target_bmp, + closest_gl_format, + closest_gl_type)) + { + /* XXX: In some cases _cogl_texture_2d_sliced_download_from_gl may + * fail to read back the texture data; such as for GLES which doesn't + * support glGetTexImage, so here we fallback to drawing the texture + * and reading the pixels from the framebuffer. */ + _cogl_texture_draw_and_read (tex, &target_bmp, + closest_gl_format, + closest_gl_type); + } + + /* Was intermediate used? */ + if (closest_format != format) + { + /* Convert to requested format */ + success = _cogl_bitmap_convert_and_premult (&target_bmp, + &new_bmp, + format); + + /* Free intermediate data and return if failed */ + g_free (target_bmp.data); + if (!success) return 0; + + /* Copy to user buffer */ + for (y = 0; y < new_bmp.height; ++y) + { + src = new_bmp.data + y * new_bmp.rowstride; + dst = data + y * rowstride; + memcpy (dst, src, new_bmp.width); + } + + /* Free converted data */ + g_free (new_bmp.data); + } + + return byte_size; +} + diff --git a/clutter/cogl/cogl/cogl-texture-driver.h b/clutter/cogl/cogl/cogl-texture-driver.h index f2443d0bc..6d6b560f2 100644 --- a/clutter/cogl/cogl/cogl-texture-driver.h +++ b/clutter/cogl/cogl/cogl-texture-driver.h @@ -91,6 +91,18 @@ _cogl_texture_driver_download_from_gl (CoglTexture *tex, GLuint target_gl_format, GLuint target_gl_type); +/* + * This driver abstraction is needed because GLES doesn't support glGetTexImage + * (). On GLES this currently just returns FALSE which will lead to a generic + * fallback path being used that simply renders the texture and reads it back + * from the framebuffer. (See _cogl_texture_draw_and_read () ) + */ +gboolean +_cogl_texture_driver_gl_get_tex_image (GLenum gl_target, + GLenum dest_gl_format, + GLenum dest_gl_type, + guint8 *dest); + /* * It may depend on the driver as to what texture sizes are supported... */ diff --git a/clutter/cogl/cogl/cogl-texture-private.h b/clutter/cogl/cogl/cogl-texture-private.h index e82bda9a7..454cf6b90 100644 --- a/clutter/cogl/cogl/cogl-texture-private.h +++ b/clutter/cogl/cogl/cogl-texture-private.h @@ -28,72 +28,31 @@ #include "cogl-handle.h" #include "cogl-material-private.h" +#define COGL_TEXTURE(tex) ((CoglTexture *)(tex)) + typedef struct _CoglTexture CoglTexture; -typedef struct _CoglTexSliceSpan CoglTexSliceSpan; -typedef struct _CoglSpanIter CoglSpanIter; -typedef struct _CoglTexturePixel CoglTexturePixel; -struct _CoglTexSliceSpan +typedef enum _CoglTextureType { - gint start; - gint size; - gint waste; -}; - -struct _CoglSpanIter -{ - gint index; - GArray *array; - CoglTexSliceSpan *span; - float pos; - float next_pos; - float origin; - float cover_start; - float cover_end; - float intersect_start; - float intersect_end; - float intersect_start_local; - float intersect_end_local; - gboolean intersects; - gboolean flipped; -}; - -/* This is used to store the first pixel of each slice. This is only - used when glGenerateMipmap is not available */ -struct _CoglTexturePixel -{ - /* We need to store the format of the pixel because we store the - data in the source format which might end up being different for - each slice if a subregion is updated with a different format */ - GLenum gl_format; - GLenum gl_type; - guint8 data[4]; -}; + COGL_TEXTURE_TYPE_2D_SLICED +} CoglTextureType; struct _CoglTexture { CoglHandleObject _parent; + CoglTextureType type; CoglBitmap bitmap; gboolean bitmap_owner; GLenum gl_target; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; - GArray *slice_x_spans; - GArray *slice_y_spans; - GArray *slice_gl_handles; - gint max_waste; GLenum min_filter; GLenum mag_filter; gboolean is_foreign; GLint wrap_mode; gboolean auto_mipmap; gboolean mipmaps_dirty; - - /* This holds a copy of the first pixel in each slice. It is only - used to force an automatic update of the mipmaps when - glGenerateMipmap is not available. */ - CoglTexturePixel *first_pixels; }; /* To improve batching of geometry when submitting vertices to OpenGL we @@ -117,9 +76,6 @@ typedef void (*CoglTextureSliceCallback) (CoglHandle handle, float *virtual_coords, void *user_data); -CoglTexture* -_cogl_texture_pointer_from_handle (CoglHandle handle); - void _cogl_texture_foreach_sub_texture_in_region (CoglHandle handle, float virtual_tx_1, @@ -151,10 +107,34 @@ _cogl_texture_set_filters (CoglHandle handle, void _cogl_texture_ensure_mipmaps (CoglHandle handle); + +/* Functions currently only used by CoglTexture implementations or + * drivers... */ + +void +_cogl_texture_free (CoglTexture *tex); + +void +_cogl_texture_bitmap_free (CoglTexture *tex); + +void +_cogl_texture_bitmap_swap (CoglTexture *tex, + CoglBitmap *new_bitmap); + +gboolean +_cogl_texture_bitmap_prepare (CoglTexture *tex, + CoglPixelFormat internal_format); + void _cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride); void _cogl_texture_prep_gl_alignment_for_pixels_download (int pixels_rowstride); +gboolean +_cogl_texture_draw_and_read (CoglTexture *tex, + CoglBitmap *target_bmp, + GLuint target_gl_format, + GLuint target_gl_type); + #endif /* __COGL_TEXTURE_PRIVATE_H */ diff --git a/clutter/cogl/cogl/cogl-texture.c b/clutter/cogl/cogl/cogl-texture.c index 796d1c234..d1c561a0f 100644 --- a/clutter/cogl/cogl/cogl-texture.c +++ b/clutter/cogl/cogl/cogl-texture.c @@ -37,6 +37,7 @@ #include "cogl-bitmap-private.h" #include "cogl-texture-private.h" #include "cogl-texture-driver.h" +#include "cogl-texture-2d-sliced-private.h" #include "cogl-material.h" #include "cogl-context.h" #include "cogl-handle.h" @@ -46,11 +47,54 @@ #include #include -static void _cogl_texture_free (CoglTexture *tex); +/* XXX: + * The CoglHandle macros don't support any form of inheritance, so for + * now we implement the CoglHandle support for the CoglTexture + * abstract class manually. + */ -COGL_HANDLE_DEFINE (Texture, texture); +gboolean +cogl_is_texture (CoglHandle handle) +{ + CoglHandleObject *obj = (CoglHandleObject *)handle; -static void + if (handle == COGL_INVALID_HANDLE) + return FALSE; + + return obj->klass->type == _cogl_handle_texture_2d_sliced_get_type (); + //|| obj->klass->type == _cogl_handle_texture_3d_get_type (); +} + +CoglHandle +cogl_texture_ref (CoglHandle handle) +{ + if (!cogl_is_texture (handle)) + return COGL_INVALID_HANDLE; + + _COGL_HANDLE_DEBUG_REF (CoglTexture, handle); + + cogl_handle_ref (handle); + + return handle; +} + +void +cogl_texture_unref (CoglHandle handle) +{ + if (!cogl_is_texture (handle)) + { + g_warning (G_STRINGIFY (cogl_texture_unref) + ": Ignoring unref of Cogl handle " + "due to type mismatch"); + return; + } + + _COGL_HANDLE_DEBUG_UNREF (CoglTexture, handle); + + cogl_handle_unref (handle); +} + +void _cogl_texture_bitmap_free (CoglTexture *tex) { if (tex->bitmap.data != NULL && tex->bitmap_owner) @@ -60,7 +104,7 @@ _cogl_texture_bitmap_free (CoglTexture *tex) tex->bitmap_owner = FALSE; } -static void +void _cogl_texture_bitmap_swap (CoglTexture *tex, CoglBitmap *new_bitmap) { @@ -71,243 +115,6 @@ _cogl_texture_bitmap_swap (CoglTexture *tex, tex->bitmap_owner = TRUE; } -static void -_cogl_span_iter_update (CoglSpanIter *iter) -{ - /* Pick current span */ - iter->span = &g_array_index (iter->array, - CoglTexSliceSpan, - iter->index); - - /* Offset next position by span size */ - iter->next_pos = iter->pos + - (float)(iter->span->size - iter->span->waste); - - /* Check if span intersects the area to cover */ - if (iter->next_pos <= iter->cover_start || - iter->pos >= iter->cover_end) - { - /* Intersection undefined */ - iter->intersects = FALSE; - return; - } - - iter->intersects = TRUE; - - /* Clip start position to coverage area */ - if (iter->pos < iter->cover_start) - iter->intersect_start = iter->cover_start; - else - iter->intersect_start = iter->pos; - - /* Clip end position to coverage area */ - if (iter->next_pos > iter->cover_end) - iter->intersect_end = iter->cover_end; - else - iter->intersect_end = iter->next_pos; -} - -void -_cogl_span_iter_begin (CoglSpanIter *iter, - GArray *spans, - float normalize_factor, - float cover_start, - float cover_end) -{ - float cover_start_normalized; - - iter->index = 0; - iter->span = NULL; - - iter->array = spans; - - /* We always iterate in a positive direction from the origin. If - * iter->flipped == TRUE that means whoever is using this API should - * interpreted the current span as extending in the opposite direction. I.e. - * it extends to the left if iterating the X axis, or up if the Y axis. */ - if (cover_start > cover_end) - { - float tmp = cover_start; - cover_start = cover_end; - cover_end = tmp; - iter->flipped = TRUE; - } - else - iter->flipped = FALSE; - - /* The texture spans cover the normalized texture coordinate space ranging - * from [0,1] but to help support repeating of sliced textures we allow - * iteration of any range so we need to relate the start of the range to the - * nearest point equivalent to 0. - */ - cover_start_normalized = cover_start / normalize_factor; - iter->origin = floorf (cover_start_normalized) * normalize_factor; - - iter->cover_start = cover_start; - iter->cover_end = cover_end; - iter->pos = iter->origin; - - /* Update intersection */ - _cogl_span_iter_update (iter); -} - -void -_cogl_span_iter_next (CoglSpanIter *iter) -{ - /* Move current position */ - iter->pos = iter->next_pos; - - /* Pick next slice (wrap when last reached) */ - iter->index = (iter->index + 1) % iter->array->len; - - /* Update intersection */ - _cogl_span_iter_update (iter); -} - -gboolean -_cogl_span_iter_end (CoglSpanIter *iter) -{ - /* End reached when whole area covered */ - return iter->pos >= iter->cover_end; -} - -/* Some CoglTextures, notably sliced textures or atlas textures when repeating - * is used, will need to divide the coordinate space into multiple GL textures - * (or rather; in the case of atlases duplicate a single texture in multiple - * positions to handle repeating) - * - * This function helps you implement primitives using such textures by - * invoking a callback once for each sub texture that intersects a given - * region specified in texture coordinates. - */ -/* To differentiate between texture coordinates of a specific, real, slice - * texture and the texture coordinates of the composite, sliced texture, the - * coordinates of the sliced texture are called "virtual" coordinates and the - * coordinates of slices are called "slice" coordinates. */ -/* This function lets you iterate all the slices that lie within the given - * virtual coordinates of the parent sliced texture. */ -/* Note: no guarantee is given about the order in which the slices will be - * visited */ -void -_cogl_texture_foreach_sub_texture_in_region (CoglHandle handle, - float virtual_tx_1, - float virtual_ty_1, - float virtual_tx_2, - float virtual_ty_2, - CoglTextureSliceCallback callback, - void *user_data) -{ - CoglTexture *tex = _cogl_texture_pointer_from_handle (handle); - float width = tex->bitmap.width; - float height = tex->bitmap.height; - CoglSpanIter iter_x; - CoglSpanIter iter_y; - - g_assert (tex->gl_target == GL_TEXTURE_2D); - - /* Slice spans are stored in denormalized coordinates, and this is what - * the _cogl_span_iter_* funcs expect to be given, so we scale the given - * virtual coordinates by the texture size to denormalize. - */ - /* XXX: I wonder if it's worth changing how we store spans so we can avoid - * the need to denormalize here */ - virtual_tx_1 *= width; - virtual_ty_1 *= height; - virtual_tx_2 *= width; - virtual_ty_2 *= height; - - /* Iterate the y axis of the virtual rectangle */ - for (_cogl_span_iter_begin (&iter_y, - tex->slice_y_spans, - height, - virtual_ty_1, - virtual_ty_2); - !_cogl_span_iter_end (&iter_y); - _cogl_span_iter_next (&iter_y)) - { - float y_intersect_start = iter_y.intersect_start; - float y_intersect_end = iter_y.intersect_end; - float slice_ty1; - float slice_ty2; - - /* Discard slices out of rectangle early */ - if (!iter_y.intersects) - continue; - - if (iter_y.flipped) - { - y_intersect_start = iter_y.intersect_end; - y_intersect_end = iter_y.intersect_start; - } - - /* Localize slice texture coordinates */ - slice_ty1 = y_intersect_start - iter_y.pos; - slice_ty2 = y_intersect_end - iter_y.pos; - - /* Normalize slice texture coordinates */ - slice_ty1 /= iter_y.span->size; - slice_ty2 /= iter_y.span->size; - - /* Iterate the x axis of the virtual rectangle */ - for (_cogl_span_iter_begin (&iter_x, - tex->slice_x_spans, - width, - virtual_tx_1, - virtual_tx_2); - !_cogl_span_iter_end (&iter_x); - _cogl_span_iter_next (&iter_x)) - { - float slice_coords[4]; - float virtual_coords[4]; - float x_intersect_start = iter_x.intersect_start; - float x_intersect_end = iter_x.intersect_end; - float slice_tx1; - float slice_tx2; - GLuint gl_handle; - - /* Discard slices out of rectangle early */ - if (!iter_x.intersects) - continue; - - if (iter_x.flipped) - { - x_intersect_start = iter_x.intersect_end; - x_intersect_end = iter_x.intersect_start; - } - - /* Localize slice texture coordinates */ - slice_tx1 = x_intersect_start - iter_x.pos; - slice_tx2 = x_intersect_end - iter_x.pos; - - /* Normalize slice texture coordinates */ - slice_tx1 /= iter_x.span->size; - slice_tx2 /= iter_x.span->size; - - /* Pluck out opengl texture object for this slice */ - gl_handle = g_array_index (tex->slice_gl_handles, GLuint, - iter_y.index * iter_x.array->len + - iter_x.index); - - slice_coords[0] = slice_tx1; - slice_coords[1] = slice_ty1; - slice_coords[2] = slice_tx2; - slice_coords[3] = slice_ty2; - - virtual_coords[0] = x_intersect_start / width; - virtual_coords[1] = y_intersect_start / height; - virtual_coords[2] = x_intersect_end / width; - virtual_coords[3] = y_intersect_end / height; - - callback (tex, - gl_handle, - tex->gl_target, - slice_coords, - virtual_coords, - user_data); - } - } -} - void _cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride) { @@ -334,714 +141,24 @@ _cogl_texture_prep_gl_alignment_for_pixels_download (int pixels_rowstride) GE( glPixelStorei (GL_PACK_ALIGNMENT, 1) ); } -static guchar * -_cogl_texture_allocate_waste_buffer (CoglTexture *tex) -{ - CoglTexSliceSpan *last_x_span; - CoglTexSliceSpan *last_y_span; - guchar *waste_buf = NULL; - - /* If the texture has any waste then allocate a buffer big enough to - fill the gaps */ - last_x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, - tex->slice_x_spans->len - 1); - last_y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, - tex->slice_y_spans->len - 1); - if (last_x_span->waste > 0 || last_y_span->waste > 0) - { - gint bpp = _cogl_get_format_bpp (tex->bitmap.format); - CoglTexSliceSpan *first_x_span - = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); - CoglTexSliceSpan *first_y_span - = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); - guint right_size = first_y_span->size * last_x_span->waste; - guint bottom_size = first_x_span->size * last_y_span->waste; - - waste_buf = g_malloc (MAX (right_size, bottom_size) * bpp); - } - - return waste_buf; -} - -static gboolean -_cogl_texture_upload_to_gl (CoglTexture *tex) -{ - CoglTexSliceSpan *x_span; - CoglTexSliceSpan *y_span; - GLuint gl_handle; - gint bpp; - gint x,y; - guchar *waste_buf; - - bpp = _cogl_get_format_bpp (tex->bitmap.format); - - waste_buf = _cogl_texture_allocate_waste_buffer (tex); - - /* Iterate vertical slices */ - for (y = 0; y < tex->slice_y_spans->len; ++y) - { - y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); - - /* Iterate horizontal slices */ - for (x = 0; x < tex->slice_x_spans->len; ++x) - { - gint slice_num = y * tex->slice_x_spans->len + x; - - x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); - - /* Pick the gl texture object handle */ - gl_handle = g_array_index (tex->slice_gl_handles, GLuint, slice_num); - - _cogl_texture_driver_upload_subregion_to_gl ( - tex, - x_span->start, /* src x */ - y_span->start, /* src y */ - 0, /* dst x */ - 0, /* dst y */ - x_span->size - x_span->waste, /* width */ - y_span->size - y_span->waste, /* height */ - &tex->bitmap, - tex->gl_format, - tex->gl_type, - gl_handle); - - /* Keep a copy of the first pixel if needed */ - if (tex->first_pixels) - { - memcpy (tex->first_pixels[slice_num].data, - tex->bitmap.data + x_span->start * bpp - + y_span->start * tex->bitmap.rowstride, - bpp); - tex->first_pixels[slice_num].gl_format = tex->gl_format; - tex->first_pixels[slice_num].gl_type = tex->gl_type; - } - - /* Fill the waste with a copies of the rightmost pixels */ - if (x_span->waste > 0) - { - const guchar *src = tex->bitmap.data - + y_span->start * tex->bitmap.rowstride - + (x_span->start + x_span->size - x_span->waste - 1) * bpp; - guchar *dst = waste_buf; - guint wx, wy; - - for (wy = 0; wy < y_span->size - y_span->waste; wy++) - { - for (wx = 0; wx < x_span->waste; wx++) - { - memcpy (dst, src, bpp); - dst += bpp; - } - src += tex->bitmap.rowstride; - } - - _cogl_texture_driver_prep_gl_for_pixels_upload ( - x_span->waste * bpp, - bpp); - - GE( glTexSubImage2D (tex->gl_target, 0, - x_span->size - x_span->waste, - 0, - x_span->waste, - y_span->size - y_span->waste, - tex->gl_format, tex->gl_type, - waste_buf) ); - } - - if (y_span->waste > 0) - { - const guchar *src = tex->bitmap.data - + ((y_span->start + y_span->size - y_span->waste - 1) - * tex->bitmap.rowstride) - + x_span->start * bpp; - guchar *dst = waste_buf; - guint wy, wx; - - for (wy = 0; wy < y_span->waste; wy++) - { - memcpy (dst, src, (x_span->size - x_span->waste) * bpp); - dst += (x_span->size - x_span->waste) * bpp; - - for (wx = 0; wx < x_span->waste; wx++) - { - memcpy (dst, dst - bpp, bpp); - dst += bpp; - } - } - - _cogl_texture_driver_prep_gl_for_pixels_upload ( - x_span->size * bpp, - bpp); - - GE( glTexSubImage2D (tex->gl_target, 0, - 0, - y_span->size - y_span->waste, - x_span->size, - y_span->waste, - tex->gl_format, tex->gl_type, - waste_buf) ); - } - } - } - - if (waste_buf) - g_free (waste_buf); - - tex->mipmaps_dirty = TRUE; - - return TRUE; -} - -static gboolean -_cogl_texture_upload_subregion_to_gl (CoglTexture *tex, - gint src_x, - gint src_y, - gint dst_x, - gint dst_y, - gint width, - gint height, - CoglBitmap *source_bmp, - GLuint source_gl_format, - GLuint source_gl_type) -{ - CoglTexSliceSpan *x_span; - CoglTexSliceSpan *y_span; - gint bpp; - CoglSpanIter x_iter; - CoglSpanIter y_iter; - GLuint gl_handle; - gint source_x = 0, source_y = 0; - gint inter_w = 0, inter_h = 0; - gint local_x = 0, local_y = 0; - guchar *waste_buf; - - bpp = _cogl_get_format_bpp (source_bmp->format); - - waste_buf = _cogl_texture_allocate_waste_buffer (tex); - - /* Iterate vertical spans */ - for (source_y = src_y, - _cogl_span_iter_begin (&y_iter, tex->slice_y_spans, - tex->bitmap.height, (float)(dst_y), - (float)(dst_y + height)); - - !_cogl_span_iter_end (&y_iter); - - _cogl_span_iter_next (&y_iter), - source_y += inter_h ) - { - /* Discard slices out of the subregion early */ - if (!y_iter.intersects) - { - inter_h = 0; - continue; - } - - y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, - y_iter.index); - - /* Iterate horizontal spans */ - for (source_x = src_x, - _cogl_span_iter_begin (&x_iter, tex->slice_x_spans, - tex->bitmap.width, (float)(dst_x), - (float)(dst_x + width)); - - !_cogl_span_iter_end (&x_iter); - - _cogl_span_iter_next (&x_iter), - source_x += inter_w ) - { - gint slice_num; - - /* Discard slices out of the subregion early */ - if (!x_iter.intersects) - { - inter_w = 0; - continue; - } - - x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, - x_iter.index); - - /* Pick intersection width and height */ - inter_w = (x_iter.intersect_end - x_iter.intersect_start); - inter_h = (y_iter.intersect_end - y_iter.intersect_start); - - /* Localize intersection top-left corner to slice*/ - local_x = (x_iter.intersect_start - x_iter.pos); - local_y = (y_iter.intersect_start - y_iter.pos); - - slice_num = y_iter.index * tex->slice_x_spans->len + x_iter.index; - - /* Pick slice GL handle */ - gl_handle = g_array_index (tex->slice_gl_handles, GLuint, slice_num); - - _cogl_texture_driver_upload_subregion_to_gl (tex, - source_x, - source_y, - local_x, /* dst x */ - local_y, /* dst x */ - inter_w, /* width */ - inter_h, /* height */ - source_bmp, - source_gl_format, - source_gl_type, - gl_handle); - - /* Keep a copy of the first pixel if needed */ - if (tex->first_pixels && local_x == 0 && local_y == 0) - { - memcpy (tex->first_pixels[slice_num].data, - source_bmp->data + source_x * bpp - + source_y * source_bmp->rowstride, - bpp); - tex->first_pixels[slice_num].gl_format = source_gl_format; - tex->first_pixels[slice_num].gl_type = source_gl_type; - } - - /* If the x_span is sliced and the upload touches the - rightmost pixels then fill the waste with copies of the - pixels */ - if (x_span->waste > 0 - && local_x < x_span->size - x_span->waste - && local_x + inter_w >= x_span->size - x_span->waste) - { - const guchar *src; - guchar *dst; - guint wx, wy; - - src = source_bmp->data - + (src_y + ((int)y_iter.intersect_start) - - dst_y) - * source_bmp->rowstride - + (src_x + x_span->start + x_span->size - x_span->waste - - dst_x - 1) - * bpp; - - dst = waste_buf; - - for (wy = 0; wy < inter_h; wy++) - { - for (wx = 0; wx < x_span->waste; wx++) - { - memcpy (dst, src, bpp); - dst += bpp; - } - src += source_bmp->rowstride; - } - - _cogl_texture_driver_prep_gl_for_pixels_upload ( - x_span->waste * bpp, - bpp); - - GE( glTexSubImage2D (tex->gl_target, 0, - x_span->size - x_span->waste, - local_y, - x_span->waste, - inter_h, - source_gl_format, - source_gl_type, - waste_buf) ); - } - - /* same for the bottom-most pixels */ - if (y_span->waste > 0 - && local_y < y_span->size - y_span->waste - && local_y + inter_h >= y_span->size - y_span->waste) - { - const guchar *src; - guchar *dst; - guint wy, wx; - guint copy_width; - - src = source_bmp->data - + (src_x + ((int)x_iter.intersect_start) - - dst_x) - * bpp - + (src_y + y_span->start + y_span->size - y_span->waste - - dst_y - 1) - * source_bmp->rowstride; - - dst = waste_buf; - - if (local_x + inter_w >= x_span->size - x_span->waste) - copy_width = x_span->size - local_x; - else - copy_width = inter_w; - - for (wy = 0; wy < y_span->waste; wy++) - { - memcpy (dst, src, inter_w * bpp); - dst += inter_w * bpp; - - for (wx = inter_w; wx < copy_width; wx++) - { - memcpy (dst, dst - bpp, bpp); - dst += bpp; - } - } - - _cogl_texture_driver_prep_gl_for_pixels_upload ( - copy_width * bpp, - bpp); - - GE( glTexSubImage2D (tex->gl_target, 0, - local_x, - y_span->size - y_span->waste, - copy_width, - y_span->waste, - source_gl_format, - source_gl_type, - waste_buf) ); - } - } - } - - if (waste_buf) - g_free (waste_buf); - - tex->mipmaps_dirty = TRUE; - - return TRUE; -} - -static gint -_cogl_rect_slices_for_size (gint size_to_fill, - gint max_span_size, - gint max_waste, - GArray *out_spans) -{ - gint n_spans = 0; - CoglTexSliceSpan span; - - /* Init first slice span */ - span.start = 0; - span.size = max_span_size; - span.waste = 0; - - /* Repeat until whole area covered */ - while (size_to_fill >= span.size) - { - /* Add another slice span of same size */ - if (out_spans) g_array_append_val (out_spans, span); - span.start += span.size; - size_to_fill -= span.size; - n_spans++; - } - - /* Add one last smaller slice span */ - if (size_to_fill > 0) - { - span.size = size_to_fill; - if (out_spans) g_array_append_val (out_spans, span); - n_spans++; - } - - return n_spans; -} - -static gint -_cogl_pot_slices_for_size (gint size_to_fill, - gint max_span_size, - gint max_waste, - GArray *out_spans) -{ - gint n_spans = 0; - CoglTexSliceSpan span; - - /* Init first slice span */ - span.start = 0; - span.size = max_span_size; - span.waste = 0; - - /* Fix invalid max_waste */ - if (max_waste < 0) - max_waste = 0; - - while (TRUE) - { - /* Is the whole area covered? */ - if (size_to_fill > span.size) - { - /* Not yet - add a span of this size */ - if (out_spans) - g_array_append_val (out_spans, span); - - span.start += span.size; - size_to_fill -= span.size; - n_spans++; - } - else if (span.size - size_to_fill <= max_waste) - { - /* Yes and waste is small enough */ - span.waste = span.size - size_to_fill; - if (out_spans) - g_array_append_val (out_spans, span); - - return ++n_spans; - } - else - { - /* Yes but waste is too large */ - while (span.size - size_to_fill > max_waste) - { - span.size /= 2; - g_assert (span.size > 0); - } - } - } - - /* Can't get here */ - return 0; -} - +/* FIXME: wrap modes should be set on materials not textures */ void _cogl_texture_set_wrap_mode_parameter (CoglHandle handle, GLenum wrap_mode) { - CoglTexture *tex = _cogl_texture_pointer_from_handle (handle); + CoglTexture *tex = COGL_TEXTURE (handle); - /* Only set the wrap mode if it's different from the current - value to avoid too many GL calls */ - if (tex->wrap_mode != wrap_mode) + switch (tex->type) { - int i; - - /* Any queued texture rectangles may be depending on the previous - * wrap mode... */ - _cogl_journal_flush (); - - for (i = 0; i < tex->slice_gl_handles->len; i++) - { - GLuint texnum = g_array_index (tex->slice_gl_handles, GLuint, i); - - GE( glBindTexture (tex->gl_target, texnum) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, wrap_mode) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, wrap_mode) ); - } - - tex->wrap_mode = wrap_mode; + case COGL_TEXTURE_TYPE_2D_SLICED: + _cogl_texture_2d_sliced_set_wrap_mode_parameter ( + (CoglTexture2DSliced *)tex, + wrap_mode); + break; } } -static gboolean -_cogl_texture_slices_create (CoglTexture *tex) -{ - gint bpp; - gint max_width; - gint max_height; - GLuint *gl_handles; - gint n_x_slices; - gint n_y_slices; - gint n_slices; - gint x, y; - CoglTexSliceSpan *x_span; - CoglTexSliceSpan *y_span; - const GLfloat transparent_color[4] = { 0x00, 0x00, 0x00, 0x00 }; - - gint (*slices_for_size) (gint, gint, gint, GArray*); - - bpp = _cogl_get_format_bpp (tex->bitmap.format); - - /* Initialize size of largest slice according to supported features */ - if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT)) - { - max_width = tex->bitmap.width; - max_height = tex->bitmap.height; - tex->gl_target = GL_TEXTURE_2D; - slices_for_size = _cogl_rect_slices_for_size; - } - else - { - max_width = cogl_util_next_p2 (tex->bitmap.width); - max_height = cogl_util_next_p2 (tex->bitmap.height); - tex->gl_target = GL_TEXTURE_2D; - slices_for_size = _cogl_pot_slices_for_size; - } - - /* Negative number means no slicing forced by the user */ - if (tex->max_waste <= -1) - { - CoglTexSliceSpan span; - - /* Check if size supported else bail out */ - if (!_cogl_texture_driver_size_supported (tex->gl_target, - tex->gl_format, - tex->gl_type, - max_width, - max_height)) - { - return FALSE; - } - - n_x_slices = 1; - n_y_slices = 1; - - /* Init span arrays */ - tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, - sizeof (CoglTexSliceSpan), - 1); - - tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, - sizeof (CoglTexSliceSpan), - 1); - - /* Add a single span for width and height */ - span.start = 0; - span.size = max_width; - span.waste = max_width - tex->bitmap.width; - g_array_append_val (tex->slice_x_spans, span); - - span.size = max_height; - span.waste = max_height - tex->bitmap.height; - g_array_append_val (tex->slice_y_spans, span); - } - else - { - /* Decrease the size of largest slice until supported by GL */ - while (!_cogl_texture_driver_size_supported (tex->gl_target, - tex->gl_format, - tex->gl_type, - max_width, - max_height)) - { - /* Alternate between width and height */ - if (max_width > max_height) - max_width /= 2; - else - max_height /= 2; - - if (max_width == 0 || max_height == 0) - return FALSE; - } - - /* Determine the slices required to cover the bitmap area */ - n_x_slices = slices_for_size (tex->bitmap.width, - max_width, tex->max_waste, - NULL); - - n_y_slices = slices_for_size (tex->bitmap.height, - max_height, tex->max_waste, - NULL); - - /* Init span arrays with reserved size */ - tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, - sizeof (CoglTexSliceSpan), - n_x_slices); - - tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, - sizeof (CoglTexSliceSpan), - n_y_slices); - - /* Fill span arrays with info */ - slices_for_size (tex->bitmap.width, - max_width, tex->max_waste, - tex->slice_x_spans); - - slices_for_size (tex->bitmap.height, - max_height, tex->max_waste, - tex->slice_y_spans); - } - - /* Init and resize GL handle array */ - n_slices = n_x_slices * n_y_slices; - - tex->slice_gl_handles = g_array_sized_new (FALSE, FALSE, - sizeof (GLuint), - n_slices); - - g_array_set_size (tex->slice_gl_handles, n_slices); - - /* Allocate some space to store a copy of the first pixel of each - slice. This is only needed if glGenerateMipmap (which is part of - the FBO extension) is not available */ - if (cogl_features_available (COGL_FEATURE_OFFSCREEN)) - tex->first_pixels = NULL; - else - tex->first_pixels = g_new (CoglTexturePixel, n_slices); - - /* Wrap mode not yet set */ - tex->wrap_mode = GL_FALSE; - - /* Generate a "working set" of GL texture objects - * (some implementations might supported faster - * re-binding between textures inside a set) */ - gl_handles = (GLuint*) tex->slice_gl_handles->data; - - GE( glGenTextures (n_slices, gl_handles) ); - - - /* Init each GL texture object */ - for (y = 0; y < n_y_slices; ++y) - { - y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); - - for (x = 0; x < n_x_slices; ++x) - { - x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); - - COGL_NOTE (TEXTURE, "CREATE SLICE (%d,%d)\tsize (%d,%d)", - x, y, - x_span->size - x_span->waste, - y_span->size - y_span->waste); - - /* Setup texture parameters */ - GE( _cogl_texture_driver_bind (tex->gl_target, - gl_handles[y * n_x_slices + x], - tex->gl_intformat) ); - - _cogl_texture_driver_try_setting_gl_border_color (tex->gl_target, - transparent_color); - - /* Pass NULL data to init size and internal format */ - GE( glTexImage2D (tex->gl_target, 0, tex->gl_intformat, - x_span->size, y_span->size, 0, - tex->gl_format, tex->gl_type, 0) ); - } - } - - return TRUE; -} - -static void -_cogl_texture_slices_free (CoglTexture *tex) -{ - if (tex->slice_x_spans != NULL) - g_array_free (tex->slice_x_spans, TRUE); - - if (tex->slice_y_spans != NULL) - g_array_free (tex->slice_y_spans, TRUE); - - if (tex->slice_gl_handles != NULL) - { - if (tex->is_foreign == FALSE) - { - GE( glDeleteTextures (tex->slice_gl_handles->len, - (GLuint*) tex->slice_gl_handles->data) ); - } - - g_array_free (tex->slice_gl_handles, TRUE); - } - - if (tex->first_pixels != NULL) - g_free (tex->first_pixels); -} - gboolean -_cogl_texture_span_has_waste (CoglTexture *tex, - gint x_span_index, - gint y_span_index) -{ - CoglTexSliceSpan *x_span; - CoglTexSliceSpan *y_span; - - x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x_span_index); - y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y_span_index); - - return (x_span->waste || y_span->waste) ? TRUE : FALSE; -} - -static gboolean _cogl_texture_bitmap_prepare (CoglTexture *tex, CoglPixelFormat internal_format) { @@ -1084,14 +201,10 @@ _cogl_texture_bitmap_prepare (CoglTexture *tex, return TRUE; } -static void +void _cogl_texture_free (CoglTexture *tex) { - /* Frees texture resources but its handle is not - released! Do that separately before this! */ _cogl_texture_bitmap_free (tex); - _cogl_texture_slices_free (tex); - g_free (tex); } CoglHandle @@ -1100,61 +213,10 @@ cogl_texture_new_with_size (guint width, CoglTextureFlags flags, CoglPixelFormat internal_format) { - CoglTexture *tex; - gint bpp; - gint rowstride; - - /* Since no data, we need some internal format */ - if (internal_format == COGL_PIXEL_FORMAT_ANY) - return COGL_INVALID_HANDLE; - - /* Rowstride from width */ - bpp = _cogl_get_format_bpp (internal_format); - rowstride = width * bpp; - - /* Init texture with empty bitmap */ - tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); - - tex->is_foreign = FALSE; - tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; - tex->mipmaps_dirty = TRUE; - tex->first_pixels = NULL; - - tex->bitmap.width = width; - tex->bitmap.height = height; - tex->bitmap.format = internal_format; - tex->bitmap.rowstride = rowstride; - tex->bitmap.data = NULL; - tex->bitmap_owner = FALSE; - - tex->slice_x_spans = NULL; - tex->slice_y_spans = NULL; - tex->slice_gl_handles = NULL; - - if (flags & COGL_TEXTURE_NO_SLICING) - tex->max_waste = -1; - else - tex->max_waste = COGL_TEXTURE_MAX_WASTE; - - /* Unknown filter */ - tex->min_filter = GL_FALSE; - tex->mag_filter = GL_FALSE; - - /* Find closest GL format match */ - tex->bitmap.format = - _cogl_pixel_format_to_gl (internal_format, - &tex->gl_intformat, - &tex->gl_format, - &tex->gl_type); - - /* Create slices for the given format and size */ - if (!_cogl_texture_slices_create (tex)) - { - _cogl_texture_free (tex); - return COGL_INVALID_HANDLE; - } - - return _cogl_texture_handle_new (tex); + return _cogl_texture_2d_sliced_new_with_size (width, + height, + flags, + internal_format); } CoglHandle @@ -1166,73 +228,13 @@ cogl_texture_new_from_data (guint width, guint rowstride, const guchar *data) { - CoglTexture *tex; - gint bpp; - - if (format == COGL_PIXEL_FORMAT_ANY) - return COGL_INVALID_HANDLE; - - if (data == NULL) - return COGL_INVALID_HANDLE; - - /* Rowstride from width if not given */ - bpp = _cogl_get_format_bpp (format); - if (rowstride == 0) rowstride = width * bpp; - - /* Create new texture and fill with given data */ - tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); - - tex->is_foreign = FALSE; - tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; - tex->mipmaps_dirty = TRUE; - tex->first_pixels = NULL; - - tex->bitmap.width = width; - tex->bitmap.height = height; - tex->bitmap.data = (guchar*)data; - tex->bitmap.format = format; - tex->bitmap.rowstride = rowstride; - tex->bitmap_owner = FALSE; - - tex->slice_x_spans = NULL; - tex->slice_y_spans = NULL; - tex->slice_gl_handles = NULL; - - if (flags & COGL_TEXTURE_NO_SLICING) - tex->max_waste = -1; - else - tex->max_waste = COGL_TEXTURE_MAX_WASTE; - - /* Unknown filter */ - tex->min_filter = GL_FALSE; - tex->mag_filter = GL_FALSE; - - /* FIXME: If upload fails we should set some kind of - * error flag but still return texture handle (this - * is to keep the behavior equal to _new_from_file; - * see below) */ - - if (!_cogl_texture_bitmap_prepare (tex, internal_format)) - { - _cogl_texture_free (tex); - return COGL_INVALID_HANDLE; - } - - if (!_cogl_texture_slices_create (tex)) - { - _cogl_texture_free (tex); - return COGL_INVALID_HANDLE; - } - - if (!_cogl_texture_upload_to_gl (tex)) - { - _cogl_texture_free (tex); - return COGL_INVALID_HANDLE; - } - - _cogl_texture_bitmap_free (tex); - - return _cogl_texture_handle_new (tex); + return _cogl_texture_2d_sliced_new_from_data (width, + height, + flags, + format, + internal_format, + rowstride, + data); } CoglHandle @@ -1240,64 +242,9 @@ cogl_texture_new_from_bitmap (CoglHandle bmp_handle, CoglTextureFlags flags, CoglPixelFormat internal_format) { - CoglTexture *tex; - CoglBitmap *bmp = (CoglBitmap *)bmp_handle; - - g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); - - /* Create new texture and fill with loaded data */ - tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture)); - - tex->is_foreign = FALSE; - tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; - tex->mipmaps_dirty = TRUE; - tex->first_pixels = NULL; - - tex->bitmap = *bmp; - tex->bitmap_owner = FALSE; - - tex->slice_x_spans = NULL; - tex->slice_y_spans = NULL; - tex->slice_gl_handles = NULL; - - if (flags & COGL_TEXTURE_NO_SLICING) - tex->max_waste = -1; - else - tex->max_waste = COGL_TEXTURE_MAX_WASTE; - - /* Unknown filter */ - tex->min_filter = GL_FALSE; - tex->mag_filter = GL_FALSE; - - /* FIXME: If upload fails we should set some kind of - * error flag but still return texture handle if the - * user decides to destroy another texture and upload - * this one instead (reloading from file is not needed - * in that case). As a rule then, everytime a valid - * CoglHandle is returned, it should also be destroyed - * with cogl_handle_unref at some point! */ - - if (!_cogl_texture_bitmap_prepare (tex, internal_format)) - { - _cogl_texture_free (tex); - return COGL_INVALID_HANDLE; - } - - if (!_cogl_texture_slices_create (tex)) - { - _cogl_texture_free (tex); - return COGL_INVALID_HANDLE; - } - - if (!_cogl_texture_upload_to_gl (tex)) - { - _cogl_texture_free (tex); - return COGL_INVALID_HANDLE; - } - - _cogl_texture_bitmap_free (tex); - - return _cogl_texture_handle_new (tex); + return _cogl_texture_2d_sliced_new_from_bitmap (bmp_handle, + flags, + internal_format); } CoglHandle @@ -1306,19 +253,10 @@ cogl_texture_new_from_file (const gchar *filename, CoglPixelFormat internal_format, GError **error) { - CoglHandle bmp; - CoglHandle handle; - - g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE); - - bmp = cogl_bitmap_new_from_file (filename, error); - if (bmp == COGL_INVALID_HANDLE) - return COGL_INVALID_HANDLE; - - handle = cogl_texture_new_from_bitmap (bmp, flags, internal_format); - cogl_handle_unref (bmp); - - return handle; + return _cogl_texture_2d_sliced_new_from_file (filename, + flags, + internal_format, + error); } CoglHandle @@ -1330,143 +268,13 @@ cogl_texture_new_from_foreign (GLuint gl_handle, GLuint y_pot_waste, CoglPixelFormat format) { - /* NOTE: width, height and internal format are not queriable - in GLES, hence such a function prototype. However, for - OpenGL they are still queried from the texture for improved - robustness and for completeness in case one day GLES gains - support for them. - */ - - GLenum gl_error = 0; - GLboolean gl_istexture; - GLint gl_compressed = GL_FALSE; - GLint gl_int_format = 0; - GLint gl_width = 0; - GLint gl_height = 0; - GLint gl_gen_mipmap; - guint bpp; - CoglTexture *tex; - CoglTexSliceSpan x_span; - CoglTexSliceSpan y_span; - - if (!_cogl_texture_driver_allows_foreign_gl_target (gl_target)) - return COGL_INVALID_HANDLE; - - /* Make sure it is a valid GL texture object */ - gl_istexture = glIsTexture (gl_handle); - if (gl_istexture == GL_FALSE) - return COGL_INVALID_HANDLE; - - /* Make sure binding succeeds */ - gl_error = glGetError (); - glBindTexture (gl_target, gl_handle); - if (glGetError () != GL_NO_ERROR) - return COGL_INVALID_HANDLE; - - /* Obtain texture parameters - (only level 0 we are interested in) */ - -#if HAVE_COGL_GL - GE( glGetTexLevelParameteriv (gl_target, 0, - GL_TEXTURE_COMPRESSED, - &gl_compressed) ); - - GE( glGetTexLevelParameteriv (gl_target, 0, - GL_TEXTURE_INTERNAL_FORMAT, - &gl_int_format) ); - - - GE( glGetTexLevelParameteriv (gl_target, 0, - GL_TEXTURE_WIDTH, - &gl_width) ); - - GE( glGetTexLevelParameteriv (gl_target, 0, - GL_TEXTURE_HEIGHT, - &gl_height) ); -#else - gl_width = width + x_pot_waste; - gl_height = height + y_pot_waste; -#endif - - GE( glGetTexParameteriv (gl_target, - GL_GENERATE_MIPMAP, - &gl_gen_mipmap) ); - - /* Validate width and height */ - if (gl_width <= 0 || gl_height <= 0) - return COGL_INVALID_HANDLE; - - /* Validate pot waste */ - if (x_pot_waste < 0 || x_pot_waste >= gl_width || - y_pot_waste < 0 || y_pot_waste >= gl_height) - return COGL_INVALID_HANDLE; - - /* Compressed texture images not supported */ - if (gl_compressed == GL_TRUE) - return COGL_INVALID_HANDLE; - - /* Try and match to a cogl format */ - if (!_cogl_pixel_format_from_gl_internal (gl_int_format, &format)) - return COGL_INVALID_HANDLE; - - /* Create new texture */ - tex = (CoglTexture *) g_malloc (sizeof (CoglTexture)); - - /* Setup bitmap info */ - tex->is_foreign = TRUE; - tex->auto_mipmap = (gl_gen_mipmap == GL_TRUE) ? TRUE : FALSE; - tex->mipmaps_dirty = TRUE; - tex->first_pixels = NULL; - - bpp = _cogl_get_format_bpp (format); - tex->bitmap.format = format; - tex->bitmap.width = gl_width - x_pot_waste; - tex->bitmap.height = gl_height - y_pot_waste; - tex->bitmap.rowstride = tex->bitmap.width * bpp; - tex->bitmap_owner = FALSE; - - tex->gl_target = gl_target; - tex->gl_intformat = gl_int_format; - tex->gl_format = gl_int_format; - tex->gl_type = GL_UNSIGNED_BYTE; - - /* Unknown filter */ - tex->min_filter = GL_FALSE; - tex->mag_filter = GL_FALSE; - tex->max_waste = 0; - - /* Wrap mode not yet set */ - tex->wrap_mode = GL_FALSE; - - /* Create slice arrays */ - tex->slice_x_spans = - g_array_sized_new (FALSE, FALSE, - sizeof (CoglTexSliceSpan), 1); - - tex->slice_y_spans = - g_array_sized_new (FALSE, FALSE, - sizeof (CoglTexSliceSpan), 1); - - tex->slice_gl_handles = - g_array_sized_new (FALSE, FALSE, - sizeof (GLuint), 1); - - /* Store info for a single slice */ - x_span.start = 0; - x_span.size = gl_width; - x_span.waste = x_pot_waste; - g_array_append_val (tex->slice_x_spans, x_span); - - y_span.start = 0; - y_span.size = gl_height; - y_span.waste = y_pot_waste; - g_array_append_val (tex->slice_y_spans, y_span); - - g_array_append_val (tex->slice_gl_handles, gl_handle); - - tex->first_pixels = NULL; - - return _cogl_texture_handle_new (tex); + return _cogl_texture_2d_sliced_new_from_foreign (gl_handle, + gl_target, + width, + height, + x_pot_waste, + y_pot_waste, + format); } guint @@ -1477,7 +285,7 @@ cogl_texture_get_width (CoglHandle handle) if (!cogl_is_texture (handle)) return 0; - tex = _cogl_texture_pointer_from_handle (handle); + tex = COGL_TEXTURE (handle); return tex->bitmap.width; } @@ -1490,7 +298,7 @@ cogl_texture_get_height (CoglHandle handle) if (!cogl_is_texture (handle)) return 0; - tex = _cogl_texture_pointer_from_handle (handle); + tex = COGL_TEXTURE (handle); return tex->bitmap.height; } @@ -1503,7 +311,7 @@ cogl_texture_get_format (CoglHandle handle) if (!cogl_is_texture (handle)) return COGL_PIXEL_FORMAT_ANY; - tex = _cogl_texture_pointer_from_handle (handle); + tex = COGL_TEXTURE (handle); return tex->bitmap.format; } @@ -1516,7 +324,7 @@ cogl_texture_get_rowstride (CoglHandle handle) if (!cogl_is_texture (handle)) return 0; - tex = _cogl_texture_pointer_from_handle (handle); + tex = COGL_TEXTURE (handle); return tex->bitmap.rowstride; } @@ -1529,9 +337,15 @@ cogl_texture_get_max_waste (CoglHandle handle) if (!cogl_is_texture (handle)) return 0; - tex = _cogl_texture_pointer_from_handle (handle); + tex = COGL_TEXTURE (handle); - return tex->max_waste; + switch (tex->type) + { + case COGL_TEXTURE_TYPE_2D_SLICED: + return _cogl_texture_2d_sliced_get_max_waste (handle); + } + + g_return_val_if_reached (0); } gboolean @@ -1542,67 +356,103 @@ cogl_texture_is_sliced (CoglHandle handle) if (!cogl_is_texture (handle)) return FALSE; - tex = _cogl_texture_pointer_from_handle (handle); + tex = COGL_TEXTURE (handle); - if (tex->slice_gl_handles == NULL) - return FALSE; + switch (tex->type) + { + case COGL_TEXTURE_TYPE_2D_SLICED: + return _cogl_texture_2d_sliced_is_sliced (handle); + } - if (tex->slice_gl_handles->len <= 1) - return FALSE; - - return TRUE; + g_return_val_if_reached (FALSE); } +/* Some CoglTextures, notably sliced textures or atlas textures when repeating + * is used, will need to divide the coordinate space into multiple GL textures + * (or rather; in the case of atlases duplicate a single texture in multiple + * positions to handle repeating) + * + * This function helps you implement primitives using such textures by + * invoking a callback once for each sub texture that intersects a given + * region specified in texture coordinates. + */ +void +_cogl_texture_foreach_sub_texture_in_region (CoglHandle handle, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglTextureSliceCallback callback, + void *user_data) +{ + CoglTexture *tex = COGL_TEXTURE (handle); + + switch (tex->type) + { + case COGL_TEXTURE_TYPE_2D_SLICED: + { + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + _cogl_texture_2d_sliced_foreach_sub_texture_in_region (tex_2ds, + virtual_tx_1, + virtual_ty_1, + virtual_tx_2, + virtual_ty_2, + callback, + user_data); + break; + } + } +} + +/* If this returns FALSE, that implies _foreach_sub_texture_in_region + * will be needed to iterate over multiple sub textures for regions whos + * texture coordinates extend out of the range [0,1] + */ gboolean _cogl_texture_can_hardware_repeat (CoglHandle handle) { - CoglTexture *tex = _cogl_texture_pointer_from_handle (handle); - CoglTexSliceSpan *x_span; - CoglTexSliceSpan *y_span; + CoglTexture *tex = (CoglTexture *)handle; #if HAVE_COGL_GL + /* TODO: COGL_TEXTURE_TYPE_2D_RECTANGLE */ if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) return FALSE; #endif - x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); - y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + switch (tex->type) + { + case COGL_TEXTURE_TYPE_2D_SLICED: + { + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + return _cogl_texture_2d_sliced_can_hardware_repeat (tex_2ds); + } + } - return (x_span->waste || y_span->waste) ? FALSE : TRUE; + g_return_val_if_reached (FALSE); } +/* NB: You can't use this with textures comprised of multiple sub textures (use + * cogl_texture_is_sliced() to check) since coordinate transformation for such + * textures will be different for each slice. */ void _cogl_texture_transform_coords_to_gl (CoglHandle handle, float *s, float *t) { - CoglTexture *tex = _cogl_texture_pointer_from_handle (handle); - CoglTexSliceSpan *x_span; - CoglTexSliceSpan *y_span; + CoglTexture *tex = COGL_TEXTURE (handle); - g_assert (!cogl_texture_is_sliced (tex)); - - /* Don't include the waste in the texture coordinates */ - x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); - y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); - - *s *= tex->bitmap.width / (float)x_span->size; - *t *= tex->bitmap.height / (float)y_span->size; - -#if HAVE_COGL_GL - /* Denormalize texture coordinates for rectangle textures */ - if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + switch (tex->type) { - *s *= x_span->size; - *t *= y_span->size; + case COGL_TEXTURE_TYPE_2D_SLICED: + return _cogl_texture_2d_sliced_transform_coords_to_gl ( + COGL_TEXTURE_2D_SLICED (tex), s, t); } -#endif } GLenum _cogl_texture_get_internal_gl_format (CoglHandle handle) { - CoglTexture *tex = _cogl_texture_pointer_from_handle (handle); + CoglTexture *tex = COGL_TEXTURE (handle); return tex->gl_intformat; } @@ -1617,21 +467,17 @@ cogl_texture_get_gl_texture (CoglHandle handle, if (!cogl_is_texture (handle)) return FALSE; - tex = _cogl_texture_pointer_from_handle (handle); + tex = COGL_TEXTURE (handle); - if (tex->slice_gl_handles == NULL) - return FALSE; + switch (tex->type) + { + case COGL_TEXTURE_TYPE_2D_SLICED: + return _cogl_texture_2d_sliced_get_gl_texture (handle, + out_gl_handle, + out_gl_target); + } - if (tex->slice_gl_handles->len < 1) - return FALSE; - - if (out_gl_handle != NULL) - *out_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); - - if (out_gl_target != NULL) - *out_gl_target = tex->gl_target; - - return TRUE; + g_return_val_if_reached (FALSE); } void @@ -1640,35 +486,17 @@ _cogl_texture_set_filters (CoglHandle handle, GLenum mag_filter) { CoglTexture *tex; - GLuint gl_handle; - int i; if (!cogl_is_texture (handle)) return; - tex = _cogl_texture_pointer_from_handle (handle); + tex = COGL_TEXTURE (handle); - /* Make sure slices were created */ - if (tex->slice_gl_handles == NULL) - return; - - if (min_filter == tex->min_filter - && mag_filter == tex->mag_filter) - return; - - /* Store new values */ - tex->min_filter = min_filter; - tex->mag_filter = mag_filter; - - /* Apply new filters to every slice */ - for (i=0; islice_gl_handles->len; ++i) + switch (tex->type) { - gl_handle = g_array_index (tex->slice_gl_handles, GLuint, i); - GE( glBindTexture (tex->gl_target, gl_handle) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, - tex->mag_filter) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, - tex->min_filter) ); + case COGL_TEXTURE_TYPE_2D_SLICED: + _cogl_texture_2d_sliced_set_filters (handle, min_filter, mag_filter); + break; } } @@ -1676,46 +504,18 @@ void _cogl_texture_ensure_mipmaps (CoglHandle handle) { CoglTexture *tex; - int i; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (!cogl_is_texture (handle)) return; - tex = _cogl_texture_pointer_from_handle (handle); + tex = COGL_TEXTURE (handle); - /* Only update if the mipmaps are dirty */ - if (!tex->auto_mipmap || !tex->mipmaps_dirty) - return; - - /* Make sure slices were created */ - if (tex->slice_gl_handles == NULL) - return; - - /* Regenerate the mipmaps on every slice */ - for (i = 0; i < tex->slice_gl_handles->len; i++) + switch (tex->type) { - GLuint gl_handle = g_array_index (tex->slice_gl_handles, GLuint, i); - GE( glBindTexture (tex->gl_target, gl_handle) ); - - /* glGenerateMipmap is defined in the FBO extension */ - if (cogl_features_available (COGL_FEATURE_OFFSCREEN)) - _cogl_texture_driver_gl_generate_mipmaps (tex->gl_target); - else if (tex->first_pixels) - { - CoglTexturePixel *pixel = tex->first_pixels + i; - /* Temporarily enable automatic mipmap generation and - re-upload the first pixel to cause a regeneration */ - GE( glTexParameteri (tex->gl_target, GL_GENERATE_MIPMAP, GL_TRUE) ); - GE( glTexSubImage2D (tex->gl_target, 0, 0, 0, 1, 1, - pixel->gl_format, pixel->gl_type, - pixel->data) ); - GE( glTexParameteri (tex->gl_target, GL_GENERATE_MIPMAP, GL_FALSE) ); - } + case COGL_TEXTURE_TYPE_2D_SLICED: + _cogl_texture_2d_sliced_ensure_mipmaps (handle); + break; } - - tex->mipmaps_dirty = FALSE; } gboolean @@ -1732,72 +532,260 @@ cogl_texture_set_region (CoglHandle handle, guint rowstride, const guchar *data) { - CoglTexture *tex; - gint bpp; - CoglBitmap source_bmp; - CoglBitmap temp_bmp; - gboolean source_bmp_owner = FALSE; - CoglPixelFormat closest_format; - GLenum closest_gl_format; - GLenum closest_gl_type; - gboolean success; + CoglTexture *tex; - /* Check if valid texture handle */ if (!cogl_is_texture (handle)) return FALSE; - tex = _cogl_texture_pointer_from_handle (handle); + tex = COGL_TEXTURE (handle); - /* Check for valid format */ - if (format == COGL_PIXEL_FORMAT_ANY) - return FALSE; - - /* Shortcut out early if the image is empty */ - if (width == 0 || height == 0) - return TRUE; - - /* Init source bitmap */ - source_bmp.width = width; - source_bmp.height = height; - source_bmp.format = format; - source_bmp.data = (guchar*)data; - - /* Rowstride from width if none specified */ - bpp = _cogl_get_format_bpp (format); - source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride; - - /* Find closest format to internal that's supported by GL */ - closest_format = _cogl_pixel_format_to_gl (tex->bitmap.format, - NULL, /* don't need */ - &closest_gl_format, - &closest_gl_type); - - /* If no direct match, convert */ - if (closest_format != format) + switch (tex->type) { - /* Convert to required format */ - success = _cogl_bitmap_convert_and_premult (&source_bmp, - &temp_bmp, - closest_format); - - /* Swap bitmaps if succeeded */ - if (!success) return FALSE; - source_bmp = temp_bmp; - source_bmp_owner = TRUE; + case COGL_TEXTURE_TYPE_2D_SLICED: + return _cogl_texture_2d_sliced_set_region (handle, + src_x, + src_y, + dst_x, + dst_y, + dst_width, + dst_height, + width, + height, + format, + rowstride, + data); } - /* Send data to GL */ - _cogl_texture_upload_subregion_to_gl (tex, - src_x, src_y, - dst_x, dst_y, - dst_width, dst_height, - &source_bmp, - closest_gl_format, - closest_gl_type); + g_return_val_if_reached (FALSE); +} - /* Free data if owner */ - if (source_bmp_owner) - g_free (source_bmp.data); +/* Reads back the contents of a texture by rendering it to the framebuffer + * and reading back the resulting pixels. + * + * It will perform multiple renders if the texture is larger than the + * current glViewport. + * + * It assumes the projection and modelview have already been setup so + * that rendering to 0,0 with the same width and height of the viewport + * will exactly cover the viewport. + * + * NB: Normally this approach isn't normally used since we can just use + * glGetTexImage, but may be used as a fallback in some circumstances. + */ +static void +do_texture_draw_and_read (CoglTexture *tex, + CoglBitmap *target_bmp, + GLint *viewport) +{ + gint bpp; + float rx1, ry1; + float rx2, ry2; + float tx1, ty1; + float tx2, ty2; + int bw, bh; + CoglBitmap rect_bmp; + CoglHandle handle; + + handle = (CoglHandle) tex; + bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); + + ry1 = 0; ry2 = 0; + ty1 = 0; ty2 = 0; + + /* Walk Y axis until whole bitmap height consumed */ + for (bh = tex->bitmap.height; bh > 0; bh -= viewport[3]) + { + /* Rectangle Y coords */ + ry1 = ry2; + ry2 += (bh < viewport[3]) ? bh : viewport[3]; + + /* Normalized texture Y coords */ + ty1 = ty2; + ty2 = (ry2 / (float)tex->bitmap.height); + + rx1 = 0; rx2 = 0; + tx1 = 0; tx2 = 0; + + /* Walk X axis until whole bitmap width consumed */ + for (bw = tex->bitmap.width; bw > 0; bw-=viewport[2]) + { + /* Rectangle X coords */ + rx1 = rx2; + rx2 += (bw < viewport[2]) ? bw : viewport[2]; + + /* Normalized texture X coords */ + tx1 = tx2; + tx2 = (rx2 / (float)tex->bitmap.width); + + /* Draw a portion of texture */ + cogl_rectangle_with_texture_coords (0, 0, + rx2 - rx1, + ry2 - ry1, + tx1, ty1, + tx2, ty2); + + /* Read into a temporary bitmap */ + rect_bmp.format = COGL_PIXEL_FORMAT_RGBA_8888; + rect_bmp.width = rx2 - rx1; + rect_bmp.height = ry2 - ry1; + rect_bmp.rowstride = bpp * rect_bmp.width; + rect_bmp.data = (guchar*) g_malloc (rect_bmp.rowstride * + rect_bmp.height); + + _cogl_texture_driver_prep_gl_for_pixels_download (rect_bmp.rowstride, + bpp); + GE( glReadPixels (viewport[0], viewport[1], + rect_bmp.width, + rect_bmp.height, + GL_RGBA, GL_UNSIGNED_BYTE, + rect_bmp.data) ); + + /* Copy to target bitmap */ + _cogl_bitmap_copy_subregion (&rect_bmp, + target_bmp, + 0,0, + rx1,ry1, + rect_bmp.width, + rect_bmp.height); + + /* Free temp bitmap */ + g_free (rect_bmp.data); + } + } +} + +/* Reads back the contents of a texture by rendering it to the framebuffer + * and reading back the resulting pixels. + * + * NB: Normally this approach isn't normally used since we can just use + * glGetTexImage, but may be used as a fallback in some circumstances. + */ +gboolean +_cogl_texture_draw_and_read (CoglTexture *tex, + CoglBitmap *target_bmp, + GLuint target_gl_format, + GLuint target_gl_type) +{ + gint bpp; + GLint viewport[4]; + CoglBitmap alpha_bmp; + CoglHandle prev_source; + + _COGL_GET_CONTEXT (ctx, FALSE); + + bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); + + /* Viewport needs to have some size and be inside the window for this */ + GE( glGetIntegerv (GL_VIEWPORT, viewport)); + if (viewport[0] < 0 || viewport[1] < 0 || + viewport[2] <= 0 || viewport[3] <= 0) + return FALSE; + + /* Setup orthographic projection into current viewport (0,0 in bottom-left + * corner to draw the texture upside-down so we match the way glReadPixels + * works) + */ + + _cogl_set_current_matrix (COGL_MATRIX_PROJECTION); + _cogl_current_matrix_push (); + _cogl_current_matrix_identity (); + + _cogl_current_matrix_ortho (0, (float)(viewport[2]), + 0, (float)(viewport[3]), + (float)(0), + (float)(100)); + + _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); + _cogl_current_matrix_push (); + _cogl_current_matrix_identity (); + + /* Direct copy operation */ + + if (ctx->texture_download_material == COGL_INVALID_HANDLE) + { + ctx->texture_download_material = cogl_material_new (); + cogl_material_set_blend (ctx->texture_download_material, + "RGBA = ADD (SRC_COLOR, 0)", + NULL); + } + + prev_source = cogl_handle_ref (ctx->source_material); + cogl_set_source (ctx->texture_download_material); + + cogl_material_set_layer (ctx->texture_download_material, 0, tex); + + cogl_material_set_layer_combine (ctx->texture_download_material, + 0, /* layer */ + "RGBA = REPLACE (TEXTURE)", + NULL); + + do_texture_draw_and_read (tex, target_bmp, viewport); + + /* Check whether texture has alpha and framebuffer not */ + /* FIXME: For some reason even if ALPHA_BITS is 8, the framebuffer + still doesn't seem to have an alpha buffer. This might be just + a PowerVR issue. + GLint r_bits, g_bits, b_bits, a_bits; + GE( glGetIntegerv (GL_ALPHA_BITS, &a_bits) ); + GE( glGetIntegerv (GL_RED_BITS, &r_bits) ); + GE( glGetIntegerv (GL_GREEN_BITS, &g_bits) ); + GE( glGetIntegerv (GL_BLUE_BITS, &b_bits) ); + printf ("R bits: %d\n", r_bits); + printf ("G bits: %d\n", g_bits); + printf ("B bits: %d\n", b_bits); + printf ("A bits: %d\n", a_bits); */ + if ((tex->bitmap.format & COGL_A_BIT)/* && a_bits == 0*/) + { + guchar *srcdata; + guchar *dstdata; + guchar *srcpixel; + guchar *dstpixel; + gint x,y; + + /* Create temp bitmap for alpha values */ + alpha_bmp.format = COGL_PIXEL_FORMAT_RGBA_8888; + alpha_bmp.width = target_bmp->width; + alpha_bmp.height = target_bmp->height; + alpha_bmp.rowstride = bpp * alpha_bmp.width; + alpha_bmp.data = (guchar*) g_malloc (alpha_bmp.rowstride * + alpha_bmp.height); + + /* Draw alpha values into RGB channels */ + cogl_material_set_layer_combine (ctx->texture_download_material, + 0, /* layer */ + "RGBA = REPLACE (TEXTURE[A])", + NULL); + + do_texture_draw_and_read (tex, &alpha_bmp, viewport); + + /* Copy temp R to target A */ + srcdata = alpha_bmp.data; + dstdata = target_bmp->data; + + for (y=0; yheight; ++y) + { + for (x=0; xwidth; ++x) + { + srcpixel = srcdata + x*bpp; + dstpixel = dstdata + x*bpp; + dstpixel[3] = srcpixel[0]; + } + srcdata += alpha_bmp.rowstride; + dstdata += target_bmp->rowstride; + } + + g_free (alpha_bmp.data); + } + + /* Restore old state */ + _cogl_set_current_matrix (COGL_MATRIX_PROJECTION); + _cogl_current_matrix_pop (); + _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); + _cogl_current_matrix_pop (); + + /* restore the original material */ + cogl_set_source (prev_source); + cogl_handle_unref (prev_source); return TRUE; } @@ -1808,92 +796,22 @@ cogl_texture_get_data (CoglHandle handle, guint rowstride, guchar *data) { - CoglTexture *tex; - gint bpp; - gint byte_size; - CoglPixelFormat closest_format; - gint closest_bpp; - GLenum closest_gl_format; - GLenum closest_gl_type; - CoglBitmap target_bmp; - CoglBitmap new_bmp; - gboolean success; - guchar *src; - guchar *dst; - gint y; + CoglTexture *tex; - /* Check if valid texture handle */ if (!cogl_is_texture (handle)) - return 0; + return FALSE; - tex = _cogl_texture_pointer_from_handle (handle); + tex = COGL_TEXTURE (handle); - /* Default to internal format if none specified */ - if (format == COGL_PIXEL_FORMAT_ANY) - format = tex->bitmap.format; - - /* Rowstride from texture width if none specified */ - bpp = _cogl_get_format_bpp (format); - if (rowstride == 0) rowstride = tex->bitmap.width * bpp; - - /* Return byte size if only that requested */ - byte_size = tex->bitmap.height * rowstride; - if (data == NULL) return byte_size; - - closest_format = - _cogl_texture_driver_find_best_gl_get_data_format (format, - &closest_gl_format, - &closest_gl_type); - closest_bpp = _cogl_get_format_bpp (closest_format); - - /* Is the requested format supported? */ - if (closest_format == format) + switch (tex->type) { - /* Target user data directly */ - target_bmp = tex->bitmap; - target_bmp.format = format; - target_bmp.rowstride = rowstride; - target_bmp.data = data; - } - else - { - /* Target intermediate buffer */ - target_bmp = tex->bitmap; - target_bmp.format = closest_format; - target_bmp.rowstride = target_bmp.width * closest_bpp; - target_bmp.data = (guchar*) g_malloc (target_bmp.height - * target_bmp.rowstride); + case COGL_TEXTURE_TYPE_2D_SLICED: + return _cogl_texture_2d_sliced_get_data (handle, + format, + rowstride, + data); } - /* Retrieve data from slices */ - _cogl_texture_driver_download_from_gl (tex, &target_bmp, - closest_gl_format, - closest_gl_type); - - /* Was intermediate used? */ - if (closest_format != format) - { - /* Convert to requested format */ - success = _cogl_bitmap_convert_and_premult (&target_bmp, - &new_bmp, - format); - - /* Free intermediate data and return if failed */ - g_free (target_bmp.data); - if (!success) return 0; - - /* Copy to user buffer */ - for (y = 0; y < new_bmp.height; ++y) - { - src = new_bmp.data + y * new_bmp.rowstride; - dst = data + y * rowstride; - memcpy (dst, src, new_bmp.width); - } - - /* Free converted data */ - g_free (new_bmp.data); - } - - return byte_size; + g_return_val_if_reached (0); } diff --git a/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c b/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c index b5a0f4284..e4c868dbd 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c +++ b/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c @@ -135,95 +135,16 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, } gboolean -_cogl_texture_driver_download_from_gl (CoglTexture *tex, - CoglBitmap *target_bmp, - GLuint target_gl_format, - GLuint target_gl_type) +_cogl_texture_driver_gl_get_tex_image (GLenum gl_target, + GLenum dest_gl_format, + GLenum dest_gl_type, + guint8 *dest) { - CoglTexSliceSpan *x_span; - CoglTexSliceSpan *y_span; - GLuint gl_handle; - gint bpp; - gint x,y; - CoglBitmap slice_bmp; - - bpp = _cogl_get_format_bpp (target_bmp->format); - - /* Iterate vertical slices */ - for (y = 0; y < tex->slice_y_spans->len; ++y) - { - y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); - - /* Iterate horizontal slices */ - for (x = 0; x < tex->slice_x_spans->len; ++x) - { - /*if (x != 0 || y != 1) continue;*/ - x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); - - /* Pick the gl texture object handle */ - gl_handle = g_array_index (tex->slice_gl_handles, GLuint, - y * tex->slice_x_spans->len + x); - - /* If there's any waste we need to copy manually - (no glGetTexSubImage) */ - - if (y_span->waste != 0 || x_span->waste != 0) - { - /* Setup temp bitmap for slice subregion */ - slice_bmp.format = target_bmp->format; - slice_bmp.width = x_span->size; - slice_bmp.height = y_span->size; - slice_bmp.rowstride = bpp * slice_bmp.width; - slice_bmp.data = (guchar*) g_malloc (slice_bmp.rowstride * - slice_bmp.height); - - /* Setup gl alignment to 0,0 top-left corner */ - _cogl_texture_driver_prep_gl_for_pixels_download ( - slice_bmp.rowstride, - bpp); - - /* Download slice image data into temp bmp */ - GE( glBindTexture (tex->gl_target, gl_handle) ); - - GE (glGetTexImage (tex->gl_target, - 0, /* level */ - target_gl_format, - target_gl_type, - slice_bmp.data) ); - - /* Copy portion of slice from temp to target bmp */ - _cogl_bitmap_copy_subregion (&slice_bmp, - target_bmp, - 0, 0, - x_span->start, - y_span->start, - x_span->size - x_span->waste, - y_span->size - y_span->waste); - /* Free temp bitmap */ - g_free (slice_bmp.data); - } - else - { - GLvoid *dst = target_bmp->data - + x_span->start * bpp - + y_span->start * target_bmp->rowstride; - - _cogl_texture_driver_prep_gl_for_pixels_download ( - target_bmp->rowstride, - bpp); - - /* Download slice image data */ - GE( glBindTexture (tex->gl_target, gl_handle) ); - - GE( glGetTexImage (tex->gl_target, - 0, /* level */ - target_gl_format, - target_gl_type, - dst) ); - } - } - } - + GE (glGetTexImage (gl_target, + 0, /* level */ + dest_gl_format, + dest_gl_type, + (GLvoid *)dest)); return TRUE; } diff --git a/clutter/cogl/cogl/driver/gles/cogl-context-driver.c b/clutter/cogl/cogl/driver/gles/cogl-context-driver.c index ee38b9b94..6660db1f3 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-context-driver.c +++ b/clutter/cogl/cogl/driver/gles/cogl-context-driver.c @@ -31,8 +31,6 @@ void _cogl_create_context_driver (CoglContext *context) { - context->drv.texture_download_material = COGL_INVALID_HANDLE; - /* Init the GLES2 wrapper */ #ifdef HAVE_COGL_GLES2 cogl_gles2_wrapper_init (&context->drv.gles2); diff --git a/clutter/cogl/cogl/driver/gles/cogl-context-driver.h b/clutter/cogl/cogl/driver/gles/cogl-context-driver.h index 06f0ee006..9b5996e74 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-context-driver.h +++ b/clutter/cogl/cogl/driver/gles/cogl-context-driver.h @@ -29,8 +29,6 @@ typedef struct _CoglContextDriver { - CoglHandle texture_download_material; - #ifdef HAVE_COGL_GLES2 CoglGles2Wrapper gles2; diff --git a/clutter/cogl/cogl/driver/gles/cogl-texture-driver.c b/clutter/cogl/cogl/driver/gles/cogl-texture-driver.c index 1d50a1984..0461ae8da 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-texture-driver.c +++ b/clutter/cogl/cogl/driver/gles/cogl-texture-driver.c @@ -128,217 +128,16 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, g_free (slice_bmp.data); } -static void -_cogl_texture_draw_and_read (CoglTexture *tex, - CoglBitmap *target_bmp, - GLint *viewport) -{ - gint bpp; - float rx1, ry1; - float rx2, ry2; - float tx1, ty1; - float tx2, ty2; - int bw, bh; - CoglBitmap rect_bmp; - CoglHandle handle; - - handle = (CoglHandle) tex; - bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); - - ry1 = 0; ry2 = 0; - ty1 = 0; ty2 = 0; - - /* Walk Y axis until whole bitmap height consumed */ - for (bh = tex->bitmap.height; bh > 0; bh -= viewport[3]) - { - /* Rectangle Y coords */ - ry1 = ry2; - ry2 += (bh < viewport[3]) ? bh : viewport[3]; - - /* Normalized texture Y coords */ - ty1 = ty2; - ty2 = (ry2 / (float)tex->bitmap.height); - - rx1 = 0; rx2 = 0; - tx1 = 0; tx2 = 0; - - /* Walk X axis until whole bitmap width consumed */ - for (bw = tex->bitmap.width; bw > 0; bw-=viewport[2]) - { - /* Rectangle X coords */ - rx1 = rx2; - rx2 += (bw < viewport[2]) ? bw : viewport[2]; - - /* Normalized texture X coords */ - tx1 = tx2; - tx2 = (rx2 / (float)tex->bitmap.width); - - /* Draw a portion of texture */ - cogl_rectangle_with_texture_coords (0, 0, - rx2 - rx1, - ry2 - ry1, - tx1, ty1, - tx2, ty2); - - /* Read into a temporary bitmap */ - rect_bmp.format = COGL_PIXEL_FORMAT_RGBA_8888; - rect_bmp.width = rx2 - rx1; - rect_bmp.height = ry2 - ry1; - rect_bmp.rowstride = bpp * rect_bmp.width; - rect_bmp.data = (guchar*) g_malloc (rect_bmp.rowstride * - rect_bmp.height); - - _cogl_texture_driver_prep_gl_for_pixels_download (rect_bmp.rowstride, - bpp); - GE( glReadPixels (viewport[0], viewport[1], - rect_bmp.width, - rect_bmp.height, - GL_RGBA, GL_UNSIGNED_BYTE, - rect_bmp.data) ); - - /* Copy to target bitmap */ - _cogl_bitmap_copy_subregion (&rect_bmp, - target_bmp, - 0,0, - rx1,ry1, - rect_bmp.width, - rect_bmp.height); - - /* Free temp bitmap */ - g_free (rect_bmp.data); - } - } -} - +/* NB: GLES doesn't support glGetTexImage2D, so cogl-texture will instead + * fallback to a generic render + readpixels approach to downloading + * texture data. (See _cogl_texture_draw_and_read() ) */ gboolean -_cogl_texture_driver_download_from_gl (CoglTexture *tex, - CoglBitmap *target_bmp, - GLuint target_gl_format, - GLuint target_gl_type) +_cogl_texture_driver_gl_get_tex_image (GLenum gl_target, + GLenum dest_gl_format, + GLenum dest_gl_type, + guint8 *dest) { - gint bpp; - GLint viewport[4]; - CoglBitmap alpha_bmp; - CoglHandle prev_source; - - _COGL_GET_CONTEXT (ctx, FALSE); - - - bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); - - /* Viewport needs to have some size and be inside the window for this */ - GE( glGetIntegerv (GL_VIEWPORT, viewport) ); - - if (viewport[0] < 0 || viewport[1] < 0 || - viewport[2] <= 0 || viewport[3] <= 0) - return FALSE; - - /* Setup orthographic projection into current viewport - (0,0 in bottom-left corner to draw the texture - upside-down so we match the way glReadPixels works) */ - - _cogl_set_current_matrix (COGL_MATRIX_PROJECTION); - _cogl_current_matrix_push (); - _cogl_current_matrix_identity (); - - _cogl_current_matrix_ortho (0, (float)(viewport[2]), - 0, (float)(viewport[3]), - (float)(0), - (float)(100)); - - _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); - _cogl_current_matrix_push (); - _cogl_current_matrix_identity (); - - /* Direct copy operation */ - - if (ctx->drv.texture_download_material == COGL_INVALID_HANDLE) - { - ctx->drv.texture_download_material = cogl_material_new (); - cogl_material_set_blend (ctx->drv.texture_download_material, - "RGBA = ADD (SRC_COLOR, 0)", - NULL); - } - - prev_source = cogl_handle_ref (ctx->source_material); - cogl_set_source (ctx->drv.texture_download_material); - - cogl_material_set_layer (ctx->drv.texture_download_material, 0, tex); - - cogl_material_set_layer_combine (ctx->drv.texture_download_material, - 0, /* layer */ - "RGBA = REPLACE (TEXTURE)", - NULL); - - _cogl_texture_draw_and_read (tex, target_bmp, viewport); - - /* Check whether texture has alpha and framebuffer not */ - /* FIXME: For some reason even if ALPHA_BITS is 8, the framebuffer - still doesn't seem to have an alpha buffer. This might be just - a PowerVR issue. - GLint r_bits, g_bits, b_bits, a_bits; - GE( glGetIntegerv (GL_ALPHA_BITS, &a_bits) ); - GE( glGetIntegerv (GL_RED_BITS, &r_bits) ); - GE( glGetIntegerv (GL_GREEN_BITS, &g_bits) ); - GE( glGetIntegerv (GL_BLUE_BITS, &b_bits) ); - printf ("R bits: %d\n", r_bits); - printf ("G bits: %d\n", g_bits); - printf ("B bits: %d\n", b_bits); - printf ("A bits: %d\n", a_bits); */ - if ((tex->bitmap.format & COGL_A_BIT)/* && a_bits == 0*/) - { - guchar *srcdata; - guchar *dstdata; - guchar *srcpixel; - guchar *dstpixel; - gint x,y; - - /* Create temp bitmap for alpha values */ - alpha_bmp.format = COGL_PIXEL_FORMAT_RGBA_8888; - alpha_bmp.width = target_bmp->width; - alpha_bmp.height = target_bmp->height; - alpha_bmp.rowstride = bpp * alpha_bmp.width; - alpha_bmp.data = (guchar*) g_malloc (alpha_bmp.rowstride * - alpha_bmp.height); - - /* Draw alpha values into RGB channels */ - cogl_material_set_layer_combine (ctx->drv.texture_download_material, - 0, /* layer */ - "RGBA = REPLACE (TEXTURE[A])", - NULL); - - _cogl_texture_draw_and_read (tex, &alpha_bmp, viewport); - - /* Copy temp R to target A */ - srcdata = alpha_bmp.data; - dstdata = target_bmp->data; - - for (y=0; yheight; ++y) - { - for (x=0; xwidth; ++x) - { - srcpixel = srcdata + x*bpp; - dstpixel = dstdata + x*bpp; - dstpixel[3] = srcpixel[0]; - } - srcdata += alpha_bmp.rowstride; - dstdata += target_bmp->rowstride; - } - - g_free (alpha_bmp.data); - } - - /* Restore old state */ - _cogl_set_current_matrix (COGL_MATRIX_PROJECTION); - _cogl_current_matrix_pop (); - _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); - _cogl_current_matrix_pop (); - - /* restore the original material */ - cogl_set_source (prev_source); - cogl_handle_unref (prev_source); - - return TRUE; + return FALSE; } gboolean