From 5030356e0e2a216df803643c00fb18e504fa765b Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 26 Nov 2009 17:32:52 +0000 Subject: [PATCH 01/33] cogl: Move data only used for upload out of CoglTexture The CoglTexture struct previously contained some fields which are only used to upload data such as the CoglBitmap and the source GL format. These are now moved to a separate CoglTextureUploadData struct which only exists for the duration of one of the cogl_texture_*_new functions. In cogl-texture there are utility functions which operate on this new struct rather than on CoglTexture directly. Some of the fields that were previously stored in the CoglBitmap struct are now copied to the CoglTexture such as the width, height, format and internal GL format. The rowstride was previously stored in CoglTexture and this was publicly accessible with the cogl_texture_get_rowstride function. However this doesn't seem to be a useful function because there is no need to use the same rowstride again when uploading or downloading new data. Instead cogl_texture_get_rowstride now just calculates a suitable rowstride from the format and width of the texture. --- cogl/cogl-texture-2d-sliced.c | 344 ++++++++++++------------- cogl/cogl-texture-driver.h | 6 +- cogl/cogl-texture-private.h | 51 ++-- cogl/cogl-texture.c | 93 +++---- cogl/driver/gl/cogl-texture-driver.c | 14 +- cogl/driver/gles/cogl-texture-driver.c | 16 +- 6 files changed, 262 insertions(+), 262 deletions(-) diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c index 56a4fdbea..b91e60b87 100644 --- a/cogl/cogl-texture-2d-sliced.c +++ b/cogl/cogl-texture-2d-sliced.c @@ -71,8 +71,8 @@ _cogl_texture_2d_sliced_foreach_sub_texture_in_region ( void *user_data) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); - float width = tex->bitmap.width; - float height = tex->bitmap.height; + float width = tex->width; + float height = tex->height; CoglSpanIter iter_x; CoglSpanIter iter_y; @@ -182,12 +182,12 @@ _cogl_texture_2d_sliced_foreach_sub_texture_in_region ( } static guchar * -_cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds) +_cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds, + CoglPixelFormat format) { CoglSpan *last_x_span; CoglSpan *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 */ @@ -197,7 +197,7 @@ _cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds) 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); + gint bpp = _cogl_get_format_bpp (format); CoglSpan *first_x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); CoglSpan *first_y_span @@ -212,7 +212,8 @@ _cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds) } static gboolean -_cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) +_cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, + CoglTextureUploadData *upload_data) { CoglSpan *x_span; CoglSpan *y_span; @@ -222,9 +223,11 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) guchar *waste_buf; CoglTexture *tex = COGL_TEXTURE (tex_2ds); - bpp = _cogl_get_format_bpp (tex->bitmap.format); + bpp = _cogl_get_format_bpp (upload_data->bitmap.format); - waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds); + waste_buf = + _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds, + upload_data->bitmap.format); /* Iterate vertical slices */ for (y = 0; y < tex_2ds->slice_y_spans->len; ++y) @@ -242,34 +245,35 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, slice_num); _cogl_texture_driver_upload_subregion_to_gl ( - tex, + tex->gl_target, + gl_handle, 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); + &upload_data->bitmap, + upload_data->gl_format, + upload_data->gl_type); /* 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, + upload_data->bitmap.data + x_span->start * bpp + + y_span->start * upload_data->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; + tex_2ds->first_pixels[slice_num].gl_format = + upload_data->gl_format; + tex_2ds->first_pixels[slice_num].gl_type = upload_data->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 + const guchar *src = upload_data->bitmap.data + + y_span->start * upload_data->bitmap.rowstride + (x_span->start + x_span->size - x_span->waste - 1) * bpp; guchar *dst = waste_buf; guint wx, wy; @@ -281,7 +285,7 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) memcpy (dst, src, bpp); dst += bpp; } - src += tex->bitmap.rowstride; + src += upload_data->bitmap.rowstride; } _cogl_texture_driver_prep_gl_for_pixels_upload ( @@ -293,15 +297,15 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) 0, x_span->waste, y_span->size - y_span->waste, - tex->gl_format, tex->gl_type, + upload_data->gl_format, upload_data->gl_type, waste_buf) ); } if (y_span->waste > 0) { - const guchar *src = tex->bitmap.data + const guchar *src = upload_data->bitmap.data + ((y_span->start + y_span->size - y_span->waste - 1) - * tex->bitmap.rowstride) + * upload_data->bitmap.rowstride) + x_span->start * bpp; guchar *dst = waste_buf; guint wy, wx; @@ -327,7 +331,7 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) y_span->size - y_span->waste, x_span->size, y_span->waste, - tex->gl_format, tex->gl_type, + upload_data->gl_format, upload_data->gl_type, waste_buf) ); } } @@ -367,13 +371,14 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, bpp = _cogl_get_format_bpp (source_bmp->format); - waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds); + waste_buf = + _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds, source_bmp->format); /* Iterate vertical spans */ for (source_y = src_y, _cogl_span_iter_begin (&y_iter, tex_2ds->slice_y_spans, - tex->bitmap.height, + tex->height, dst_y, dst_y + height); @@ -396,7 +401,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, for (source_x = src_x, _cogl_span_iter_begin (&x_iter, tex_2ds->slice_x_spans, - tex->bitmap.width, + tex->width, dst_x, dst_x + width); @@ -430,7 +435,8 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, /* 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, + _cogl_texture_driver_upload_subregion_to_gl (tex->gl_target, + gl_handle, source_x, source_y, local_x, /* dst x */ @@ -439,8 +445,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, inter_h, /* height */ source_bmp, source_gl_format, - source_gl_type, - gl_handle); + source_gl_type); /* Keep a copy of the first pixel if needed */ if (tex_2ds->first_pixels && local_x == 0 && local_y == 0) @@ -681,10 +686,10 @@ _cogl_texture_2d_sliced_set_wrap_mode_parameter (CoglTexture *tex, } static gboolean -_cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) +_cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, + const CoglTextureUploadData *upload_data) { CoglTexture *tex = COGL_TEXTURE (tex_2ds); - gint bpp; gint max_width; gint max_height; GLuint *gl_handles; @@ -698,20 +703,18 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) 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; + max_width = upload_data->bitmap.width; + max_height = upload_data->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); + max_width = cogl_util_next_p2 (upload_data->bitmap.width); + max_height = cogl_util_next_p2 (upload_data->bitmap.height); tex->gl_target = GL_TEXTURE_2D; slices_for_size = _cogl_pot_slices_for_size; } @@ -723,8 +726,8 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) /* Check if size supported else bail out */ if (!_cogl_texture_driver_size_supported (tex->gl_target, - tex->gl_format, - tex->gl_type, + upload_data->gl_intformat, + upload_data->gl_type, max_width, max_height)) { @@ -746,19 +749,19 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) /* Add a single span for width and height */ span.start = 0; span.size = max_width; - span.waste = max_width - tex->bitmap.width; + span.waste = max_width - upload_data->bitmap.width; g_array_append_val (tex_2ds->slice_x_spans, span); span.size = max_height; - span.waste = max_height - tex->bitmap.height; + span.waste = max_height - upload_data->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, + upload_data->gl_intformat, + upload_data->gl_type, max_width, max_height)) { @@ -773,11 +776,11 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) } /* Determine the slices required to cover the bitmap area */ - n_x_slices = slices_for_size (tex->bitmap.width, + n_x_slices = slices_for_size (upload_data->bitmap.width, max_width, tex_2ds->max_waste, NULL); - n_y_slices = slices_for_size (tex->bitmap.height, + n_y_slices = slices_for_size (upload_data->bitmap.height, max_height, tex_2ds->max_waste, NULL); @@ -791,11 +794,11 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) n_y_slices); /* Fill span arrays with info */ - slices_for_size (tex->bitmap.width, + slices_for_size (upload_data->bitmap.width, max_width, tex_2ds->max_waste, tex_2ds->slice_x_spans); - slices_for_size (tex->bitmap.height, + slices_for_size (upload_data->bitmap.height, max_height, tex_2ds->max_waste, tex_2ds->slice_y_spans); } @@ -845,15 +848,15 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) /* Setup texture parameters */ GE( _cogl_texture_driver_bind (tex->gl_target, gl_handles[y * n_x_slices + x], - tex->gl_intformat) ); + upload_data->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, + GE( glTexImage2D (tex->gl_target, 0, upload_data->gl_intformat, x_span->size, y_span->size, 0, - tex->gl_format, tex->gl_type, 0) ); + upload_data->gl_format, upload_data->gl_type, 0) ); } } @@ -889,77 +892,108 @@ _cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *tex_2ds) 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); } +static gboolean +_cogl_texture_2d_sliced_upload_from_data + (CoglTexture2DSliced *tex_2ds, + CoglTextureUploadData *upload_data, + CoglPixelFormat internal_format) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + + tex->vtable = &cogl_texture_2d_sliced_vtable; + + tex->is_foreign = FALSE; + tex->auto_mipmap = FALSE; + tex->mipmaps_dirty = TRUE; + tex_2ds->first_pixels = NULL; + + tex_2ds->slice_x_spans = NULL; + tex_2ds->slice_y_spans = NULL; + tex_2ds->slice_gl_handles = NULL; + + /* Unknown filter */ + tex->min_filter = GL_FALSE; + tex->mag_filter = GL_FALSE; + + if (upload_data->bitmap.data) + { + if (!_cogl_texture_upload_data_prepare (upload_data, internal_format)) + return FALSE; + + /* Create slices for the given format and size */ + if (!_cogl_texture_2d_sliced_slices_create (tex_2ds, upload_data)) + return FALSE; + + if (!_cogl_texture_2d_sliced_upload_to_gl (tex_2ds, upload_data)) + return FALSE; + } + else + { + /* Find closest GL format match */ + upload_data->bitmap.format = + _cogl_pixel_format_to_gl (internal_format, + &upload_data->gl_intformat, + &upload_data->gl_format, + &upload_data->gl_type); + + /* Create slices for the given format and size */ + if (!_cogl_texture_2d_sliced_slices_create (tex_2ds, upload_data)) + return FALSE; + } + + tex->gl_format = upload_data->gl_intformat; + tex->width = upload_data->bitmap.width; + tex->height = upload_data->bitmap.height; + tex->format = upload_data->bitmap.format; + + return TRUE; +} + 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; + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + CoglTextureUploadData upload_data; /* Since no data, we need some internal format */ if (internal_format == COGL_PIXEL_FORMAT_ANY) internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; - /* 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->vtable = &cogl_texture_2d_sliced_vtable; - tex->is_foreign = FALSE; - tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; - tex->mipmaps_dirty = TRUE; - tex_2ds->first_pixels = NULL; + upload_data.bitmap.width = width; + upload_data.bitmap.height = height; + upload_data.bitmap.data = NULL; + upload_data.bitmap_owner = FALSE; - tex->bitmap.width = width; - tex->bitmap.height = height; - tex->bitmap.format = internal_format; - tex->bitmap.rowstride = rowstride; - tex->bitmap.data = NULL; - tex->bitmap_owner = FALSE; - - tex_2ds->slice_x_spans = NULL; - tex_2ds->slice_y_spans = NULL; - tex_2ds->slice_gl_handles = NULL; - - if (flags & COGL_TEXTURE_NO_SLICING) + 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)) + if (!_cogl_texture_2d_sliced_upload_from_data (tex_2ds, &upload_data, + internal_format)) { _cogl_texture_2d_sliced_free (tex_2ds); + _cogl_texture_upload_data_free (&upload_data); return COGL_INVALID_HANDLE; } + tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; + + _cogl_texture_upload_data_free (&upload_data); + return _cogl_texture_2d_sliced_handle_new (tex_2ds); } @@ -972,9 +1006,9 @@ _cogl_texture_2d_sliced_new_from_data (unsigned int width, unsigned int rowstride, const guint8 *data) { - CoglTexture2DSliced *tex_2ds; - CoglTexture *tex; - int bpp; + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + CoglTextureUploadData upload_data; if (format == COGL_PIXEL_FORMAT_ANY) return COGL_INVALID_HANDLE; @@ -983,8 +1017,8 @@ _cogl_texture_2d_sliced_new_from_data (unsigned int width, return COGL_INVALID_HANDLE; /* Rowstride from width if not given */ - bpp = _cogl_get_format_bpp (format); - if (rowstride == 0) rowstride = width * bpp; + if (rowstride == 0) + rowstride = width * _cogl_get_format_bpp (format); /* Create new texture and fill with given data */ tex_2ds = g_new0 (CoglTexture2DSliced, 1); @@ -992,55 +1026,34 @@ _cogl_texture_2d_sliced_new_from_data (unsigned int width, tex = COGL_TEXTURE (tex_2ds); tex->vtable = &cogl_texture_2d_sliced_vtable; - 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; + upload_data.bitmap.width = width; + upload_data.bitmap.height = height; + upload_data.bitmap.data = (guchar*)data; + upload_data.bitmap.format = format; + upload_data.bitmap.rowstride = rowstride; + upload_data.bitmap_owner = FALSE; 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)) + if (!_cogl_texture_2d_sliced_upload_from_data (tex_2ds, &upload_data, + internal_format)) { _cogl_texture_2d_sliced_free (tex_2ds); + _cogl_texture_upload_data_free (&upload_data); return COGL_INVALID_HANDLE; } - if (!_cogl_texture_2d_sliced_slices_create (tex_2ds)) - { - _cogl_texture_2d_sliced_free (tex_2ds); - return COGL_INVALID_HANDLE; - } + _cogl_texture_upload_data_free (&upload_data); - 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)); + tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; return _cogl_texture_2d_sliced_handle_new (tex_2ds); } @@ -1050,9 +1063,10 @@ _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; + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + CoglBitmap *bmp = (CoglBitmap *)bmp_handle; + CoglTextureUploadData upload_data; g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); @@ -1060,29 +1074,15 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, tex_2ds = g_new0 (CoglTexture2DSliced, 1); tex = COGL_TEXTURE (tex_2ds); - tex->vtable = &cogl_texture_2d_sliced_vtable; - 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; + upload_data.bitmap = *bmp; + upload_data.bitmap_owner = FALSE; 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 @@ -1091,25 +1091,17 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, * CoglHandle is returned, it should also be destroyed * with cogl_handle_unref at some point! */ - if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + if (!_cogl_texture_2d_sliced_upload_from_data (tex_2ds, &upload_data, + internal_format)) { _cogl_texture_2d_sliced_free (tex_2ds); + _cogl_texture_upload_data_free (&upload_data); return COGL_INVALID_HANDLE; } - if (!_cogl_texture_2d_sliced_slices_create (tex_2ds)) - { - _cogl_texture_2d_sliced_free (tex_2ds); - return COGL_INVALID_HANDLE; - } + tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; - 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); + _cogl_texture_upload_data_free (&upload_data); return _cogl_texture_2d_sliced_handle_new (tex_2ds); } @@ -1160,7 +1152,6 @@ _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, GLint gl_width = 0; GLint gl_height = 0; GLint gl_gen_mipmap; - guint bpp; CoglTexture2DSliced *tex_2ds; CoglTexture *tex; CoglSpan x_span; @@ -1250,17 +1241,11 @@ _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, 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->format = format; + tex->width = gl_width - x_pot_waste; + tex->height = gl_height - y_pot_waste; 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; @@ -1351,8 +1336,8 @@ _cogl_texture_2d_sliced_transform_coords_to_gl (CoglTexture *tex, x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0); - *s *= tex->bitmap.width / (float)x_span->size; - *t *= tex->bitmap.height / (float)y_span->size; + *s *= tex->width / (float)x_span->size; + *t *= tex->height / (float)y_span->size; #if HAVE_COGL_GL /* Denormalize texture coordinates for rectangle textures */ @@ -1503,7 +1488,7 @@ _cogl_texture_2d_sliced_set_region (CoglTexture *tex, 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, + closest_format = _cogl_pixel_format_to_gl (tex->format, NULL, /* don't need */ &closest_gl_format, &closest_gl_type); @@ -1661,14 +1646,14 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, /* Default to internal format if none specified */ if (format == COGL_PIXEL_FORMAT_ANY) - format = tex->bitmap.format; + format = tex->format; /* Rowstride from texture width if none specified */ bpp = _cogl_get_format_bpp (format); - if (rowstride == 0) rowstride = tex->bitmap.width * bpp; + if (rowstride == 0) rowstride = tex->width * bpp; /* Return byte size if only that requested */ - byte_size = tex->bitmap.height * rowstride; + byte_size = tex->height * rowstride; if (data == NULL) return byte_size; closest_format = @@ -1677,11 +1662,13 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, &closest_gl_type); closest_bpp = _cogl_get_format_bpp (closest_format); + target_bmp.width = tex->width; + target_bmp.height = tex->height; + /* 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; @@ -1689,7 +1676,6 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, 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 diff --git a/cogl/cogl-texture-driver.h b/cogl/cogl-texture-driver.h index 6d6b560f2..6626d18ff 100644 --- a/cogl/cogl-texture-driver.h +++ b/cogl/cogl-texture-driver.h @@ -57,7 +57,8 @@ _cogl_texture_driver_prep_gl_for_pixels_upload (int pixels_rowstride, * XXX: sorry for the ridiculous number of arguments :-( */ void -_cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, +_cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target, + GLuint gl_handle, int src_x, int src_y, int dst_x, @@ -66,8 +67,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, int height, CoglBitmap *source_bmp, GLuint source_gl_format, - GLuint source_gl_type, - GLuint gl_handle); + GLuint source_gl_type); /* * This sets up the glPixelStore state for an download to a destination with diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index 8c06af3da..14e2d3926 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -30,8 +30,9 @@ #define COGL_TEXTURE(tex) ((CoglTexture *)(tex)) -typedef struct _CoglTexture CoglTexture; -typedef struct _CoglTextureVtable CoglTextureVtable; +typedef struct _CoglTexture CoglTexture; +typedef struct _CoglTextureVtable CoglTextureVtable; +typedef struct _CoglTextureUploadData CoglTextureUploadData; typedef void (*CoglTextureSliceCallback) (CoglHandle handle, GLuint gl_handle, @@ -95,16 +96,29 @@ struct _CoglTextureVtable GLenum wrap_mode); }; +/* This represents the state needed to upload texture data. There are + utility functions in cogl-texture which use this state */ +struct _CoglTextureUploadData +{ + CoglBitmap bitmap; + gboolean bitmap_owner; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; +}; + struct _CoglTexture { CoglHandleObject _parent; const CoglTextureVtable *vtable; - CoglBitmap bitmap; - gboolean bitmap_owner; - GLenum gl_target; - GLenum gl_intformat; + /* The internal format of the GL texture represented as a + CoglPixelFormat */ + CoglPixelFormat format; + /* The internal format of the GL texture represented as a GL enum */ GLenum gl_format; - GLenum gl_type; + GLenum gl_target; + gint width; + gint height; GLenum min_filter; GLenum mag_filter; gboolean is_foreign; @@ -144,23 +158,19 @@ _cogl_texture_set_filters (CoglHandle handle, void _cogl_texture_ensure_mipmaps (CoglHandle handle); - -/* Functions currently only used by CoglTexture implementations or - * drivers... */ +/* Utility functions to help uploading a bitmap. These are intended to + * be used by CoglTexture implementations or drivers... */ void -_cogl_texture_free (CoglTexture *tex); +_cogl_texture_upload_data_free (CoglTextureUploadData *data); void -_cogl_texture_bitmap_free (CoglTexture *tex); - -void -_cogl_texture_bitmap_swap (CoglTexture *tex, - CoglBitmap *new_bitmap); +_cogl_texture_upload_data_swap_bitmap (CoglTextureUploadData *data, + CoglBitmap *new_bitmap); gboolean -_cogl_texture_bitmap_prepare (CoglTexture *tex, - CoglPixelFormat internal_format); +_cogl_texture_upload_data_prepare (CoglTextureUploadData *data, + CoglPixelFormat internal_format); void _cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride); @@ -168,8 +178,11 @@ _cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride); void _cogl_texture_prep_gl_alignment_for_pixels_download (int pixels_rowstride); +/* Utility function to use as a fallback for getting the data of any + texture via the framebuffer */ + gboolean -_cogl_texture_draw_and_read (CoglTexture *tex, +_cogl_texture_draw_and_read (CoglHandle handle, CoglBitmap *target_bmp, GLuint target_gl_format, GLuint target_gl_type); diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 5b3950bc3..0600898e2 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -96,24 +96,24 @@ cogl_texture_unref (CoglHandle handle) } void -_cogl_texture_bitmap_free (CoglTexture *tex) +_cogl_texture_upload_data_free (CoglTextureUploadData *data) { - if (tex->bitmap.data != NULL && tex->bitmap_owner) - g_free (tex->bitmap.data); + if (data->bitmap.data != NULL && data->bitmap_owner) + g_free (data->bitmap.data); - tex->bitmap.data = NULL; - tex->bitmap_owner = FALSE; + data->bitmap.data = NULL; + data->bitmap_owner = FALSE; } void -_cogl_texture_bitmap_swap (CoglTexture *tex, - CoglBitmap *new_bitmap) +_cogl_texture_upload_data_swap_bitmap (CoglTextureUploadData *data, + CoglBitmap *new_bitmap) { - if (tex->bitmap.data != NULL && tex->bitmap_owner) - g_free (tex->bitmap.data); + if (data->bitmap.data != NULL && data->bitmap_owner) + g_free (data->bitmap.data); - tex->bitmap = *new_bitmap; - tex->bitmap_owner = TRUE; + data->bitmap = *new_bitmap; + data->bitmap_owner = TRUE; } void @@ -153,8 +153,8 @@ _cogl_texture_set_wrap_mode_parameter (CoglHandle handle, } gboolean -_cogl_texture_bitmap_prepare (CoglTexture *tex, - CoglPixelFormat internal_format) +_cogl_texture_upload_data_prepare (CoglTextureUploadData *data, + CoglPixelFormat internal_format) { CoglBitmap new_bitmap; CoglPixelFormat new_data_format; @@ -165,23 +165,23 @@ _cogl_texture_bitmap_prepare (CoglTexture *tex, * add control over this. */ if (internal_format == COGL_PIXEL_FORMAT_ANY) { - if ((tex->bitmap.format & COGL_A_BIT) && - tex->bitmap.format != COGL_PIXEL_FORMAT_A_8) - internal_format = tex->bitmap.format | COGL_PREMULT_BIT; + if ((data->bitmap.format & COGL_A_BIT) && + data->bitmap.format != COGL_PIXEL_FORMAT_A_8) + internal_format = data->bitmap.format | COGL_PREMULT_BIT; else - internal_format = tex->bitmap.format; + internal_format = data->bitmap.format; } /* Find closest format accepted by GL */ new_data_format = _cogl_pixel_format_to_gl (internal_format, - &tex->gl_intformat, - &tex->gl_format, - &tex->gl_type); + &data->gl_intformat, + &data->gl_format, + &data->gl_type); /* Convert to internal format */ - if (new_data_format != tex->bitmap.format) + if (new_data_format != data->bitmap.format) { - success = _cogl_bitmap_convert_and_premult (&tex->bitmap, + success = _cogl_bitmap_convert_and_premult (&data->bitmap, &new_bitmap, new_data_format); @@ -189,18 +189,12 @@ _cogl_texture_bitmap_prepare (CoglTexture *tex, return FALSE; /* Update texture with new data */ - _cogl_texture_bitmap_swap (tex, &new_bitmap); + _cogl_texture_upload_data_swap_bitmap (data, &new_bitmap); } return TRUE; } -void -_cogl_texture_free (CoglTexture *tex) -{ - _cogl_texture_bitmap_free (tex); -} - CoglHandle cogl_texture_new_with_size (guint width, guint height, @@ -281,7 +275,7 @@ cogl_texture_get_width (CoglHandle handle) tex = COGL_TEXTURE (handle); - return tex->bitmap.width; + return tex->width; } guint @@ -294,7 +288,7 @@ cogl_texture_get_height (CoglHandle handle) tex = COGL_TEXTURE (handle); - return tex->bitmap.height; + return tex->height; } CoglPixelFormat @@ -307,7 +301,7 @@ cogl_texture_get_format (CoglHandle handle) tex = COGL_TEXTURE (handle); - return tex->bitmap.format; + return tex->format; } guint @@ -318,9 +312,14 @@ cogl_texture_get_rowstride (CoglHandle handle) if (!cogl_is_texture (handle)) return 0; + /* FIXME: This function should go away. It previously just returned + the rowstride that was used to upload the data as far as I can + tell. This is not helpful */ + tex = COGL_TEXTURE (handle); - return tex->bitmap.rowstride; + /* Just guess at a suitable rowstride */ + return _cogl_get_format_bpp (tex->format) * tex->width; } gint @@ -416,7 +415,7 @@ _cogl_texture_get_internal_gl_format (CoglHandle handle) { CoglTexture *tex = COGL_TEXTURE (handle); - return tex->gl_intformat; + return tex->gl_format; } gboolean @@ -507,7 +506,7 @@ cogl_texture_set_region (CoglHandle handle, * glGetTexImage, but may be used as a fallback in some circumstances. */ static void -do_texture_draw_and_read (CoglTexture *tex, +do_texture_draw_and_read (CoglHandle handle, CoglBitmap *target_bmp, GLint *viewport) { @@ -518,16 +517,18 @@ do_texture_draw_and_read (CoglTexture *tex, float tx2, ty2; int bw, bh; CoglBitmap rect_bmp; - CoglHandle handle; + guint tex_width, tex_height; - handle = (CoglHandle) tex; bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); + tex_width = cogl_texture_get_width (handle); + tex_height = cogl_texture_get_height (handle); + 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]) + for (bh = tex_height; bh > 0; bh -= viewport[3]) { /* Rectangle Y coords */ ry1 = ry2; @@ -535,13 +536,13 @@ do_texture_draw_and_read (CoglTexture *tex, /* Normalized texture Y coords */ ty1 = ty2; - ty2 = (ry2 / (float)tex->bitmap.height); + ty2 = (ry2 / (float) tex_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]) + for (bw = tex_width; bw > 0; bw-=viewport[2]) { /* Rectangle X coords */ rx1 = rx2; @@ -549,7 +550,7 @@ do_texture_draw_and_read (CoglTexture *tex, /* Normalized texture X coords */ tx1 = tx2; - tx2 = (rx2 / (float)tex->bitmap.width); + tx2 = (rx2 / (float) tex_width); /* Draw a portion of texture */ cogl_rectangle_with_texture_coords (0, 0, @@ -595,7 +596,7 @@ do_texture_draw_and_read (CoglTexture *tex, * glGetTexImage, but may be used as a fallback in some circumstances. */ gboolean -_cogl_texture_draw_and_read (CoglTexture *tex, +_cogl_texture_draw_and_read (CoglHandle handle, CoglBitmap *target_bmp, GLuint target_gl_format, GLuint target_gl_type) @@ -650,14 +651,14 @@ _cogl_texture_draw_and_read (CoglTexture *tex, 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 (ctx->texture_download_material, 0, handle); cogl_material_set_layer_combine (ctx->texture_download_material, 0, /* layer */ "RGBA = REPLACE (TEXTURE)", NULL); - do_texture_draw_and_read (tex, target_bmp, viewport); + do_texture_draw_and_read (handle, target_bmp, viewport); /* Check whether texture has alpha and framebuffer not */ /* FIXME: For some reason even if ALPHA_BITS is 8, the framebuffer @@ -672,7 +673,7 @@ _cogl_texture_draw_and_read (CoglTexture *tex, 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*/) + if ((cogl_texture_get_format (handle) & COGL_A_BIT)/* && a_bits == 0*/) { guchar *srcdata; guchar *dstdata; @@ -694,7 +695,7 @@ _cogl_texture_draw_and_read (CoglTexture *tex, "RGBA = REPLACE (TEXTURE[A])", NULL); - do_texture_draw_and_read (tex, &alpha_bmp, viewport); + do_texture_draw_and_read (handle, &alpha_bmp, viewport); /* Copy temp R to target A */ srcdata = alpha_bmp.data; diff --git a/cogl/driver/gl/cogl-texture-driver.c b/cogl/driver/gl/cogl-texture-driver.c index 742fab807..024ce9b8a 100644 --- a/cogl/driver/gl/cogl-texture-driver.c +++ b/cogl/driver/gl/cogl-texture-driver.c @@ -102,7 +102,8 @@ _cogl_texture_driver_prep_gl_for_pixels_download (int pixels_rowstride, } void -_cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, +_cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target, + GLuint gl_handle, int src_x, int src_y, int dst_x, @@ -111,8 +112,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, int height, CoglBitmap *source_bmp, GLuint source_gl_format, - GLuint source_gl_type, - GLuint gl_handle) + GLuint source_gl_type) { int bpp = _cogl_get_format_bpp (source_bmp->format); @@ -122,11 +122,11 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, src_y, bpp); - /* Upload new image data */ - GE( _cogl_texture_driver_bind (tex->gl_target, - gl_handle, tex->gl_intformat) ); + /* We don't need to use _cogl_texture_driver_bind here because we're + not using the bound texture to render yet */ + GE( glBindTexture (gl_target, gl_handle) ); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (gl_target, 0, dst_x, dst_y, width, height, source_gl_format, diff --git a/cogl/driver/gles/cogl-texture-driver.c b/cogl/driver/gles/cogl-texture-driver.c index 0461ae8da..42e5af27d 100644 --- a/cogl/driver/gles/cogl-texture-driver.c +++ b/cogl/driver/gles/cogl-texture-driver.c @@ -71,7 +71,8 @@ _cogl_texture_driver_prep_gl_for_pixels_download (int pixels_rowstride, } void -_cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, +_cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target, + GLuint gl_handle, int src_x, int src_y, int dst_x, @@ -80,8 +81,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, int height, CoglBitmap *source_bmp, GLuint source_gl_format, - GLuint source_gl_type, - GLuint gl_handle) + GLuint source_gl_type) { int bpp = _cogl_get_format_bpp (source_bmp->format); CoglBitmap slice_bmp; @@ -94,7 +94,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, * rowstride = bpp * width and the texture image is not sliced */ /* Setup temp bitmap for slice subregion */ - slice_bmp.format = tex->bitmap.format; + slice_bmp.format = source_bmp->format; slice_bmp.width = width; slice_bmp.height = height; slice_bmp.rowstride = bpp * slice_bmp.width; @@ -113,11 +113,11 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, slice_bmp.width, slice_bmp.height); - /* Upload new image data */ - GE( _cogl_texture_driver_bind (tex->gl_target, - gl_handle, tex->gl_intformat) ); + /* We don't need to use _cogl_texture_driver_bind here because we're + not using the bound texture to render yet */ + GE( glBindTexture (gl_target, gl_handle) ); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (gl_target, 0, dst_x, dst_y, width, height, source_gl_format, From 25f3b993c61957801fcd2071d4d0cf060b02c886 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 26 Nov 2009 18:58:17 +0000 Subject: [PATCH 02/33] cogl: Move all of the fields from CoglTexture to CoglTexture2DSliced Most of the fields that were previously in CoglTexture are specific to the implementation of CoglTexture2DSliced so they should be placed there instead. For example, the 'mipmaps_dirty' flag is an implementation detail of the ensure_mipmaps function so it doesn't make sense to force all texture backends to have this function. Other fields such as width, height, gl_format and format may make sense for all textures but I've added them as virtual functions instead. This may make more sense for a sub-texture backend for example where it can calculate these based on the full texture. --- cogl/cogl-texture-2d-sliced-private.h | 25 +++- cogl/cogl-texture-2d-sliced.c | 204 +++++++++++++++----------- cogl/cogl-texture-private.h | 21 +-- cogl/cogl-texture.c | 19 +-- 4 files changed, 149 insertions(+), 120 deletions(-) diff --git a/cogl/cogl-texture-2d-sliced-private.h b/cogl/cogl-texture-2d-sliced-private.h index 99f0e722a..70eba4a97 100644 --- a/cogl/cogl-texture-2d-sliced-private.h +++ b/cogl/cogl-texture-2d-sliced-private.h @@ -49,15 +49,30 @@ struct _CoglTexturePixel struct _CoglTexture2DSliced { CoglTexture _parent; - GArray *slice_x_spans; - GArray *slice_y_spans; - GArray *slice_gl_handles; - gint max_waste; + GArray *slice_x_spans; + GArray *slice_y_spans; + GArray *slice_gl_handles; + gint max_waste; + + /* The internal format of the GL texture represented as a + CoglPixelFormat */ + CoglPixelFormat format; + /* The internal format of the GL texture represented as a GL enum */ + GLenum gl_format; + GLenum gl_target; + gint width; + gint height; + 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; + CoglTexturePixel *first_pixels; }; GQuark diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c index b91e60b87..5f0900d4b 100644 --- a/cogl/cogl-texture-2d-sliced.c +++ b/cogl/cogl-texture-2d-sliced.c @@ -71,12 +71,12 @@ _cogl_texture_2d_sliced_foreach_sub_texture_in_region ( void *user_data) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); - float width = tex->width; - float height = tex->height; + float width = tex_2ds->width; + float height = tex_2ds->height; CoglSpanIter iter_x; CoglSpanIter iter_y; - g_assert (tex->gl_target == GL_TEXTURE_2D); + g_assert (tex_2ds->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 @@ -173,7 +173,7 @@ _cogl_texture_2d_sliced_foreach_sub_texture_in_region ( callback (tex, gl_handle, - tex->gl_target, + tex_2ds->gl_target, slice_coords, virtual_coords, user_data); @@ -221,7 +221,6 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, gint bpp; gint x,y; guchar *waste_buf; - CoglTexture *tex = COGL_TEXTURE (tex_2ds); bpp = _cogl_get_format_bpp (upload_data->bitmap.format); @@ -245,7 +244,7 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, slice_num); _cogl_texture_driver_upload_subregion_to_gl ( - tex->gl_target, + tex_2ds->gl_target, gl_handle, x_span->start, /* src x */ y_span->start, /* src y */ @@ -292,7 +291,7 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, x_span->waste * bpp, bpp); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (tex_2ds->gl_target, 0, x_span->size - x_span->waste, 0, x_span->waste, @@ -326,7 +325,7 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, x_span->size * bpp, bpp); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (tex_2ds->gl_target, 0, 0, y_span->size - y_span->waste, x_span->size, @@ -340,7 +339,7 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, if (waste_buf) g_free (waste_buf); - tex->mipmaps_dirty = TRUE; + tex_2ds->mipmaps_dirty = TRUE; return TRUE; } @@ -357,7 +356,6 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, GLuint source_gl_format, GLuint source_gl_type) { - CoglTexture *tex = COGL_TEXTURE (tex_2ds); CoglSpan *x_span; CoglSpan *y_span; gint bpp; @@ -378,7 +376,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, for (source_y = src_y, _cogl_span_iter_begin (&y_iter, tex_2ds->slice_y_spans, - tex->height, + tex_2ds->height, dst_y, dst_y + height); @@ -401,7 +399,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, for (source_x = src_x, _cogl_span_iter_begin (&x_iter, tex_2ds->slice_x_spans, - tex->width, + tex_2ds->width, dst_x, dst_x + width); @@ -435,7 +433,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, /* 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->gl_target, + _cogl_texture_driver_upload_subregion_to_gl (tex_2ds->gl_target, gl_handle, source_x, source_y, @@ -493,7 +491,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, x_span->waste * bpp, bpp); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (tex_2ds->gl_target, 0, x_span->size - x_span->waste, local_y, x_span->waste, @@ -544,7 +542,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, copy_width * bpp, bpp); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (tex_2ds->gl_target, 0, local_x, y_span->size - y_span->waste, copy_width, @@ -559,7 +557,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, if (waste_buf) g_free (waste_buf); - tex->mipmaps_dirty = TRUE; + tex_2ds->mipmaps_dirty = TRUE; return TRUE; } @@ -664,7 +662,7 @@ _cogl_texture_2d_sliced_set_wrap_mode_parameter (CoglTexture *tex, /* 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) + if (tex_2ds->wrap_mode != wrap_mode) { int i; @@ -676,12 +674,12 @@ _cogl_texture_2d_sliced_set_wrap_mode_parameter (CoglTexture *tex, { 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) ); + GE( glBindTexture (tex_2ds->gl_target, texnum) ); + GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_WRAP_S, wrap_mode) ); + GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_WRAP_T, wrap_mode) ); } - tex->wrap_mode = wrap_mode; + tex_2ds->wrap_mode = wrap_mode; } } @@ -689,7 +687,6 @@ static gboolean _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, const CoglTextureUploadData *upload_data) { - CoglTexture *tex = COGL_TEXTURE (tex_2ds); gint max_width; gint max_height; GLuint *gl_handles; @@ -708,14 +705,14 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, { max_width = upload_data->bitmap.width; max_height = upload_data->bitmap.height; - tex->gl_target = GL_TEXTURE_2D; + tex_2ds->gl_target = GL_TEXTURE_2D; slices_for_size = _cogl_rect_slices_for_size; } else { max_width = cogl_util_next_p2 (upload_data->bitmap.width); max_height = cogl_util_next_p2 (upload_data->bitmap.height); - tex->gl_target = GL_TEXTURE_2D; + tex_2ds->gl_target = GL_TEXTURE_2D; slices_for_size = _cogl_pot_slices_for_size; } @@ -725,7 +722,7 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, CoglSpan span; /* Check if size supported else bail out */ - if (!_cogl_texture_driver_size_supported (tex->gl_target, + if (!_cogl_texture_driver_size_supported (tex_2ds->gl_target, upload_data->gl_intformat, upload_data->gl_type, max_width, @@ -759,7 +756,7 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, else { /* Decrease the size of largest slice until supported by GL */ - while (!_cogl_texture_driver_size_supported (tex->gl_target, + while (!_cogl_texture_driver_size_supported (tex_2ds->gl_target, upload_data->gl_intformat, upload_data->gl_type, max_width, @@ -821,7 +818,7 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, tex_2ds->first_pixels = g_new (CoglTexturePixel, n_slices); /* Wrap mode not yet set */ - tex->wrap_mode = GL_FALSE; + tex_2ds->wrap_mode = GL_FALSE; /* Generate a "working set" of GL texture objects * (some implementations might supported faster @@ -846,15 +843,15 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, y_span->size - y_span->waste); /* Setup texture parameters */ - GE( _cogl_texture_driver_bind (tex->gl_target, + GE( _cogl_texture_driver_bind (tex_2ds->gl_target, gl_handles[y * n_x_slices + x], upload_data->gl_intformat) ); - _cogl_texture_driver_try_setting_gl_border_color (tex->gl_target, + _cogl_texture_driver_try_setting_gl_border_color (tex_2ds->gl_target, transparent_color); /* Pass NULL data to init size and internal format */ - GE( glTexImage2D (tex->gl_target, 0, upload_data->gl_intformat, + GE( glTexImage2D (tex_2ds->gl_target, 0, upload_data->gl_intformat, x_span->size, y_span->size, 0, upload_data->gl_format, upload_data->gl_type, 0) ); } @@ -866,8 +863,6 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, 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); @@ -876,7 +871,7 @@ _cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *tex_2ds) if (tex_2ds->slice_gl_handles != NULL) { - if (tex->is_foreign == FALSE) + if (tex_2ds->is_foreign == FALSE) { GE( glDeleteTextures (tex_2ds->slice_gl_handles->len, (GLuint*) tex_2ds->slice_gl_handles->data) ); @@ -906,9 +901,9 @@ _cogl_texture_2d_sliced_upload_from_data tex->vtable = &cogl_texture_2d_sliced_vtable; - tex->is_foreign = FALSE; - tex->auto_mipmap = FALSE; - tex->mipmaps_dirty = TRUE; + tex_2ds->is_foreign = FALSE; + tex_2ds->auto_mipmap = FALSE; + tex_2ds->mipmaps_dirty = TRUE; tex_2ds->first_pixels = NULL; tex_2ds->slice_x_spans = NULL; @@ -916,8 +911,8 @@ _cogl_texture_2d_sliced_upload_from_data tex_2ds->slice_gl_handles = NULL; /* Unknown filter */ - tex->min_filter = GL_FALSE; - tex->mag_filter = GL_FALSE; + tex_2ds->min_filter = GL_FALSE; + tex_2ds->mag_filter = GL_FALSE; if (upload_data->bitmap.data) { @@ -945,10 +940,10 @@ _cogl_texture_2d_sliced_upload_from_data return FALSE; } - tex->gl_format = upload_data->gl_intformat; - tex->width = upload_data->bitmap.width; - tex->height = upload_data->bitmap.height; - tex->format = upload_data->bitmap.format; + tex_2ds->gl_format = upload_data->gl_intformat; + tex_2ds->width = upload_data->bitmap.width; + tex_2ds->height = upload_data->bitmap.height; + tex_2ds->format = upload_data->bitmap.format; return TRUE; } @@ -990,7 +985,7 @@ _cogl_texture_2d_sliced_new_with_size (unsigned int width, return COGL_INVALID_HANDLE; } - tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; + tex_2ds->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; _cogl_texture_upload_data_free (&upload_data); @@ -1053,7 +1048,7 @@ _cogl_texture_2d_sliced_new_from_data (unsigned int width, _cogl_texture_upload_data_free (&upload_data); - tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; + tex_2ds->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; return _cogl_texture_2d_sliced_handle_new (tex_2ds); } @@ -1099,7 +1094,7 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, return COGL_INVALID_HANDLE; } - tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; + tex_2ds->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; _cogl_texture_upload_data_free (&upload_data); @@ -1236,24 +1231,24 @@ _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, tex->vtable = &cogl_texture_2d_sliced_vtable; /* Setup bitmap info */ - tex->is_foreign = TRUE; - tex->auto_mipmap = (gl_gen_mipmap == GL_TRUE) ? TRUE : FALSE; - tex->mipmaps_dirty = TRUE; + tex_2ds->is_foreign = TRUE; + tex_2ds->auto_mipmap = (gl_gen_mipmap == GL_TRUE) ? TRUE : FALSE; + tex_2ds->mipmaps_dirty = TRUE; tex_2ds->first_pixels = NULL; - tex->format = format; - tex->width = gl_width - x_pot_waste; - tex->height = gl_height - y_pot_waste; - tex->gl_target = gl_target; - tex->gl_format = gl_int_format; + tex_2ds->format = format; + tex_2ds->width = gl_width - x_pot_waste; + tex_2ds->height = gl_height - y_pot_waste; + tex_2ds->gl_target = gl_target; + tex_2ds->gl_format = gl_int_format; /* Unknown filter */ - tex->min_filter = GL_FALSE; - tex->mag_filter = GL_FALSE; + tex_2ds->min_filter = GL_FALSE; + tex_2ds->mag_filter = GL_FALSE; tex_2ds->max_waste = 0; /* Wrap mode not yet set */ - tex->wrap_mode = GL_FALSE; + tex_2ds->wrap_mode = GL_FALSE; /* Create slice arrays */ tex_2ds->slice_x_spans = @@ -1318,6 +1313,12 @@ _cogl_texture_2d_sliced_can_hardware_repeat (CoglTexture *tex) x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0); +#if HAVE_COGL_GL + /* TODO: COGL_TEXTURE_TYPE_2D_RECTANGLE */ + if (tex_2ds->gl_target == GL_TEXTURE_RECTANGLE_ARB) + return FALSE; +#endif + return (x_span->waste || y_span->waste) ? FALSE : TRUE; } @@ -1336,12 +1337,12 @@ _cogl_texture_2d_sliced_transform_coords_to_gl (CoglTexture *tex, x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0); - *s *= tex->width / (float)x_span->size; - *t *= tex->height / (float)y_span->size; + *s *= tex_2ds->width / (float)x_span->size; + *t *= tex_2ds->height / (float)y_span->size; #if HAVE_COGL_GL /* Denormalize texture coordinates for rectangle textures */ - if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + if (tex_2ds->gl_target == GL_TEXTURE_RECTANGLE_ARB) { *s *= x_span->size; *t *= y_span->size; @@ -1366,7 +1367,7 @@ _cogl_texture_2d_sliced_get_gl_texture (CoglTexture *tex, *out_gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, 0); if (out_gl_target != NULL) - *out_gl_target = tex->gl_target; + *out_gl_target = tex_2ds->gl_target; return TRUE; } @@ -1384,23 +1385,23 @@ _cogl_texture_2d_sliced_set_filters (CoglTexture *tex, if (tex_2ds->slice_gl_handles == NULL) return; - if (min_filter == tex->min_filter - && mag_filter == tex->mag_filter) + if (min_filter == tex_2ds->min_filter + && mag_filter == tex_2ds->mag_filter) return; /* Store new values */ - tex->min_filter = min_filter; - tex->mag_filter = mag_filter; + tex_2ds->min_filter = min_filter; + tex_2ds->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) ); + GE( glBindTexture (tex_2ds->gl_target, gl_handle) ); + GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_MAG_FILTER, + tex_2ds->mag_filter) ); + GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_MIN_FILTER, + tex_2ds->min_filter) ); } } @@ -1413,7 +1414,7 @@ _cogl_texture_2d_sliced_ensure_mipmaps (CoglTexture *tex) _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Only update if the mipmaps are dirty */ - if (!tex->auto_mipmap || !tex->mipmaps_dirty) + if (!tex_2ds->auto_mipmap || !tex_2ds->mipmaps_dirty) return; /* Make sure slices were created */ @@ -1424,25 +1425,25 @@ _cogl_texture_2d_sliced_ensure_mipmaps (CoglTexture *tex) 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) ); + GE( glBindTexture (tex_2ds->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); + _cogl_texture_driver_gl_generate_mipmaps (tex_2ds->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, + GE( glTexParameteri (tex_2ds->gl_target, GL_GENERATE_MIPMAP, GL_TRUE) ); + GE( glTexSubImage2D (tex_2ds->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) ); + GE( glTexParameteri (tex_2ds->gl_target, GL_GENERATE_MIPMAP, GL_FALSE) ); } } - tex->mipmaps_dirty = FALSE; + tex_2ds->mipmaps_dirty = FALSE; } static gboolean @@ -1488,7 +1489,7 @@ _cogl_texture_2d_sliced_set_region (CoglTexture *tex, 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->format, + closest_format = _cogl_pixel_format_to_gl (tex_2ds->format, NULL, /* don't need */ &closest_gl_format, &closest_gl_type); @@ -1530,7 +1531,6 @@ _cogl_texture_2d_sliced_download_from_gl ( GLuint target_gl_format, GLuint target_gl_type) { - CoglTexture *tex = COGL_TEXTURE (tex_2ds); CoglSpan *x_span; CoglSpan *y_span; GLuint gl_handle; @@ -1574,9 +1574,9 @@ _cogl_texture_2d_sliced_download_from_gl ( bpp); /* Download slice image data into temp bmp */ - GE( glBindTexture (tex->gl_target, gl_handle) ); + GE( glBindTexture (tex_2ds->gl_target, gl_handle) ); - if (!_cogl_texture_driver_gl_get_tex_image (tex->gl_target, + if (!_cogl_texture_driver_gl_get_tex_image (tex_2ds->gl_target, target_gl_format, target_gl_type, slice_bmp.data)) @@ -1608,9 +1608,9 @@ _cogl_texture_2d_sliced_download_from_gl ( bpp); /* Download slice image data */ - GE( glBindTexture (tex->gl_target, gl_handle) ); + GE( glBindTexture (tex_2ds->gl_target, gl_handle) ); - if (!_cogl_texture_driver_gl_get_tex_image (tex->gl_target, + if (!_cogl_texture_driver_gl_get_tex_image (tex_2ds->gl_target, target_gl_format, target_gl_type, dst)) @@ -1646,14 +1646,14 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, /* Default to internal format if none specified */ if (format == COGL_PIXEL_FORMAT_ANY) - format = tex->format; + format = tex_2ds->format; /* Rowstride from texture width if none specified */ bpp = _cogl_get_format_bpp (format); - if (rowstride == 0) rowstride = tex->width * bpp; + if (rowstride == 0) rowstride = tex_2ds->width * bpp; /* Return byte size if only that requested */ - byte_size = tex->height * rowstride; + byte_size = tex_2ds->height * rowstride; if (data == NULL) return byte_size; closest_format = @@ -1662,8 +1662,8 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, &closest_gl_type); closest_bpp = _cogl_get_format_bpp (closest_format); - target_bmp.width = tex->width; - target_bmp.height = tex->height; + target_bmp.width = tex_2ds->width; + target_bmp.height = tex_2ds->height; /* Is the requested format supported? */ if (closest_format == format) @@ -1723,6 +1723,30 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, return byte_size; } +static CoglPixelFormat +_cogl_texture_2d_sliced_get_format (CoglTexture *tex) +{ + return COGL_TEXTURE_2D_SLICED (tex)->format; +} + +static GLenum +_cogl_texture_2d_sliced_get_gl_format (CoglTexture *tex) +{ + return COGL_TEXTURE_2D_SLICED (tex)->gl_format; +} + +static gint +_cogl_texture_2d_sliced_get_width (CoglTexture *tex) +{ + return COGL_TEXTURE_2D_SLICED (tex)->width; +} + +static gint +_cogl_texture_2d_sliced_get_height (CoglTexture *tex) +{ + return COGL_TEXTURE_2D_SLICED (tex)->height; +} + static const CoglTextureVtable cogl_texture_2d_sliced_vtable = { @@ -1736,5 +1760,9 @@ cogl_texture_2d_sliced_vtable = _cogl_texture_2d_sliced_get_gl_texture, _cogl_texture_2d_sliced_set_filters, _cogl_texture_2d_sliced_ensure_mipmaps, - _cogl_texture_2d_sliced_set_wrap_mode_parameter + _cogl_texture_2d_sliced_set_wrap_mode_parameter, + _cogl_texture_2d_sliced_get_format, + _cogl_texture_2d_sliced_get_gl_format, + _cogl_texture_2d_sliced_get_width, + _cogl_texture_2d_sliced_get_height }; diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index 14e2d3926..d8edb2865 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -94,6 +94,11 @@ struct _CoglTextureVtable void (* set_wrap_mode_parameter) (CoglTexture *tex, GLenum wrap_mode); + + CoglPixelFormat (* get_format) (CoglTexture *tex); + GLenum (* get_gl_format) (CoglTexture *tex); + gint (* get_width) (CoglTexture *tex); + gint (* get_height) (CoglTexture *tex); }; /* This represents the state needed to upload texture data. There are @@ -111,20 +116,6 @@ struct _CoglTexture { CoglHandleObject _parent; const CoglTextureVtable *vtable; - /* The internal format of the GL texture represented as a - CoglPixelFormat */ - CoglPixelFormat format; - /* The internal format of the GL texture represented as a GL enum */ - GLenum gl_format; - GLenum gl_target; - gint width; - gint height; - GLenum min_filter; - GLenum mag_filter; - gboolean is_foreign; - GLint wrap_mode; - gboolean auto_mipmap; - gboolean mipmaps_dirty; }; void @@ -144,7 +135,7 @@ _cogl_texture_transform_coords_to_gl (CoglHandle handle, float *s, float *t); GLenum -_cogl_texture_get_internal_gl_format (CoglHandle handle); +_cogl_texture_get_gl_format (CoglHandle handle); void _cogl_texture_set_wrap_mode_parameter (CoglHandle handle, diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 0600898e2..c3e256fd3 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -275,7 +275,7 @@ cogl_texture_get_width (CoglHandle handle) tex = COGL_TEXTURE (handle); - return tex->width; + return tex->vtable->get_width (tex); } guint @@ -288,7 +288,7 @@ cogl_texture_get_height (CoglHandle handle) tex = COGL_TEXTURE (handle); - return tex->height; + return tex->vtable->get_height (tex); } CoglPixelFormat @@ -301,7 +301,7 @@ cogl_texture_get_format (CoglHandle handle) tex = COGL_TEXTURE (handle); - return tex->format; + return tex->vtable->get_format (tex); } guint @@ -319,7 +319,8 @@ cogl_texture_get_rowstride (CoglHandle handle) tex = COGL_TEXTURE (handle); /* Just guess at a suitable rowstride */ - return _cogl_get_format_bpp (tex->format) * tex->width; + return (_cogl_get_format_bpp (cogl_texture_get_format (tex)) + * cogl_texture_get_width (tex)); } gint @@ -388,12 +389,6 @@ _cogl_texture_can_hardware_repeat (CoglHandle handle) { 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 - return tex->vtable->can_hardware_repeat (tex); } @@ -411,11 +406,11 @@ _cogl_texture_transform_coords_to_gl (CoglHandle handle, } GLenum -_cogl_texture_get_internal_gl_format (CoglHandle handle) +_cogl_texture_get_gl_format (CoglHandle handle) { CoglTexture *tex = COGL_TEXTURE (handle); - return tex->gl_format; + return tex->vtable->get_gl_format (tex); } gboolean From 28e37e9f57275887ae47b83bc87375b91926c534 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 27 Nov 2009 15:47:22 +0000 Subject: [PATCH 03/33] cogl-primitives: Check for repeating after the coordinate transform In _cogl_multitexture_quad_single_primitive we use a wrap mode of GL_CLAMP_TO_EDGE if the texture coordinates are all in the range [0,1] or GL_REPEAT otherwise. This is to avoid pulling in pixels from either side when using GL_LINEAR filter mode and rendering the entire texture. Previously it was checking using the unconverted texture coordinates. This is ok unless the texture backend is radically transforming the texture coordinates, such as in the sub texture backend where the coordinates may map to something completely different. We now check whether the coordinates are in range after converting them. --- cogl/cogl-primitives.c | 87 ++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 50 deletions(-) diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c index 63bd382c6..4a614e1c5 100644 --- a/cogl/cogl-primitives.c +++ b/cogl/cogl-primitives.c @@ -249,6 +249,9 @@ _cogl_multitexture_quad_single_primitive (float x_1, const float *in_tex_coords; float *out_tex_coords; float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; + gboolean need_repeat = FALSE; + gint coord_num; + GLenum wrap_mode; tex_handle = cogl_material_layer_get_texture (layer); @@ -257,27 +260,41 @@ _cogl_multitexture_quad_single_primitive (float x_1, if (tex_handle == COGL_INVALID_HANDLE) continue; - in_tex_coords = &user_tex_coords[i * 4]; + /* If the user didn't supply texture coordinates for this layer + then use the default coords */ + if (i >= user_tex_coords_len / 4) + in_tex_coords = default_tex_coords; + else + in_tex_coords = &user_tex_coords[i * 4]; + out_tex_coords = &final_tex_coords[i * 4]; + memcpy (out_tex_coords, in_tex_coords, sizeof (GLfloat) * 4); + + /* Convert the texture coordinates to GL. We also work out + whether any of the texture coordinates are outside the range + [0.0,1.0]. We need to do this after calling + transform_coords_to_gl in case the texture backend is munging + the coordinates (such as in the sub texture backend). This + should be safe to call because we know that the texture only + has one slice. */ + for (coord_num = 0; coord_num < 2; coord_num++) + { + float *s = out_tex_coords + coord_num * 2; + float *t = s + 1; + _cogl_texture_transform_coords_to_gl (tex_handle, s, t); + if (*s < 0.0f || *s > 1.0f || *t < 0.0f || *t > 1.0f) + need_repeat = TRUE; + } /* If the texture has waste or we are using GL_TEXTURE_RECT we - * can't handle texture repeating so we check that the texture - * coords lie in the range [0,1]. - * - * NB: We already know that the texture isn't sliced so we can assume - * that the default coords (0,0) and (1,1) would only reference a single - * GL texture. + * can't handle texture repeating so we can't use the layer if + * repeating is required. * * NB: We already know that no texture matrix is being used if the * texture doesn't support hardware repeat. */ - if (!_cogl_texture_can_hardware_repeat (tex_handle) - && i < user_tex_coords_len / 4 - && (in_tex_coords[0] < 0 || in_tex_coords[0] > 1.0 - || in_tex_coords[1] < 0 || in_tex_coords[1] > 1.0 - || in_tex_coords[2] < 0 || in_tex_coords[2] > 1.0 - || in_tex_coords[3] < 0 || in_tex_coords[3] > 1.0)) + if (!_cogl_texture_can_hardware_repeat (tex_handle) && need_repeat) { if (i == 0) { @@ -315,45 +332,15 @@ _cogl_multitexture_quad_single_primitive (float x_1, } } - - /* - * Setup the texture unit... - */ - - /* NB: The user might not have supplied texture coordinates for all - * layers... */ - if (i < (user_tex_coords_len / 4)) - { - GLenum wrap_mode; - - /* If the texture coords are all in the range [0,1] then we want to - clamp the coords to the edge otherwise it can pull in edge pixels - from the wrong side when scaled */ - if (in_tex_coords[0] >= 0 && in_tex_coords[0] <= 1.0 - && in_tex_coords[1] >= 0 && in_tex_coords[1] <= 1.0 - && in_tex_coords[2] >= 0 && in_tex_coords[2] <= 1.0 - && in_tex_coords[3] >= 0 && in_tex_coords[3] <= 1.0) - wrap_mode = GL_CLAMP_TO_EDGE; - else - wrap_mode = GL_REPEAT; - - memcpy (out_tex_coords, in_tex_coords, sizeof (GLfloat) * 4); - - _cogl_texture_set_wrap_mode_parameter (tex_handle, wrap_mode); - } + /* If we're not repeating then we want to clamp the coords + to the edge otherwise it can pull in edge pixels from the + wrong side when scaled */ + if (need_repeat) + wrap_mode = GL_REPEAT; else - { - memcpy (out_tex_coords, default_tex_coords, sizeof (GLfloat) * 4); + wrap_mode = GL_CLAMP_TO_EDGE; - _cogl_texture_set_wrap_mode_parameter (tex_handle, GL_CLAMP_TO_EDGE); - } - - _cogl_texture_transform_coords_to_gl (tex_handle, - &out_tex_coords[0], - &out_tex_coords[1]); - _cogl_texture_transform_coords_to_gl (tex_handle, - &out_tex_coords[2], - &out_tex_coords[3]); + _cogl_texture_set_wrap_mode_parameter (tex_handle, wrap_mode); } _cogl_journal_log_quad (x_1, From 6bc3319c86be6b74e7e0701e11e34d4acfb56017 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 2 Dec 2009 13:41:49 +0000 Subject: [PATCH 04/33] cogl: Add a texture utility function for manually repeating Given a region of texture coordinates this utility invokes a callback enough times to cover the region with a subregion that spans the texture at most once. Eg, if called with tx1 and tx2 as 0.5 and 3.0 it it would invoke the callback with: 0.5,1.0 1.0,2.0 2.0,3.0 Manual repeating is needed by all texture backends regardless of whether they can support hardware repeating because when Cogl calls the foreach_sub_texture_in_region method then it sets the wrap mode to GL_CLAMP_TO_EDGE and no hardware repeating is possible. --- cogl/cogl-texture-private.h | 14 +++++ cogl/cogl-texture.c | 102 ++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index d8edb2865..9ea5fbfb2 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -41,6 +41,9 @@ typedef void (*CoglTextureSliceCallback) (CoglHandle handle, float *virtual_coords, void *user_data); +typedef void (* CoglTextureManualRepeatCallback) (const float *coords, + void *user_data); + struct _CoglTextureVtable { /* Virtual functions that must be implemented for a texture @@ -169,6 +172,17 @@ _cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride); void _cogl_texture_prep_gl_alignment_for_pixels_download (int pixels_rowstride); +/* Utility function for implementing manual repeating. Even texture + backends that always support hardware repeating need this because + when foreach_sub_texture_in_region is invoked Cogl will set the + wrap mode to GL_CLAMP_TO_EDGE so hardware repeating can't be + done */ +void +_cogl_texture_iterate_manual_repeats (CoglTextureManualRepeatCallback callback, + float tx_1, float ty_1, + float tx_2, float ty_2, + void *user_data); + /* Utility function to use as a fallback for getting the data of any texture via the framebuffer */ diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index c3e256fd3..e4eff2e06 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -195,6 +195,108 @@ _cogl_texture_upload_data_prepare (CoglTextureUploadData *data, return TRUE; } +gboolean +_cogl_texture_upload_data_prepare (CoglTextureUploadData *data, + CoglPixelFormat internal_format) +{ + return (_cogl_texture_upload_data_prepare_format (data, &internal_format) && + _cogl_texture_upload_data_convert (data, internal_format)); +} + +/* This is like CoglSpanIter except it deals with floats and it + effectively assumes there is only one span from 0.0 to 1.0 */ +typedef struct _CoglTextureIter +{ + gfloat pos, end, next_pos; + gboolean flipped; + gfloat t_1, t_2; +} CoglTextureIter; + +static void +_cogl_texture_iter_update (CoglTextureIter *iter) +{ + gfloat t_2; + + modff (iter->pos, &iter->next_pos); + + /* modff rounds the int part towards zero so we need to add one if + we're meant to be heading away from zero */ + if (iter->pos >= 0.0f) + iter->next_pos += 1.0f; + + if (iter->next_pos > iter->end) + t_2 = iter->end; + else + t_2 = iter->next_pos; + + if (iter->flipped) + { + iter->t_1 = t_2; + iter->t_2 = iter->pos; + } + else + { + iter->t_1 = iter->pos; + iter->t_2 = t_2; + } +} + +static void +_cogl_texture_iter_begin (CoglTextureIter *iter, + gfloat t_1, gfloat t_2) +{ + if (t_1 <= t_2) + { + iter->pos = t_1; + iter->end = t_2; + iter->flipped = FALSE; + } + else + { + iter->pos = t_2; + iter->end = t_1; + iter->flipped = TRUE; + } + + _cogl_texture_iter_update (iter); +} + +static void +_cogl_texture_iter_next (CoglTextureIter *iter) +{ + iter->pos = iter->next_pos; + _cogl_texture_iter_update (iter); +} + +static gboolean +_cogl_texture_iter_end (CoglTextureIter *iter) +{ + return iter->pos >= iter->end; +} + +/* This invokes the callback with enough quads to cover the manually + repeated range specified by the virtual texture coordinates without + emitting coordinates outside the range [0,1] */ +void +_cogl_texture_iterate_manual_repeats (CoglTextureManualRepeatCallback callback, + float tx_1, float ty_1, + float tx_2, float ty_2, + void *user_data) +{ + CoglTextureIter x_iter, y_iter; + + for (_cogl_texture_iter_begin (&y_iter, ty_1, ty_2); + !_cogl_texture_iter_end (&y_iter); + _cogl_texture_iter_next (&y_iter)) + for (_cogl_texture_iter_begin (&x_iter, tx_1, tx_2); + !_cogl_texture_iter_end (&x_iter); + _cogl_texture_iter_next (&x_iter)) + { + float coords[4] = { x_iter.t_1, y_iter.t_1, x_iter.t_2, y_iter.t_2 }; + callback (coords, user_data); + } +} + CoglHandle cogl_texture_new_with_size (guint width, guint height, From 7c5aea9b688e7fb3d57cd5a8c5bf2b8095f5f71a Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 2 Dec 2009 17:17:24 +0000 Subject: [PATCH 05/33] cogl: Make the callback for foreach_sub_texture_in_region use const The CoglTextureSliceCallback function pointer now takes const pointers for the texture coordinates. This makes it clearer that the callback should not modify the array and therefore the backend can use the same array for both sets of coords. --- cogl/cogl-journal-private.h | 2 +- cogl/cogl-journal.c | 2 +- cogl/cogl-primitives.c | 14 +++++++------- cogl/cogl-texture-private.h | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cogl/cogl-journal-private.h b/cogl/cogl-journal-private.h index dc008f67a..a410c8ff0 100644 --- a/cogl/cogl-journal-private.h +++ b/cogl/cogl-journal-private.h @@ -51,7 +51,7 @@ _cogl_journal_log_quad (float x_1, int n_layers, guint32 fallback_layers, GLuint layer0_override_texture, - float *tex_coords, + const float *tex_coords, unsigned int tex_coords_len); #endif /* __COGL_JOURNAL_PRIVATE_H */ diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c index db406ea9c..e05068365 100644 --- a/cogl/cogl-journal.c +++ b/cogl/cogl-journal.c @@ -631,7 +631,7 @@ _cogl_journal_log_quad (float x_1, int n_layers, guint32 fallback_layers, GLuint layer0_override_texture, - float *tex_coords, + const float *tex_coords, unsigned int tex_coords_len) { size_t stride; diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c index 4a614e1c5..58fec0240 100644 --- a/cogl/cogl-primitives.c +++ b/cogl/cogl-primitives.c @@ -71,8 +71,8 @@ static void log_quad_sub_textures_cb (CoglHandle texture_handle, GLuint gl_handle, GLenum gl_target, - float *subtexture_coords, - float *virtual_coords, + const float *subtexture_coords, + const float *virtual_coords, void *user_data) { TextureSlicedQuadState *state = user_data; @@ -638,11 +638,11 @@ cogl_rectangle (float x_1, void draw_polygon_sub_texture_cb (CoglHandle tex_handle, - GLuint gl_handle, - GLenum gl_target, - float *subtexture_coords, - float *virtual_coords, - void *user_data) + GLuint gl_handle, + GLenum gl_target, + const float *subtexture_coords, + const float *virtual_coords, + void *user_data) { TextureSlicedPolygonState *state = user_data; GLfloat *v; diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index 9ea5fbfb2..ee018faf4 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -37,8 +37,8 @@ typedef struct _CoglTextureUploadData CoglTextureUploadData; typedef void (*CoglTextureSliceCallback) (CoglHandle handle, GLuint gl_handle, GLenum gl_target, - float *slice_coords, - float *virtual_coords, + const float *slice_coords, + const float *virtual_coords, void *user_data); typedef void (* CoglTextureManualRepeatCallback) (const float *coords, From c9cafc798c5245a034a9537cbf390b652e17ac16 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 27 Nov 2009 16:39:16 +0000 Subject: [PATCH 06/33] cogl: Add a sub texture backend This adds a new texture backend which represents a sub texture of a larger texture. The texture is created with a reference to the full texture and a set of coordinates describing the region. The backend simply defers to the full texture for all operations and maps the coordinates to the other range. You can also use coordinates outside the range [0,1] to create a repeated version of the full texture. A new public API function called cogl_texture_new_from_sub_texture is available to create the sub texture. --- cogl/Makefile.am | 2 + cogl/cogl-sub-texture-private.h | 56 +++ cogl/cogl-sub-texture.c | 699 +++++++++++++++++++++++++++ cogl/cogl-texture.c | 15 +- cogl/cogl-texture.h | 27 ++ doc/reference/cogl/cogl-sections.txt | 1 + 6 files changed, 798 insertions(+), 2 deletions(-) create mode 100644 cogl/cogl-sub-texture-private.h create mode 100644 cogl/cogl-sub-texture.c diff --git a/cogl/Makefile.am b/cogl/Makefile.am index c3b19c424..02ab16601 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -122,9 +122,11 @@ libclutter_cogl_la_SOURCES = \ $(srcdir)/cogl-blend-string.c \ $(srcdir)/cogl-blend-string.h \ $(srcdir)/cogl-debug.c \ + $(srcdir)/cogl-sub-texture-private.h \ $(srcdir)/cogl-texture-private.h \ $(srcdir)/cogl-texture-2d-sliced-private.h \ $(srcdir)/cogl-texture-driver.h \ + $(srcdir)/cogl-sub-texture.c \ $(srcdir)/cogl-texture.c \ $(srcdir)/cogl-texture-2d-sliced.c \ $(srcdir)/cogl-spans.h \ diff --git a/cogl/cogl-sub-texture-private.h b/cogl/cogl-sub-texture-private.h new file mode 100644 index 000000000..899f61e8c --- /dev/null +++ b/cogl/cogl-sub-texture-private.h @@ -0,0 +1,56 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_SUB_TEXTURE_H +#define __COGL_SUB_TEXTURE_H + +#include "cogl-handle.h" +#include "cogl-texture-private.h" + +#define COGL_SUB_TEXTURE(tex) ((CoglSubTexture *) tex) + +typedef struct _CoglSubTexture CoglSubTexture; + +struct _CoglSubTexture +{ + CoglTexture _parent; + + CoglHandle full_texture; + + /* The texture coordinates of the subregion of full_texture */ + gfloat tx1, ty1; + gfloat tx2, ty2; + + /* Are all of the texture coordinates a multiple of one? */ + gboolean tex_coords_are_a_multiple; +}; + +GQuark +_cogl_handle_sub_texture_get_type (void); + +CoglHandle +_cogl_sub_texture_new (CoglHandle full_texture, + gfloat tx1, gfloat ty1, + gfloat tx2, gfloat ty2); + +#endif /* __COGL_SUB_TEXTURE_H */ diff --git a/cogl/cogl-sub-texture.c b/cogl/cogl-sub-texture.c new file mode 100644 index 000000000..a8e164160 --- /dev/null +++ b/cogl/cogl-sub-texture.c @@ -0,0 +1,699 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-sub-texture-private.h" +#include "cogl-context.h" +#include "cogl-handle.h" +#include "cogl-texture-driver.h" + +#include +#include + +static void _cogl_sub_texture_free (CoglSubTexture *sub_tex); + +COGL_HANDLE_DEFINE (SubTexture, sub_texture); + +static const CoglTextureVtable cogl_sub_texture_vtable; + +/* Maps from the texture coordinates of this texture to the texture + coordinates of the full texture */ + +static void +_cogl_sub_texture_map_coordinate_pair (CoglSubTexture *sub_tex, + gfloat *tx, gfloat *ty) +{ + *tx = *tx * (sub_tex->tx2 - sub_tex->tx1) + sub_tex->tx1; + *ty = *ty * (sub_tex->ty2 - sub_tex->ty1) + sub_tex->ty1; +} + +static void +_cogl_sub_texture_map_coordinate_set (CoglSubTexture *sub_tex, + gfloat *tx1, gfloat *ty1, + gfloat *tx2, gfloat *ty2) +{ + _cogl_sub_texture_map_coordinate_pair (sub_tex, tx1, ty1); + _cogl_sub_texture_map_coordinate_pair (sub_tex, tx2, ty2); +} + +/* Maps from the texture coordinates of the full texture to the + texture coordinates of the sub texture */ +static void +_cogl_sub_texture_unmap_coordinate_pair (CoglSubTexture *sub_tex, + gfloat *coords) +{ + if (sub_tex->tx1 == sub_tex->tx2) + coords[0] = sub_tex->tx1; + else + coords[0] = (coords[0] - sub_tex->tx1) / (sub_tex->tx2 - sub_tex->tx1); + + if (sub_tex->ty1 == sub_tex->ty2) + coords[0] = sub_tex->ty1; + else + coords[1] = (coords[1] - sub_tex->ty1) / (sub_tex->ty2 - sub_tex->ty1); +} + +static void +_cogl_sub_texture_unmap_coordinate_set (CoglSubTexture *sub_tex, + gfloat *coords) +{ + _cogl_sub_texture_unmap_coordinate_pair (sub_tex, coords); + _cogl_sub_texture_unmap_coordinate_pair (sub_tex, coords + 2); +} + +static gboolean +_cogl_sub_texture_same_int_part (float t1, float t2) +{ + float int_part1, int_part2; + float frac_part1, frac_part2; + + frac_part1 = modff (t1, &int_part1); + frac_part2 = modff (t2, &int_part2); + + return (int_part1 == int_part2 || + ((frac_part1 == 0.0f || frac_part2 == 0.0f) && + ABS (int_part1 - int_part2) == 1.0f)); +} + +typedef struct _CoglSubTextureForeachData +{ + CoglSubTexture *sub_tex; + CoglTextureSliceCallback callback; + void *user_data; +} CoglSubTextureForeachData; + +static void +_cogl_sub_texture_foreach_cb (CoglHandle handle, + GLuint gl_handle, + GLenum gl_target, + const float *slice_coords, + const float *full_virtual_coords, + void *user_data) +{ + CoglSubTextureForeachData *data = user_data; + float virtual_coords[4]; + + memcpy (virtual_coords, full_virtual_coords, sizeof (virtual_coords)); + /* Convert the virtual coords from the full-texture space to the sub + texture space */ + _cogl_sub_texture_unmap_coordinate_set (data->sub_tex, virtual_coords); + + data->callback (handle, gl_handle, gl_target, + slice_coords, virtual_coords, + data->user_data); +} + +static void +_cogl_sub_texture_manual_repeat_cb (const float *coords, + void *user_data) +{ + CoglSubTextureForeachData *data = user_data; + float mapped_coords[4]; + + memcpy (mapped_coords, coords, sizeof (mapped_coords)); + + _cogl_sub_texture_map_coordinate_set (data->sub_tex, + &mapped_coords[0], + &mapped_coords[1], + &mapped_coords[2], + &mapped_coords[3]); + + _cogl_texture_foreach_sub_texture_in_region (data->sub_tex->full_texture, + mapped_coords[0], + mapped_coords[1], + mapped_coords[2], + mapped_coords[3], + _cogl_sub_texture_foreach_cb, + user_data); +} + +static void +_cogl_sub_texture_foreach_sub_texture_in_region ( + CoglTexture *tex, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglTextureSliceCallback callback, + void *user_data) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + CoglSubTextureForeachData data; + + data.sub_tex = sub_tex; + data.callback = callback; + data.user_data = user_data; + + /* If there is no repeating or the sub texture coordinates are a + multiple of the whole texture then we can just directly map the + texture coordinates */ + if (sub_tex->tex_coords_are_a_multiple || + (_cogl_sub_texture_same_int_part (virtual_tx_1, virtual_tx_2) && + _cogl_sub_texture_same_int_part (virtual_ty_1, virtual_ty_2))) + { + _cogl_sub_texture_map_coordinate_set (sub_tex, + &virtual_tx_1, + &virtual_ty_1, + &virtual_tx_2, + &virtual_ty_2); + + _cogl_texture_foreach_sub_texture_in_region + (sub_tex->full_texture, + virtual_tx_1, virtual_ty_1, + virtual_tx_2, virtual_ty_2, + _cogl_sub_texture_foreach_cb, &data); + } + else + _cogl_texture_iterate_manual_repeats (_cogl_sub_texture_manual_repeat_cb, + virtual_tx_1, virtual_ty_1, + virtual_tx_2, virtual_ty_2, + &data); +} + +static void +_cogl_sub_texture_set_wrap_mode_parameter (CoglTexture *tex, + GLenum wrap_mode) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + _cogl_texture_set_wrap_mode_parameter (sub_tex->full_texture, wrap_mode); +} + +static void +_cogl_sub_texture_free (CoglSubTexture *sub_tex) +{ + cogl_handle_unref (sub_tex->full_texture); + + g_free (sub_tex); +} + +CoglHandle +_cogl_sub_texture_new (CoglHandle full_texture, + gfloat tx1, gfloat ty1, + gfloat tx2, gfloat ty2) +{ + CoglSubTexture *sub_tex; + CoglTexture *tex; + gfloat integer_part; + + sub_tex = g_new (CoglSubTexture, 1); + + tex = COGL_TEXTURE (sub_tex); + tex->vtable = &cogl_sub_texture_vtable; + + sub_tex->full_texture = cogl_handle_ref (full_texture); + + sub_tex->tx1 = tx1; + sub_tex->ty1 = ty1; + sub_tex->tx2 = tx2; + sub_tex->ty2 = ty2; + + /* Track whether the texture coords are a multiple of one because in + that case we can use hardware repeating */ + sub_tex->tex_coords_are_a_multiple + = (modff (tx1, &integer_part) == 0.0f && + modff (ty1, &integer_part) == 0.0f && + modff (tx2, &integer_part) == 0.0f && + modff (ty2, &integer_part) == 0.0f); + + return _cogl_sub_texture_handle_new (sub_tex); +} + +static gint +_cogl_sub_texture_get_max_waste (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_get_max_waste (sub_tex->full_texture); +} + +static gboolean +_cogl_sub_texture_is_sliced (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_is_sliced (sub_tex->full_texture); +} + +static gboolean +_cogl_sub_texture_can_hardware_repeat (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + /* We can hardware repeat if the full texture can hardware repeat + and the coordinates for the subregion are all a multiple of the + full size of the texture (ie, they have no fractional part) */ + + return (sub_tex->tex_coords_are_a_multiple && + _cogl_texture_can_hardware_repeat (sub_tex->full_texture)); +} + +static void +_cogl_sub_texture_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + _cogl_sub_texture_map_coordinate_pair (sub_tex, s, t); + _cogl_texture_transform_coords_to_gl (sub_tex->full_texture, s, t); +} + +static gboolean +_cogl_sub_texture_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_get_gl_texture (sub_tex->full_texture, + out_gl_handle, + out_gl_target); +} + +static void +_cogl_sub_texture_set_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + _cogl_texture_set_filters (sub_tex->full_texture, min_filter, mag_filter); +} + +static void +_cogl_sub_texture_ensure_mipmaps (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + _cogl_texture_ensure_mipmaps (sub_tex->full_texture); +} + +static void +_cogl_sub_texture_get_next_chunk (int pos, int end, + int tex_size, + int *chunk_start, int *chunk_end) +{ + /* pos and end may be negative or greater than the size of the + texture. We want to calculate the next largest range we can copy + in one chunk */ + + if (pos < 0) + /* The behaviour of % for negative numbers is implementation + dependant in C89 so we have to do this */ + *chunk_start = (tex_size - pos) % tex_size; + else + *chunk_start = pos % tex_size; + + /* If the region is larger than the remaining size of the texture + then we need to crop it */ + if (end - pos > tex_size - *chunk_start) + end = pos + tex_size - *chunk_start; + + if (end < 0) + *chunk_end = (tex_size - end) % tex_size; + else + *chunk_end = end % tex_size; + + if (*chunk_end == 0) + *chunk_end = tex_size; +} + +static void +_cogl_sub_texture_get_x_pixel_pos (CoglSubTexture *sub_tex, + gint *px1, gint *px2) +{ + gint full_width = cogl_texture_get_width (sub_tex->full_texture); + + *px1 = full_width * sub_tex->tx1; + *px2 = full_width * sub_tex->tx2; + + if (*px1 > *px2) + { + gint temp = *px1; + *px1 = *px2; + *px2 = temp; + } +} + +static void +_cogl_sub_texture_get_y_pixel_pos (CoglSubTexture *sub_tex, + gint *py1, gint *py2) +{ + gint full_width = cogl_texture_get_width (sub_tex->full_texture); + + *py1 = full_width * sub_tex->ty1; + *py2 = full_width * sub_tex->ty2; + + if (*py1 > *py2) + { + gint temp = *py1; + *py1 = *py2; + *py2 = temp; + } +} + +static gboolean +_cogl_sub_texture_set_region (CoglTexture *tex, + 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) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + gint full_tex_width, full_tex_height; + gint bpp; + gint px1, py1, px2, py2; + gint it_x, it_y; + gint src_x1, src_y1, src_x2, src_y2; + CoglBitmap source_bmp; + CoglBitmap temp_bmp; + gboolean source_bmp_owner = FALSE; + CoglPixelFormat closest_format; + GLenum closest_gl_format; + GLenum closest_gl_type; + gboolean success; + CoglPixelFormat tex_format; + + /* 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; + + /* FIXME: If the sub texture coordinates are swapped around then we + should flip the bitmap */ + + _cogl_sub_texture_get_x_pixel_pos (sub_tex, &px1, &px2); + _cogl_sub_texture_get_y_pixel_pos (sub_tex, &py1, &py2); + + full_tex_width = cogl_texture_get_width (sub_tex->full_texture); + full_tex_height = cogl_texture_get_height (sub_tex->full_texture); + + /* Init source bitmap */ + source_bmp.width = width; + source_bmp.height = height; + source_bmp.format = format; + source_bmp.data = (guchar*) data; + + /* Rowstride from texture 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 */ + tex_format = cogl_texture_get_format (sub_tex->full_texture); + closest_format = _cogl_pixel_format_to_gl (tex_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; + } + + for (it_y = py1; it_y < py2; it_y += src_y2 - src_y1) + { + _cogl_sub_texture_get_next_chunk (it_y, py2, full_tex_width, + &src_y1, &src_y2); + + for (it_x = px1; it_x < px2; it_x += src_x2 - src_x1) + { + gint virt_x_1, virt_y_1, virt_width, virt_height; + gint copy_dst_x, copy_dst_y, copy_dst_width, copy_dst_height; + + _cogl_sub_texture_get_next_chunk (it_x, px2, full_tex_height, + &src_x1, &src_x2); + + /* Offset of the chunk from the left edge in the virtual sub + texture coordinates */ + virt_x_1 = it_x - px1; + /* Pixel width covered by this chunk */ + virt_width = src_x2 - src_x1; + /* Offset of the chunk from the top edge in the virtual sub + texture coordinates */ + virt_y_1 = it_y - py1; + /* Pixel height covered by this chunk */ + virt_height = src_y2 - src_y1; + + /* Check if this chunk intersects with the update region */ + if (dst_x + dst_width <= virt_x_1 || + dst_x >= virt_x_1 + virt_width || + dst_y + dst_height <= it_y - py1 || + dst_y >= virt_y_1 + virt_height) + continue; + + /* Calculate the intersection in virtual coordinates */ + copy_dst_width = dst_width; + if (dst_x < virt_x_1) + { + copy_dst_width -= virt_x_1 - dst_x; + copy_dst_x = virt_x_1; + } + else + copy_dst_x = dst_x; + if (copy_dst_width + copy_dst_x > virt_x_1 + virt_width) + copy_dst_width = virt_x_1 + virt_width - copy_dst_x; + + copy_dst_height = dst_height; + if (dst_y < virt_y_1) + { + copy_dst_height -= virt_y_1 - dst_y; + copy_dst_y = virt_y_1; + } + else + copy_dst_y = dst_y; + if (copy_dst_height + copy_dst_y > virt_y_1 + virt_height) + copy_dst_height = virt_y_1 + virt_height - copy_dst_y; + + /* Update the region in the full texture */ + cogl_texture_set_region (sub_tex->full_texture, + src_x + copy_dst_x - dst_x, + src_y + copy_dst_y - dst_y, + src_x1 + copy_dst_x - virt_x_1, + src_y1 + copy_dst_y - virt_y_1, + copy_dst_width, + copy_dst_height, + width, + height, + format, + rowstride, + data); + } + } + + /* Free data if owner */ + if (source_bmp_owner) + g_free (source_bmp.data); + + return TRUE; +} + +static void +_cogl_sub_texture_copy_region (guchar *dst, + const guchar *src, + gint dst_x, gint dst_y, + gint src_x, gint src_y, + gint width, gint height, + gint dst_rowstride, + gint src_rowstride, + gint bpp) +{ + int y; + + dst += dst_x * bpp + dst_y * dst_rowstride; + src += src_x * bpp + src_y * src_rowstride; + + for (y = 0; y < height; y++) + { + memcpy (dst, src, bpp * width); + dst += dst_rowstride; + src += src_rowstride; + } +} + +static int +_cogl_sub_texture_get_data (CoglTexture *tex, + CoglPixelFormat format, + unsigned int rowstride, + guint8 *data) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + unsigned int full_rowstride; + guint8 *full_data; + int byte_size, full_size; + gint bpp; + gint px1, py1, px2, py2; + gint full_tex_width, full_tex_height; + + /* FIXME: This gets the full data from the full texture and then + copies a subregion of that. It would be better if there was a + texture_get_sub_data virtual and it can just munge the texture + coordinates */ + + /* Default to internal format if none specified */ + if (format == COGL_PIXEL_FORMAT_ANY) + format = cogl_texture_get_format (sub_tex->full_texture); + + _cogl_sub_texture_get_x_pixel_pos (sub_tex, &px1, &px2); + _cogl_sub_texture_get_y_pixel_pos (sub_tex, &py1, &py2); + + full_tex_width = cogl_texture_get_width (sub_tex->full_texture); + full_tex_height = cogl_texture_get_height (sub_tex->full_texture); + + /* Rowstride from texture width if none specified */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) + rowstride = px2 - px1; + + /* Return byte size if only that requested */ + byte_size = (py2 - py1) * rowstride; + if (data == NULL) + return byte_size; + + full_rowstride = _cogl_get_format_bpp (format) * full_tex_width; + full_data = g_malloc (full_rowstride * full_tex_height); + + full_size = cogl_texture_get_data (sub_tex->full_texture, format, + full_rowstride, full_data); + + if (full_size) + { + int dst_x, dst_y; + int src_x1, src_y1; + int src_x2, src_y2; + + for (dst_y = py1; dst_y < py2; dst_y += src_y2 - src_y1) + { + _cogl_sub_texture_get_next_chunk (dst_y, py2, full_tex_width, + &src_y1, &src_y2); + + for (dst_x = px1; dst_x < px2; dst_x += src_x2 - src_x1) + { + _cogl_sub_texture_get_next_chunk (dst_x, px2, full_tex_height, + &src_x1, &src_x2); + + _cogl_sub_texture_copy_region (data, full_data, + dst_x - px1, dst_y - py1, + src_x1, src_y1, + src_x2 - src_x1, + src_y2 - src_y1, + rowstride, + full_rowstride, + bpp); + } + } + } + else + byte_size = 0; + + g_free (full_data); + + return byte_size; +} + +static CoglPixelFormat +_cogl_sub_texture_get_format (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_get_format (sub_tex->full_texture); +} + +static GLenum +_cogl_sub_texture_get_gl_format (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return _cogl_texture_get_gl_format (sub_tex->full_texture); +} + +static gint +_cogl_sub_texture_get_width (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + gint px1, px2; + + _cogl_sub_texture_get_x_pixel_pos (sub_tex, &px1, &px2); + + return px2 - px1; +} + +static gint +_cogl_sub_texture_get_height (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + gint py1, py2; + + _cogl_sub_texture_get_y_pixel_pos (sub_tex, &py1, &py2); + + return py2 - py1; +} + +static const CoglTextureVtable +cogl_sub_texture_vtable = + { + _cogl_sub_texture_set_region, + _cogl_sub_texture_get_data, + _cogl_sub_texture_foreach_sub_texture_in_region, + _cogl_sub_texture_get_max_waste, + _cogl_sub_texture_is_sliced, + _cogl_sub_texture_can_hardware_repeat, + _cogl_sub_texture_transform_coords_to_gl, + _cogl_sub_texture_get_gl_texture, + _cogl_sub_texture_set_filters, + _cogl_sub_texture_ensure_mipmaps, + _cogl_sub_texture_set_wrap_mode_parameter, + _cogl_sub_texture_get_format, + _cogl_sub_texture_get_gl_format, + _cogl_sub_texture_get_width, + _cogl_sub_texture_get_height + }; diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index e4eff2e06..5fc0892cb 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -38,6 +38,7 @@ #include "cogl-texture-private.h" #include "cogl-texture-driver.h" #include "cogl-texture-2d-sliced-private.h" +#include "cogl-sub-texture-private.h" #include "cogl-material.h" #include "cogl-context.h" #include "cogl-handle.h" @@ -62,8 +63,8 @@ cogl_is_texture (CoglHandle handle) 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 (); + return (obj->klass->type == _cogl_handle_texture_2d_sliced_get_type () || + obj->klass->type == _cogl_handle_sub_texture_get_type ()); } CoglHandle @@ -367,6 +368,16 @@ cogl_texture_new_from_foreign (GLuint gl_handle, format); } +CoglHandle +cogl_texture_new_from_sub_texture (CoglHandle full_texture, + gfloat tx1, + gfloat ty1, + gfloat tx2, + gfloat ty2) +{ + return _cogl_sub_texture_new (full_texture, tx1, ty1, tx2, ty2); +} + guint cogl_texture_get_width (CoglHandle handle) { diff --git a/cogl/cogl-texture.h b/cogl/cogl-texture.h index b831d1ae1..5e4910f31 100644 --- a/cogl/cogl-texture.h +++ b/cogl/cogl-texture.h @@ -316,6 +316,33 @@ gboolean cogl_texture_set_region (CoglHandle handle, guint rowstride, const guchar *data); +/** + * cogl_texture_new_from_sub_texture: + * @full_texture: a #CoglHandle to an existing texture + * @tx1: X coordinate of the top-left of the subregion + * @ty1: Y coordinate of the top-left of the subregion + * @tx2: X coordinate of the bottom-right of the subregion + * @ty2: Y coordinate of the bottom-right of the subregion + * + * Creates a new texture which represents a subregion of another + * texture. The GL resources will be shared so that no new texture + * data is actually allocated. + * + * You can also specify texture coordinates outside the range of [0,1] + * to make a texture that represents a repeated version of another + * texture. + * + * Return value: a #CoglHandle to the new texture. + * + * Since: 1.2 + */ +CoglHandle cogl_texture_new_from_sub_texture + (CoglHandle full_texture, + gfloat tx1, + gfloat ty1, + gfloat tx2, + gfloat ty2); + #ifndef COGL_DISABLE_DEPRECATED /** diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index 336f9ad03..eda10b462 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -192,6 +192,7 @@ cogl_texture_new_from_file cogl_texture_new_from_data cogl_texture_new_from_foreign cogl_texture_new_from_bitmap +cogl_texture_new_from_sub_texture cogl_is_texture cogl_texture_ref cogl_texture_unref From 8753422ef53608b90cef24e35dee10b01dede14e Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 27 Nov 2009 16:59:51 +0000 Subject: [PATCH 07/33] cogl: Move some of the texture_2d_sliced_new_* functions into cogl-texture new_from_data and new_from_file can be implemented in terms of new_from_bitmap so it makes sense to move these to cogl-texture rather than having to implement them in every texture backend. --- cogl/cogl-texture-2d-sliced-private.h | 16 ----- cogl/cogl-texture-2d-sliced.c | 84 --------------------------- cogl/cogl-texture.c | 44 ++++++++++---- 3 files changed, 33 insertions(+), 111 deletions(-) diff --git a/cogl/cogl-texture-2d-sliced-private.h b/cogl/cogl-texture-2d-sliced-private.h index 70eba4a97..dfde144ac 100644 --- a/cogl/cogl-texture-2d-sliced-private.h +++ b/cogl/cogl-texture-2d-sliced-private.h @@ -84,12 +84,6 @@ _cogl_texture_2d_sliced_new_with_size (unsigned int width, 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, @@ -99,16 +93,6 @@ _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, 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, diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c index 5f0900d4b..0f53bc1dc 100644 --- a/cogl/cogl-texture-2d-sliced.c +++ b/cogl/cogl-texture-2d-sliced.c @@ -992,67 +992,6 @@ _cogl_texture_2d_sliced_new_with_size (unsigned int width, 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; - CoglTextureUploadData upload_data; - - if (format == COGL_PIXEL_FORMAT_ANY) - return COGL_INVALID_HANDLE; - - if (data == NULL) - return COGL_INVALID_HANDLE; - - /* Rowstride from width if not given */ - if (rowstride == 0) - rowstride = width * _cogl_get_format_bpp (format); - - /* Create new texture and fill with given data */ - tex_2ds = g_new0 (CoglTexture2DSliced, 1); - - tex = COGL_TEXTURE (tex_2ds); - tex->vtable = &cogl_texture_2d_sliced_vtable; - - upload_data.bitmap.width = width; - upload_data.bitmap.height = height; - upload_data.bitmap.data = (guchar*)data; - upload_data.bitmap.format = format; - upload_data.bitmap.rowstride = rowstride; - upload_data.bitmap_owner = FALSE; - - if (flags & COGL_TEXTURE_NO_SLICING) - tex_2ds->max_waste = -1; - else - tex_2ds->max_waste = COGL_TEXTURE_MAX_WASTE; - - /* 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_2d_sliced_upload_from_data (tex_2ds, &upload_data, - internal_format)) - { - _cogl_texture_2d_sliced_free (tex_2ds); - _cogl_texture_upload_data_free (&upload_data); - return COGL_INVALID_HANDLE; - } - - _cogl_texture_upload_data_free (&upload_data); - - tex_2ds->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; - - return _cogl_texture_2d_sliced_handle_new (tex_2ds); -} - CoglHandle _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, CoglTextureFlags flags, @@ -1101,29 +1040,6 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, 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, diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 5fc0892cb..516b23002 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -319,13 +319,26 @@ cogl_texture_new_from_data (guint width, guint rowstride, const guchar *data) { - return _cogl_texture_2d_sliced_new_from_data (width, - height, - flags, - format, - internal_format, - rowstride, - data); + CoglBitmap bitmap; + + if (format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + if (data == NULL) + return COGL_INVALID_HANDLE; + + /* Rowstride from width if not given */ + if (rowstride == 0) + rowstride = width * _cogl_get_format_bpp (format); + + /* Wrap the data into a bitmap */ + bitmap.width = width; + bitmap.height = height; + bitmap.data = (guchar *) data; + bitmap.format = format; + bitmap.rowstride = rowstride; + + return cogl_texture_new_from_bitmap (&bitmap, flags, internal_format); } CoglHandle @@ -344,10 +357,19 @@ cogl_texture_new_from_file (const gchar *filename, CoglPixelFormat internal_format, GError **error) { - return _cogl_texture_2d_sliced_new_from_file (filename, - flags, - internal_format, - 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; } CoglHandle From f17767e4c105d1d172d9496646ec22105e107e31 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 30 Nov 2009 12:15:05 +0000 Subject: [PATCH 08/33] cogl: Add _cogl_texture_driver_upload_to_gl This provides a way to upload the entire data for a texture without having to first call glTexImage and then glTexSubImage. This should be faster especially with indirect rendering where it would needlessy send the data for the texture twice. --- cogl/cogl-texture-driver.h | 14 ++++++++ cogl/driver/gl/cogl-texture-driver.c | 26 ++++++++++++++ cogl/driver/gles/cogl-texture-driver.c | 47 ++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/cogl/cogl-texture-driver.h b/cogl/cogl-texture-driver.h index 6626d18ff..6961659d8 100644 --- a/cogl/cogl-texture-driver.h +++ b/cogl/cogl-texture-driver.h @@ -69,6 +69,20 @@ _cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target, GLuint source_gl_format, GLuint source_gl_type); +/* + * Replaces the contents of the GL texture with the entire bitmap. On + * GL this just directly calls glTexImage2D, but under GLES it needs + * to copy the bitmap if the rowstride is not a multiple of a possible + * alignment value because there is no GL_UNPACK_ROW_LENGTH + */ +void +_cogl_texture_driver_upload_to_gl (GLenum gl_target, + GLuint gl_handle, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type); + /* * This sets up the glPixelStore state for an download to a destination with * the same size, and with no offset. diff --git a/cogl/driver/gl/cogl-texture-driver.c b/cogl/driver/gl/cogl-texture-driver.c index 024ce9b8a..144474f23 100644 --- a/cogl/driver/gl/cogl-texture-driver.c +++ b/cogl/driver/gl/cogl-texture-driver.c @@ -134,6 +134,32 @@ _cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target, source_bmp->data) ); } +void +_cogl_texture_driver_upload_to_gl (GLenum gl_target, + GLuint gl_handle, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type) +{ + int bpp = _cogl_get_format_bpp (source_bmp->format); + + /* Setup gl alignment to match rowstride and top-left corner */ + prep_gl_for_pixels_upload_full (source_bmp->rowstride, 0, 0, bpp); + + /* We don't need to use _cogl_texture_driver_bind here because we're + not using the bound texture to render yet */ + GE( glBindTexture (gl_target, gl_handle) ); + + GE( glTexImage2D (gl_target, 0, + internal_gl_format, + source_bmp->width, source_bmp->height, + 0, + source_gl_format, + source_gl_type, + source_bmp->data) ); +} + gboolean _cogl_texture_driver_gl_get_tex_image (GLenum gl_target, GLenum dest_gl_format, diff --git a/cogl/driver/gles/cogl-texture-driver.c b/cogl/driver/gles/cogl-texture-driver.c index 42e5af27d..62324f58c 100644 --- a/cogl/driver/gles/cogl-texture-driver.c +++ b/cogl/driver/gles/cogl-texture-driver.c @@ -128,6 +128,53 @@ _cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target, g_free (slice_bmp.data); } +void +_cogl_texture_driver_upload_to_gl (GLenum gl_target, + GLuint gl_handle, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type) +{ + int bpp = _cogl_get_format_bpp (source_bmp->format); + CoglBitmap bmp = *source_bmp; + gboolean bmp_owner = FALSE; + + /* If the rowstride can't be specified with just GL_ALIGNMENT alone + then we need to copy the bitmap because there is no GL_ROW_LENGTH */ + if (source_bmp->rowstride / bpp != source_bmp->width) + { + bmp.rowstride = bpp * bmp.width; + bmp.data = g_malloc (bmp.rowstride * bmp.height); + bmp_owner = TRUE; + + _cogl_bitmap_copy_subregion (source_bmp, + &bmp, + 0, 0, 0, 0, + bmp.width, + bmp.height); + } + + /* Setup gl alignment to match rowstride and top-left corner */ + _cogl_texture_driver_prep_gl_for_pixels_upload (bmp.rowstride, + bpp); + + /* We don't need to use _cogl_texture_driver_bind here because we're + not using the bound texture to render yet */ + GE( glBindTexture (gl_target, gl_handle) ); + + GE( glTexImage2D (gl_target, 0, + internal_gl_format, + bmp.width, bmp.height, + 0, + source_gl_format, + source_gl_type, + bmp.data) ); + + if (bmp_owner) + g_free (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() ) */ From 05d42ed390232b14a40511bae985c2f58a474859 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 27 Nov 2009 18:45:36 +0000 Subject: [PATCH 09/33] cogl: Add a CoglTexture2D backend This is an optimised version of CoglTexture2DSliced that always deals with a single texture and always uses the GL_TEXTURE_2D target. cogl_texture_new_from_bitmap now tries to use this backend first. If it can't create a texture with that size then it falls back the sliced backend. cogl_texture_upload_data_prepare has been split into two functions because the sliced backend needs to know the real internal format before the conversion is performed. Otherwise the converted bitmap will be wasted if the backend can't support the size. --- cogl/Makefile.am | 2 + cogl/cogl-texture-2d-private.h | 69 ++++ cogl/cogl-texture-2d.c | 623 +++++++++++++++++++++++++++++++++ cogl/cogl-texture-private.h | 9 + cogl/cogl-texture.c | 75 ++-- 5 files changed, 755 insertions(+), 23 deletions(-) create mode 100644 cogl/cogl-texture-2d-private.h create mode 100644 cogl/cogl-texture-2d.c diff --git a/cogl/Makefile.am b/cogl/Makefile.am index 02ab16601..995453832 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -124,10 +124,12 @@ libclutter_cogl_la_SOURCES = \ $(srcdir)/cogl-debug.c \ $(srcdir)/cogl-sub-texture-private.h \ $(srcdir)/cogl-texture-private.h \ + $(srcdir)/cogl-texture-2d-private.h \ $(srcdir)/cogl-texture-2d-sliced-private.h \ $(srcdir)/cogl-texture-driver.h \ $(srcdir)/cogl-sub-texture.c \ $(srcdir)/cogl-texture.c \ + $(srcdir)/cogl-texture-2d.c \ $(srcdir)/cogl-texture-2d-sliced.c \ $(srcdir)/cogl-spans.h \ $(srcdir)/cogl-spans.c \ diff --git a/cogl/cogl-texture-2d-private.h b/cogl/cogl-texture-2d-private.h new file mode 100644 index 000000000..69910db66 --- /dev/null +++ b/cogl/cogl-texture-2d-private.h @@ -0,0 +1,69 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_TEXTURE_2D_H +#define __COGL_TEXTURE_2D_H + +#include "cogl-handle.h" +#include "cogl-material-private.h" +#include "cogl-texture-private.h" + +#define COGL_TEXTURE_2D(tex) ((CoglTexture2D *) tex) + +typedef struct _CoglTexture2D CoglTexture2D; + +struct _CoglTexture2D +{ + CoglTexture _parent; + + /* The internal format of the GL texture represented as a + CoglPixelFormat */ + CoglPixelFormat format; + /* The internal format of the GL texture represented as a GL enum */ + GLenum gl_format; + /* The texture object number */ + GLuint gl_texture; + gint width; + gint height; + GLenum min_filter; + GLenum mag_filter; + GLint wrap_mode; + gboolean auto_mipmap; + gboolean mipmaps_dirty; +}; + +GQuark +_cogl_handle_texture_2d_get_type (void); + +CoglHandle +_cogl_texture_2d_new_with_size (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat internal_format); + +CoglHandle +_cogl_texture_2d_new_from_bitmap (CoglHandle bmp_handle, + CoglTextureFlags flags, + CoglPixelFormat internal_format); + +#endif /* __COGL_TEXTURE_2D_H */ diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c new file mode 100644 index 000000000..e605bb73e --- /dev/null +++ b/cogl/cogl-texture-2d.c @@ -0,0 +1,623 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-texture-driver.h" +#include "cogl-context.h" +#include "cogl-handle.h" + +#include +#include + +static void _cogl_texture_2d_free (CoglTexture2D *tex_2d); + +COGL_HANDLE_DEFINE (Texture2D, texture_2d); + +static const CoglTextureVtable cogl_texture_2d_vtable; + +typedef struct _CoglTexture2DManualRepeatData +{ + CoglTexture2D *tex_2d; + CoglTextureSliceCallback callback; + void *user_data; +} CoglTexture2DManualRepeatData; + +static void +_cogl_texture_2d_wrap_coords (float t_1, float t_2, + float *out_t_1, float *out_t_2) +{ + float int_part; + + /* Wrap t_1 and t_2 to the range [0,1] */ + + modff (t_1 < t_2 ? t_1 : t_2, &int_part); + t_1 -= int_part; + t_2 -= int_part; + if (signbit (int_part)) + { + *out_t_1 = 1.0f - t_1; + *out_t_2 = 1.0f - t_2; + } + else + { + *out_t_1 = t_1; + *out_t_2 = t_2; + } +} + +static void +_cogl_texture_2d_manual_repeat_cb (const float *coords, + void *user_data) +{ + CoglTexture2DManualRepeatData *data = user_data; + float slice_coords[4]; + + _cogl_texture_2d_wrap_coords (coords[0], coords[2], + slice_coords + 0, slice_coords + 2); + _cogl_texture_2d_wrap_coords (coords[1], coords[3], + slice_coords + 1, slice_coords + 3); + + data->callback (COGL_TEXTURE (data->tex_2d), + data->tex_2d->gl_texture, + GL_TEXTURE_2D, + slice_coords, + coords, + data->user_data); +} + +static void +_cogl_texture_2d_foreach_sub_texture_in_region ( + CoglTexture *tex, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglTextureSliceCallback callback, + void *user_data) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + CoglTexture2DManualRepeatData data; + + data.tex_2d = tex_2d; + data.callback = callback; + data.user_data = user_data; + + /* We need to implement manual repeating because if Cogl is calling + this function then it will set the wrap mode to GL_CLAMP_TO_EDGE + and hardware repeating can't be done */ + _cogl_texture_iterate_manual_repeats (_cogl_texture_2d_manual_repeat_cb, + virtual_tx_1, virtual_ty_1, + virtual_tx_2, virtual_ty_2, + &data); +} + +static void +_cogl_texture_2d_set_wrap_mode_parameter (CoglTexture *tex, + GLenum wrap_mode) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + /* Only set the wrap mode if it's different from the current + value to avoid too many GL calls */ + if (tex_2d->wrap_mode != wrap_mode) + { + /* Any queued texture rectangles may be depending on the + * previous wrap mode... */ + _cogl_journal_flush (); + + GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) ); + GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode) ); + GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode) ); + + tex_2d->wrap_mode = wrap_mode; + } +} + +static void +_cogl_texture_2d_free (CoglTexture2D *tex_2d) +{ + GE( glDeleteTextures (1, &tex_2d->gl_texture) ); + g_free (tex_2d); +} + +static gboolean +_cogl_texture_2d_is_pot (unsigned int num) +{ + gboolean have_bit = FALSE; + + /* Make sure there is only one bit set */ + while (num) + { + if (num & 1) + { + if (have_bit) + return FALSE; + have_bit = TRUE; + } + num >>= 1; + } + + return TRUE; +} + +static gboolean +_cogl_texture_2d_can_create (unsigned int width, + unsigned int height, + CoglPixelFormat internal_format) +{ + GLenum gl_intformat; + GLenum gl_type; + + /* If the driver doesn't support glGenerateMipmap then we need to + store a copy of the first pixels to cause an update. Instead of + duplicating the code here we'll just make it fallback to + CoglTexture2DSliced */ + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN)) + return FALSE; + + /* If NPOT textures aren't supported then the size must be a power + of two */ + if (!cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) && + (!_cogl_texture_2d_is_pot (width) || + !_cogl_texture_2d_is_pot (height))) + return FALSE; + + _cogl_pixel_format_to_gl (internal_format, + &gl_intformat, + NULL, + &gl_type); + + /* Check that the driver can create a texture with that size */ + if (!_cogl_texture_driver_size_supported (GL_TEXTURE_2D, + gl_intformat, + gl_type, + width, + height)) + return FALSE; + + return TRUE; +} + +static CoglTexture2D * +_cogl_texture_2d_create_base (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglTexture2D *tex_2d = g_new (CoglTexture2D, 1); + CoglTexture *tex = COGL_TEXTURE (tex_2d); + + tex->vtable = &cogl_texture_2d_vtable; + + tex_2d->width = width; + tex_2d->height = height; + tex_2d->mipmaps_dirty = TRUE; + tex_2d->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; + + /* Unknown filter */ + tex_2d->min_filter = GL_FALSE; + tex_2d->mag_filter = GL_FALSE; + + /* Wrap mode not yet set */ + tex_2d->wrap_mode = GL_FALSE; + + tex_2d->format = internal_format; + + return tex_2d; +} + +CoglHandle +_cogl_texture_2d_new_with_size (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglTexture2D *tex_2d; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + /* Since no data, we need some internal format */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; + + if (!_cogl_texture_2d_can_create (width, height, internal_format)) + return COGL_INVALID_HANDLE; + + internal_format = _cogl_pixel_format_to_gl (internal_format, + &gl_intformat, + &gl_format, + &gl_type); + + tex_2d = _cogl_texture_2d_create_base (width, height, flags, internal_format); + + GE( glGenTextures (1, &tex_2d->gl_texture) ); + GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) ); + GE( glTexImage2D (GL_TEXTURE_2D, 0, gl_intformat, + width, height, 0, gl_format, gl_type, NULL) ); + + return _cogl_texture_2d_handle_new (tex_2d); +} + +CoglHandle +_cogl_texture_2d_new_from_bitmap (CoglHandle bmp_handle, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglTexture2D *tex_2d; + CoglBitmap *bmp = (CoglBitmap *)bmp_handle; + CoglTextureUploadData upload_data; + + g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); + + upload_data.bitmap = *bmp; + upload_data.bitmap_owner = FALSE; + + if (!_cogl_texture_upload_data_prepare_format (&upload_data, + &internal_format) || + !_cogl_texture_2d_can_create (upload_data.bitmap.width, + upload_data.bitmap.height, + internal_format) || + !_cogl_texture_upload_data_convert (&upload_data, internal_format)) + { + _cogl_texture_upload_data_free (&upload_data); + return COGL_INVALID_HANDLE; + } + + tex_2d = _cogl_texture_2d_create_base (upload_data.bitmap.width, + upload_data.bitmap.height, + flags, + upload_data.bitmap.format); + + GE( glGenTextures (1, &tex_2d->gl_texture) ); + _cogl_texture_driver_upload_to_gl (GL_TEXTURE_2D, + tex_2d->gl_texture, + &upload_data.bitmap, + upload_data.gl_intformat, + upload_data.gl_format, + upload_data.gl_type); + + tex_2d->gl_format = upload_data.gl_intformat; + + _cogl_texture_upload_data_free (&upload_data); + + return _cogl_texture_2d_handle_new (tex_2d); +} + +static gint +_cogl_texture_2d_get_max_waste (CoglTexture *tex) +{ + return -1; +} + +static gboolean +_cogl_texture_2d_is_sliced (CoglTexture *tex) +{ + return FALSE; +} + +static gboolean +_cogl_texture_2d_can_hardware_repeat (CoglTexture *tex) +{ + return TRUE; +} + +static void +_cogl_texture_2d_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + /* The texture coordinates map directly so we don't need to do + anything */ +} + +static gboolean +_cogl_texture_2d_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + if (out_gl_handle) + *out_gl_handle = tex_2d->gl_texture; + + if (out_gl_target) + *out_gl_target = GL_TEXTURE_2D; + + return TRUE; +} + +static void +_cogl_texture_2d_set_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + if (min_filter == tex_2d->min_filter + && mag_filter == tex_2d->mag_filter) + return; + + /* Store new values */ + tex_2d->min_filter = min_filter; + tex_2d->mag_filter = mag_filter; + + /* Apply new filters to the texture */ + GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) ); + GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter) ); + GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter) ); +} + +static void +_cogl_texture_2d_ensure_mipmaps (CoglTexture *tex) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Only update if the mipmaps are dirty */ + if (!tex_2d->auto_mipmap || !tex_2d->mipmaps_dirty) + return; + + GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) ); + /* glGenerateMipmap is defined in the FBO extension. We only allow + CoglTexture2D instances to be created if this feature is + available so we don't need to check for the extension */ + _cogl_texture_driver_gl_generate_mipmaps (GL_TEXTURE_2D); + + tex_2d->mipmaps_dirty = FALSE; +} + +static gboolean +_cogl_texture_2d_set_region (CoglTexture *tex, + 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) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + gint bpp; + CoglBitmap source_bmp; + CoglBitmap temp_bmp; + gboolean source_bmp_owner = FALSE; + CoglPixelFormat closest_format; + GLenum closest_gl_format; + GLenum closest_gl_type; + gboolean success; + + /* Check 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_2d->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_driver_upload_subregion_to_gl (GL_TEXTURE_2D, + tex_2d->gl_texture, + 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; +} + +static int +_cogl_texture_2d_get_data (CoglTexture *tex, + CoglPixelFormat format, + unsigned int rowstride, + guint8 *data) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (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; + + /* Default to internal format if none specified */ + if (format == COGL_PIXEL_FORMAT_ANY) + format = tex_2d->format; + + /* Rowstride from texture width if none specified */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = tex_2d->width * bpp; + + /* Return byte size if only that requested */ + byte_size = tex_2d->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); + + target_bmp.width = tex_2d->width; + target_bmp.height = tex_2d->height; + + /* Is the requested format supported? */ + if (closest_format == format) + { + /* Target user data directly */ + target_bmp.format = format; + target_bmp.rowstride = rowstride; + target_bmp.data = data; + } + else + { + /* Target intermediate buffer */ + 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); + } + + GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) ); + if (!_cogl_texture_driver_gl_get_tex_image (GL_TEXTURE_2D, + closest_gl_format, + closest_gl_type, + target_bmp.data)) + { + /* XXX: In some cases _cogl_texture_2d_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; +} + +static CoglPixelFormat +_cogl_texture_2d_get_format (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->format; +} + +static GLenum +_cogl_texture_2d_get_gl_format (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->gl_format; +} + +static gint +_cogl_texture_2d_get_width (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->width; +} + +static gint +_cogl_texture_2d_get_height (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->height; +} + +static const CoglTextureVtable +cogl_texture_2d_vtable = + { + _cogl_texture_2d_set_region, + _cogl_texture_2d_get_data, + _cogl_texture_2d_foreach_sub_texture_in_region, + _cogl_texture_2d_get_max_waste, + _cogl_texture_2d_is_sliced, + _cogl_texture_2d_can_hardware_repeat, + _cogl_texture_2d_transform_coords_to_gl, + _cogl_texture_2d_get_gl_texture, + _cogl_texture_2d_set_filters, + _cogl_texture_2d_ensure_mipmaps, + _cogl_texture_2d_set_wrap_mode_parameter, + _cogl_texture_2d_get_format, + _cogl_texture_2d_get_gl_format, + _cogl_texture_2d_get_width, + _cogl_texture_2d_get_height + }; diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index ee018faf4..ad6449d6d 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -162,6 +162,15 @@ void _cogl_texture_upload_data_swap_bitmap (CoglTextureUploadData *data, CoglBitmap *new_bitmap); +gboolean +_cogl_texture_upload_data_prepare_format + (CoglTextureUploadData *data, + CoglPixelFormat *internal_format); + +gboolean +_cogl_texture_upload_data_convert (CoglTextureUploadData *data, + CoglPixelFormat internal_format); + gboolean _cogl_texture_upload_data_prepare (CoglTextureUploadData *data, CoglPixelFormat internal_format); diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 516b23002..5b94b1dbe 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -38,6 +38,7 @@ #include "cogl-texture-private.h" #include "cogl-texture-driver.h" #include "cogl-texture-2d-sliced-private.h" +#include "cogl-texture-2d-private.h" #include "cogl-sub-texture-private.h" #include "cogl-material.h" #include "cogl-context.h" @@ -63,7 +64,8 @@ cogl_is_texture (CoglHandle handle) if (handle == COGL_INVALID_HANDLE) return FALSE; - return (obj->klass->type == _cogl_handle_texture_2d_sliced_get_type () || + return (obj->klass->type == _cogl_handle_texture_2d_get_type () || + obj->klass->type == _cogl_handle_texture_2d_sliced_get_type () || obj->klass->type == _cogl_handle_sub_texture_get_type ()); } @@ -154,37 +156,44 @@ _cogl_texture_set_wrap_mode_parameter (CoglHandle handle, } gboolean -_cogl_texture_upload_data_prepare (CoglTextureUploadData *data, - CoglPixelFormat internal_format) +_cogl_texture_upload_data_prepare_format + (CoglTextureUploadData *data, + CoglPixelFormat *internal_format) { - CoglBitmap new_bitmap; - CoglPixelFormat new_data_format; - gboolean success; - /* Was there any internal conversion requested? * By default Cogl will use a premultiplied internal format. Later we will * add control over this. */ - if (internal_format == COGL_PIXEL_FORMAT_ANY) + if (*internal_format == COGL_PIXEL_FORMAT_ANY) { if ((data->bitmap.format & COGL_A_BIT) && data->bitmap.format != COGL_PIXEL_FORMAT_A_8) - internal_format = data->bitmap.format | COGL_PREMULT_BIT; + *internal_format = data->bitmap.format | COGL_PREMULT_BIT; else - internal_format = data->bitmap.format; + *internal_format = data->bitmap.format; } /* Find closest format accepted by GL */ - new_data_format = _cogl_pixel_format_to_gl (internal_format, - &data->gl_intformat, - &data->gl_format, - &data->gl_type); + *internal_format = _cogl_pixel_format_to_gl (*internal_format, + &data->gl_intformat, + &data->gl_format, + &data->gl_type); + + return TRUE; +} + +gboolean +_cogl_texture_upload_data_convert (CoglTextureUploadData *data, + CoglPixelFormat internal_format) +{ + CoglBitmap new_bitmap; + gboolean success; /* Convert to internal format */ - if (new_data_format != data->bitmap.format) + if (internal_format != data->bitmap.format) { success = _cogl_bitmap_convert_and_premult (&data->bitmap, &new_bitmap, - new_data_format); + internal_format); if (!success) return FALSE; @@ -304,10 +313,19 @@ cogl_texture_new_with_size (guint width, CoglTextureFlags flags, CoglPixelFormat internal_format) { - return _cogl_texture_2d_sliced_new_with_size (width, - height, - flags, - internal_format); + CoglHandle tex; + + /* First try creating a fast-path non-sliced texture */ + tex = _cogl_texture_2d_new_with_size (width, height, flags, internal_format); + + /* If it fails resort to sliced textures */ + if (tex == COGL_INVALID_HANDLE) + tex = _cogl_texture_2d_sliced_new_with_size (width, + height, + flags, + internal_format); + + return tex; } CoglHandle @@ -346,9 +364,20 @@ cogl_texture_new_from_bitmap (CoglHandle bmp_handle, CoglTextureFlags flags, CoglPixelFormat internal_format) { - return _cogl_texture_2d_sliced_new_from_bitmap (bmp_handle, - flags, - internal_format); + CoglHandle tex; + + /* First try creating a fast-path non-sliced texture */ + tex = _cogl_texture_2d_new_from_bitmap (bmp_handle, + flags, + internal_format); + + /* If it fails resort to sliced textures */ + if (tex == COGL_INVALID_HANDLE) + tex = _cogl_texture_2d_sliced_new_from_bitmap (bmp_handle, + flags, + internal_format); + + return tex; } CoglHandle From 63c984f193739622b2f1523604d841227eeea63e Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 3 Dec 2009 14:46:20 +0000 Subject: [PATCH 10/33] cogl-sub-texture: Fix the height of sub textures The code which is used to get the texture height was accidentally using the width due to a cut-and-paste fail. --- cogl/cogl-sub-texture.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cogl/cogl-sub-texture.c b/cogl/cogl-sub-texture.c index a8e164160..d0213fe88 100644 --- a/cogl/cogl-sub-texture.c +++ b/cogl/cogl-sub-texture.c @@ -370,10 +370,10 @@ static void _cogl_sub_texture_get_y_pixel_pos (CoglSubTexture *sub_tex, gint *py1, gint *py2) { - gint full_width = cogl_texture_get_width (sub_tex->full_texture); + gint full_height = cogl_texture_get_height (sub_tex->full_texture); - *py1 = full_width * sub_tex->ty1; - *py2 = full_width * sub_tex->ty2; + *py1 = full_height * sub_tex->ty1; + *py2 = full_height * sub_tex->ty2; if (*py1 > *py2) { From bec2600087dc127619688a593c41e60633f4cba5 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 4 Dec 2009 13:06:32 +0000 Subject: [PATCH 11/33] cogl: Add an atlased texture backend This adds a CoglAtlas type which is a data structure that keeps track of unused sub rectangles of a larger rectangle. There is a new atlased texture backend which uses this to put multiple textures into a single larger texture. Currently the atlas is always sized 256x256 and the textures are never moved once they are put in. Eventually it needs to be able to reorganise the atlas and grow it if necessary. It also needs to migrate the textures out of the atlas if mipmaps are required. --- cogl/Makefile.am | 4 + cogl/cogl-atlas-texture-private.h | 64 ++++ cogl/cogl-atlas-texture.c | 488 ++++++++++++++++++++++++++++ cogl/cogl-atlas.c | 520 ++++++++++++++++++++++++++++++ cogl/cogl-atlas.h | 76 +++++ cogl/cogl-context.c | 14 + cogl/cogl-context.h | 8 + cogl/cogl-texture.c | 26 +- cogl/cogl-types.h | 3 +- 9 files changed, 1192 insertions(+), 11 deletions(-) create mode 100644 cogl/cogl-atlas-texture-private.h create mode 100644 cogl/cogl-atlas-texture.c create mode 100644 cogl/cogl-atlas.c create mode 100644 cogl/cogl-atlas.h diff --git a/cogl/Makefile.am b/cogl/Makefile.am index 995453832..d2a4c488e 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -131,6 +131,10 @@ libclutter_cogl_la_SOURCES = \ $(srcdir)/cogl-texture.c \ $(srcdir)/cogl-texture-2d.c \ $(srcdir)/cogl-texture-2d-sliced.c \ + $(srcdir)/cogl-atlas.h \ + $(srcdir)/cogl-atlas.c \ + $(srcdir)/cogl-atlas-texture-private.h \ + $(srcdir)/cogl-atlas-texture.c \ $(srcdir)/cogl-spans.h \ $(srcdir)/cogl-spans.c \ $(srcdir)/cogl-journal-private.h \ diff --git a/cogl/cogl-atlas-texture-private.h b/cogl/cogl-atlas-texture-private.h new file mode 100644 index 000000000..de0fcdb7f --- /dev/null +++ b/cogl/cogl-atlas-texture-private.h @@ -0,0 +1,64 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_ATLAS_TEXTURE_H +#define __COGL_ATLAS_TEXTURE_H + +#include "cogl-handle.h" +#include "cogl-texture-private.h" +#include "cogl-atlas.h" + +#define COGL_ATLAS_TEXTURE(tex) ((CoglAtlasTexture *) tex) + +typedef struct _CoglAtlasTexture CoglAtlasTexture; + +struct _CoglAtlasTexture +{ + CoglTexture _parent; + + /* The format that the texture is in. This isn't necessarily the + same format as the atlas texture because we can store + pre-multiplied and non-pre-multiplied textures together */ + CoglPixelFormat format; + + /* The rectangle that was used to add this texture to the + atlas. This includes the 1-pixel border */ + CoglAtlasRectangle rectangle; + + /* The texture might need to be migrated out in which case this will + be set to TRUE and sub_texture will actually be a real texture */ + gboolean in_atlas; + + /* A CoglSubTexture representing the region for easy rendering */ + CoglHandle sub_texture; +}; + +GQuark +_cogl_handle_atlas_texture_get_type (void); + +CoglHandle +_cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, + CoglTextureFlags flags, + CoglPixelFormat internal_format); + +#endif /* __COGL_ATLAS_TEXTURE_H */ diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c new file mode 100644 index 000000000..fb52e91c8 --- /dev/null +++ b/cogl/cogl-atlas-texture.c @@ -0,0 +1,488 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-atlas-texture-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-context.h" +#include "cogl-handle.h" +#include "cogl-texture-driver.h" +#include "cogl-atlas.h" + +static void _cogl_atlas_texture_free (CoglAtlasTexture *sub_tex); + +COGL_HANDLE_DEFINE (AtlasTexture, atlas_texture); + +static const CoglTextureVtable cogl_atlas_texture_vtable; + +static void +_cogl_atlas_texture_foreach_sub_texture_in_region ( + CoglTexture *tex, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglTextureSliceCallback callback, + void *user_data) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + _cogl_texture_foreach_sub_texture_in_region (atlas_tex->sub_texture, + virtual_tx_1, + virtual_ty_1, + virtual_tx_2, + virtual_ty_2, + callback, + user_data); +} + +static void +_cogl_atlas_texture_set_wrap_mode_parameter (CoglTexture *tex, + GLenum wrap_mode) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + _cogl_texture_set_wrap_mode_parameter (atlas_tex->sub_texture, wrap_mode); +} + +static void +_cogl_atlas_texture_free (CoglAtlasTexture *atlas_tex) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Remove the texture from the atlas */ + if (atlas_tex->in_atlas) + cogl_atlas_remove_rectangle ((atlas_tex->format & COGL_A_BIT) ? + ctx->atlas_alpha : + ctx->atlas_no_alpha, + &atlas_tex->rectangle); + + cogl_handle_unref (atlas_tex->sub_texture); +} + +static gint +_cogl_atlas_texture_get_max_waste (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_max_waste (atlas_tex->sub_texture); +} + +static gboolean +_cogl_atlas_texture_is_sliced (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_is_sliced (atlas_tex->sub_texture); +} + +static gboolean +_cogl_atlas_texture_can_hardware_repeat (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return _cogl_texture_can_hardware_repeat (atlas_tex->sub_texture); +} + +static void +_cogl_atlas_texture_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + _cogl_texture_transform_coords_to_gl (atlas_tex->sub_texture, s, t); +} + +static gboolean +_cogl_atlas_texture_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_gl_texture (atlas_tex->sub_texture, + out_gl_handle, + out_gl_target); +} + +static void +_cogl_atlas_texture_set_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + _cogl_texture_set_filters (atlas_tex->sub_texture, min_filter, mag_filter); +} + +static void +_cogl_atlas_texture_ensure_mipmaps (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* FIXME: If mipmaps are required then we need to migrate the + texture out of the atlas because it will show artifacts */ + + /* Forward on to the sub texture */ + _cogl_texture_ensure_mipmaps (atlas_tex->sub_texture); +} + +static gboolean +_cogl_atlas_texture_set_region (CoglTexture *tex, + 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) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* If the texture is in the atlas then we need to copy the edge + pixels to the border */ + if (atlas_tex->in_atlas) + { + CoglHandle big_texture; + + _COGL_GET_CONTEXT (ctx, FALSE); + + big_texture = ((atlas_tex->format & COGL_A_BIT) ? + ctx->atlas_alpha_texture : ctx->atlas_no_alpha_texture); + + /* Copy the central data */ + if (!cogl_texture_set_region (big_texture, + src_x, src_y, + dst_x + atlas_tex->rectangle.x + 1, + dst_y + atlas_tex->rectangle.y + 1, + dst_width, + dst_height, + width, height, + format, + rowstride, + data)) + return FALSE; + + /* Update the left edge pixels */ + if (dst_x == 0 && + !cogl_texture_set_region (big_texture, + src_x, src_y, + atlas_tex->rectangle.x, + dst_y + atlas_tex->rectangle.y + 1, + 1, dst_height, + width, height, + format, rowstride, + data)) + return FALSE; + /* Update the right edge pixels */ + if (dst_x + dst_width == atlas_tex->rectangle.width - 2 && + !cogl_texture_set_region (big_texture, + src_x + dst_width - 1, src_y, + atlas_tex->rectangle.x + + atlas_tex->rectangle.width - 1, + dst_y + atlas_tex->rectangle.y + 1, + 1, dst_height, + width, height, + format, rowstride, + data)) + return FALSE; + /* Update the top edge pixels */ + if (dst_y == 0 && + !cogl_texture_set_region (big_texture, + src_x, src_y, + dst_x + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y, + dst_width, 1, + width, height, + format, rowstride, + data)) + return FALSE; + /* Update the bottom edge pixels */ + if (dst_y + dst_height == atlas_tex->rectangle.height - 2 && + !cogl_texture_set_region (big_texture, + src_x, src_y + dst_height - 1, + dst_x + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y + + atlas_tex->rectangle.height - 1, + dst_width, 1, + width, height, + format, rowstride, + data)) + return FALSE; + + return TRUE; + } + else + /* Otherwise we can just forward on to the sub texture */ + return cogl_texture_set_region (atlas_tex->sub_texture, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + width, height, + format, rowstride, + data); +} + +static int +_cogl_atlas_texture_get_data (CoglTexture *tex, + CoglPixelFormat format, + unsigned int rowstride, + guint8 *data) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_data (atlas_tex->sub_texture, + format, + rowstride, + data); +} + +static CoglPixelFormat +_cogl_atlas_texture_get_format (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* We don't want to forward this on the sub-texture because it isn't + the necessarily the same format. This will happen if the texture + isn't pre-multiplied */ + return atlas_tex->format; +} + +static GLenum +_cogl_atlas_texture_get_gl_format (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return _cogl_texture_get_gl_format (atlas_tex->sub_texture); +} + +static gint +_cogl_atlas_texture_get_width (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_width (atlas_tex->sub_texture); +} + +static gint +_cogl_atlas_texture_get_height (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_height (atlas_tex->sub_texture); +} + +static gboolean +_cogl_atlas_texture_reserve_space (CoglPixelFormat format, + CoglAtlas **atlas_ptr, + CoglHandle *atlas_tex_ptr, + gpointer rectangle_data, + guint width, + guint height, + CoglAtlasRectangle *rectangle) +{ + _COGL_GET_CONTEXT (ctx, FALSE); + + /* Create the atlas if we haven't already */ + if (*atlas_ptr == NULL) + *atlas_ptr = cogl_atlas_new (256, 256, NULL); + + /* Create the texture if we haven't already */ + if (*atlas_tex_ptr == NULL) + *atlas_tex_ptr = _cogl_texture_2d_new_with_size (256, 256, + COGL_TEXTURE_NONE, + format); + + /* Try to grab the space in the atlas */ + /* FIXME: This should try to reorganise the atlas to make space and + grow it if necessary. */ + return cogl_atlas_add_rectangle (*atlas_ptr, width, height, + rectangle_data, rectangle); +} + +CoglHandle +_cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglAtlasTexture *atlas_tex; + CoglBitmap *bmp = (CoglBitmap *) bmp_handle; + CoglTextureUploadData upload_data; + CoglAtlas **atlas_ptr; + CoglHandle *atlas_tex_ptr; + CoglAtlasRectangle rectangle; + gfloat tx1, ty1, tx2, ty2; + + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); + + /* We can't put the texture in the atlas if there are any special + flags. This precludes textures with COGL_TEXTURE_NO_ATLAS and + COGL_TEXTURE_NO_SLICING from being atlased */ + if (flags) + return COGL_INVALID_HANDLE; + + /* We can't atlas zero-sized textures because it breaks the atlas + data structure */ + if (bmp->width < 1 || bmp->height < 1) + return COGL_INVALID_HANDLE; + + /* If we can't read back texture data then it will be too slow to + migrate textures and we shouldn't use the atlas */ + if (!cogl_features_available (COGL_FEATURE_TEXTURE_READ_PIXELS)) + return COGL_INVALID_HANDLE; + + upload_data.bitmap = *bmp; + upload_data.bitmap_owner = FALSE; + + if (!_cogl_texture_upload_data_prepare_format (&upload_data, + &internal_format)) + { + _cogl_texture_upload_data_free (&upload_data); + return COGL_INVALID_HANDLE; + } + + /* If the texture is in a strange format then we can't use it */ + if (internal_format != COGL_PIXEL_FORMAT_RGB_888 && + (internal_format & ~COGL_PREMULT_BIT) != COGL_PIXEL_FORMAT_RGBA_8888) + { + _cogl_texture_upload_data_free (&upload_data); + return COGL_INVALID_HANDLE; + } + + if ((internal_format & COGL_A_BIT)) + { + atlas_ptr = &ctx->atlas_alpha; + atlas_tex_ptr = &ctx->atlas_alpha_texture; + } + else + { + atlas_ptr = &ctx->atlas_no_alpha; + atlas_tex_ptr = &ctx->atlas_no_alpha_texture; + } + + /* We need to allocate the texture now because we need the pointer + to set as the data for the rectangle in the atlas */ + atlas_tex = g_new (CoglAtlasTexture, 1); + + /* Try to make some space in the atlas for the texture */ + if (!_cogl_atlas_texture_reserve_space (internal_format, + atlas_ptr, + atlas_tex_ptr, + atlas_tex, + /* Add two pixels for the border */ + upload_data.bitmap.width + 2, + upload_data.bitmap.height + 2, + &rectangle)) + { + g_free (atlas_tex); + _cogl_texture_upload_data_free (&upload_data); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_upload_data_convert (&upload_data, internal_format)) + { + cogl_atlas_remove_rectangle (*atlas_ptr, &rectangle); + g_free (atlas_tex); + _cogl_texture_upload_data_free (&upload_data); + return COGL_INVALID_HANDLE; + } + + tx1 = (rectangle.x + 1) / (gfloat) cogl_atlas_get_width (*atlas_ptr); + ty1 = (rectangle.y + 1) / (gfloat) cogl_atlas_get_height (*atlas_ptr); + tx2 = ((rectangle.x + rectangle.width - 1) / + (gfloat) cogl_atlas_get_width (*atlas_ptr)); + ty2 = ((rectangle.y + rectangle.height - 1) / + (gfloat) cogl_atlas_get_height (*atlas_ptr)); + + atlas_tex->_parent.vtable = &cogl_atlas_texture_vtable; + atlas_tex->format = internal_format; + atlas_tex->rectangle = rectangle; + atlas_tex->in_atlas = TRUE; + atlas_tex->sub_texture = cogl_texture_new_from_sub_texture (*atlas_tex_ptr, + tx1, ty1, + tx2, ty2); + + /* Defer to set_region so that we can share the code for copying the + edge pixels to the border */ + _cogl_atlas_texture_set_region (COGL_TEXTURE (atlas_tex), + 0, 0, + 0, 0, + upload_data.bitmap.width, + upload_data.bitmap.height, + upload_data.bitmap.width, + upload_data.bitmap.height, + upload_data.bitmap.format, + upload_data.bitmap.rowstride, + upload_data.bitmap.data); + + return _cogl_atlas_texture_handle_new (atlas_tex); +} + +static const CoglTextureVtable +cogl_atlas_texture_vtable = + { + _cogl_atlas_texture_set_region, + _cogl_atlas_texture_get_data, + _cogl_atlas_texture_foreach_sub_texture_in_region, + _cogl_atlas_texture_get_max_waste, + _cogl_atlas_texture_is_sliced, + _cogl_atlas_texture_can_hardware_repeat, + _cogl_atlas_texture_transform_coords_to_gl, + _cogl_atlas_texture_get_gl_texture, + _cogl_atlas_texture_set_filters, + _cogl_atlas_texture_ensure_mipmaps, + _cogl_atlas_texture_set_wrap_mode_parameter, + _cogl_atlas_texture_get_format, + _cogl_atlas_texture_get_gl_format, + _cogl_atlas_texture_get_width, + _cogl_atlas_texture_get_height + }; diff --git a/cogl/cogl-atlas.c b/cogl/cogl-atlas.c new file mode 100644 index 000000000..d368d2aa8 --- /dev/null +++ b/cogl/cogl-atlas.c @@ -0,0 +1,520 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-atlas.h" + +/* Implements a data structure which keeps track of unused + sub-rectangles within a larger rectangle using a binary tree + structure. The algorithm for this is based on the description here: + + http://www.blackpawn.com/texts/lightmaps/default.html +*/ + +typedef struct _CoglAtlasNode CoglAtlasNode; +typedef struct _CoglAtlasStackEntry CoglAtlasStackEntry; + +typedef void (* CoglAtlasInternalForeachCb) (CoglAtlasNode *node, + gpointer data); + +typedef enum +{ + COGL_ATLAS_BRANCH, + COGL_ATLAS_FILLED_LEAF, + COGL_ATLAS_EMPTY_LEAF +} CoglAtlasNodeType; + +struct _CoglAtlas +{ + CoglAtlasNode *root; + + guint space_remaining; + guint n_rectangles; + + GDestroyNotify value_destroy_func; +}; + +struct _CoglAtlasNode +{ + CoglAtlasNodeType type; + + CoglAtlasRectangle rectangle; + + CoglAtlasNode *parent; + + union + { + /* Fields used when this is a branch */ + struct + { + CoglAtlasNode *left; + CoglAtlasNode *right; + } branch; + + /* Field used when this is a filled leaf */ + gpointer data; + } d; +}; + +struct _CoglAtlasStackEntry +{ + /* The node to search */ + CoglAtlasNode *node; + /* Index of next branch of this node to explore. Basically either 0 + to go left or 1 to go right */ + gboolean next_index; + /* Next entry in the stack */ + CoglAtlasStackEntry *next; +}; + +static CoglAtlasNode * +cogl_atlas_node_new (void) +{ + return g_slice_new (CoglAtlasNode); +} + +static void +cogl_atlas_node_free (CoglAtlasNode *node) +{ + g_slice_free (CoglAtlasNode, node); +} + +CoglAtlas * +cogl_atlas_new (guint width, guint height, + GDestroyNotify value_destroy_func) +{ + CoglAtlas *atlas = g_new (CoglAtlas, 1); + CoglAtlasNode *root = cogl_atlas_node_new (); + + root->type = COGL_ATLAS_EMPTY_LEAF; + root->parent = NULL; + root->rectangle.x = 0; + root->rectangle.y = 0; + root->rectangle.width = width; + root->rectangle.height = height; + + atlas->root = root; + atlas->space_remaining = width * height; + atlas->n_rectangles = 0; + atlas->value_destroy_func = value_destroy_func; + + return atlas; +} + +static CoglAtlasStackEntry * +cogl_atlas_stack_push (CoglAtlasStackEntry *stack, + CoglAtlasNode *node, + gboolean next_index) +{ + CoglAtlasStackEntry *new_entry = g_slice_new (CoglAtlasStackEntry); + + new_entry->node = node; + new_entry->next_index = next_index; + new_entry->next = stack; + + return new_entry; +} + +static CoglAtlasStackEntry * +cogl_atlas_stack_pop (CoglAtlasStackEntry *stack) +{ + CoglAtlasStackEntry *next = stack->next; + + g_slice_free (CoglAtlasStackEntry, stack); + + return next; +} + +static CoglAtlasNode * +cogl_atlas_node_split_horizontally (CoglAtlasNode *node, + guint left_width) +{ + /* Splits the node horizontally (according to emacs' definition, not + vim) by converting it to a branch and adding two new leaf + nodes. The leftmost branch will have the width left_width and + will be returned. If the node is already just the right size it + won't do anything */ + + CoglAtlasNode *left_node, *right_node; + + if (node->rectangle.width == left_width) + return node; + + left_node = cogl_atlas_node_new (); + left_node->type = COGL_ATLAS_EMPTY_LEAF; + left_node->parent = node; + left_node->rectangle.x = node->rectangle.x; + left_node->rectangle.y = node->rectangle.y; + left_node->rectangle.width = left_width; + left_node->rectangle.height = node->rectangle.height; + node->d.branch.left = left_node; + + right_node = cogl_atlas_node_new (); + right_node->type = COGL_ATLAS_EMPTY_LEAF; + right_node->parent = node; + right_node->rectangle.x = node->rectangle.x + left_width; + right_node->rectangle.y = node->rectangle.y; + right_node->rectangle.width = node->rectangle.width - left_width; + right_node->rectangle.height = node->rectangle.height; + node->d.branch.right = right_node; + + node->type = COGL_ATLAS_BRANCH; + + return left_node; +} + +static CoglAtlasNode * +cogl_atlas_node_split_vertically (CoglAtlasNode *node, + guint top_height) +{ + /* Splits the node vertically (according to emacs' definition, not + vim) by converting it to a branch and adding two new leaf + nodes. The topmost branch will have the height top_height and + will be returned. If the node is already just the right size it + won't do anything */ + + CoglAtlasNode *top_node, *bottom_node; + + if (node->rectangle.height == top_height) + return node; + + top_node = cogl_atlas_node_new (); + top_node->type = COGL_ATLAS_EMPTY_LEAF; + top_node->parent = node; + top_node->rectangle.x = node->rectangle.x; + top_node->rectangle.y = node->rectangle.y; + top_node->rectangle.width = node->rectangle.width; + top_node->rectangle.height = top_height; + node->d.branch.left = top_node; + + bottom_node = cogl_atlas_node_new (); + bottom_node->type = COGL_ATLAS_EMPTY_LEAF; + bottom_node->parent = node; + bottom_node->rectangle.x = node->rectangle.x; + bottom_node->rectangle.y = node->rectangle.y + top_height; + bottom_node->rectangle.width = node->rectangle.width; + bottom_node->rectangle.height = node->rectangle.height - top_height; + node->d.branch.right = bottom_node; + + node->type = COGL_ATLAS_BRANCH; + + return top_node; +} + +gboolean +cogl_atlas_add_rectangle (CoglAtlas *atlas, + guint width, guint height, + gpointer data, + CoglAtlasRectangle *rectangle) +{ + /* Stack of nodes to search in */ + CoglAtlasStackEntry *node_stack; + CoglAtlasNode *found_node = NULL; + + /* Zero-sized rectangles break the algorithm for removing rectangles + so we'll disallow them */ + g_return_val_if_fail (width > 0 && height > 0, FALSE); + + /* Start with the root node */ + node_stack = cogl_atlas_stack_push (NULL, atlas->root, FALSE); + + /* Depth-first search for an empty node that is big enough */ + while (node_stack) + { + /* Pop an entry off the stack */ + CoglAtlasNode *node = node_stack->node; + int next_index = node_stack->next_index; + node_stack = cogl_atlas_stack_pop (node_stack); + + /* Regardless of the type of the node, there's no point + descending any further if the new rectangle won't fit within + it */ + if (node->rectangle.width >= width && + node->rectangle.height >= height) + { + if (node->type == COGL_ATLAS_EMPTY_LEAF) + { + /* We've found a node we can use */ + found_node = node; + break; + } + else if (node->type == COGL_ATLAS_BRANCH) + { + if (next_index) + /* Try the right branch */ + node_stack = cogl_atlas_stack_push (node_stack, + node->d.branch.right, + 0); + else + { + /* Make sure we remember to try the right branch once + we've finished descending the left branch */ + node_stack = cogl_atlas_stack_push (node_stack, + node, + 1); + /* Try the left branch */ + node_stack = cogl_atlas_stack_push (node_stack, + node->d.branch.left, + 0); + } + } + } + } + + /* Free the stack */ + while (node_stack) + node_stack = cogl_atlas_stack_pop (node_stack); + + if (found_node) + { + /* Split according to whichever axis will leave us with the + largest space */ + if (found_node->rectangle.width - width > + found_node->rectangle.height - height) + { + found_node = cogl_atlas_node_split_horizontally (found_node, width); + found_node = cogl_atlas_node_split_vertically (found_node, height); + } + else + { + found_node = cogl_atlas_node_split_vertically (found_node, height); + found_node = cogl_atlas_node_split_horizontally (found_node, width); + } + + found_node->type = COGL_ATLAS_FILLED_LEAF; + found_node->d.data = data; + if (rectangle) + *rectangle = found_node->rectangle; + + /* Record how much empty space is remaining after this rectangle + is added */ + g_assert (width * height <= atlas->space_remaining); + atlas->space_remaining -= width * height; + atlas->n_rectangles++; + + return TRUE; + } + else + return FALSE; +} + +void +cogl_atlas_remove_rectangle (CoglAtlas *atlas, + const CoglAtlasRectangle *rectangle) +{ + CoglAtlasNode *node = atlas->root; + + /* We can do a binary-chop down the search tree to find the rectangle */ + while (node->type == COGL_ATLAS_BRANCH) + { + CoglAtlasNode *left_node = node->d.branch.left; + + /* If and only if the rectangle is in the left node then the x,y + position of the rectangle will be within the node's + rectangle */ + if (rectangle->x < left_node->rectangle.x + left_node->rectangle.width && + rectangle->y < left_node->rectangle.y + left_node->rectangle.height) + /* Go left */ + node = left_node; + else + /* Go right */ + node = node->d.branch.right; + } + + /* Make sure we found the right node */ + if (node->type != COGL_ATLAS_FILLED_LEAF || + node->rectangle.x != rectangle->x || + node->rectangle.y != rectangle->y || + node->rectangle.width != rectangle->width || + node->rectangle.height != rectangle->height) + /* This should only happen if someone tried to remove a rectangle + that was not in the atlas so something has gone wrong */ + g_return_if_reached (); + else + { + /* Convert the node back to an empty node */ + if (atlas->value_destroy_func) + atlas->value_destroy_func (node->d.data); + node->type = COGL_ATLAS_EMPTY_LEAF; + + /* Walk back up the tree combining branch nodes that have two + empty leaves back into a single empty leaf */ + for (node = node->parent; node; node = node->parent) + { + /* This node is a parent so it should always be a branch */ + g_assert (node->type == COGL_ATLAS_BRANCH); + + if (node->d.branch.left->type == COGL_ATLAS_EMPTY_LEAF && + node->d.branch.right->type == COGL_ATLAS_EMPTY_LEAF) + { + cogl_atlas_node_free (node->d.branch.left); + cogl_atlas_node_free (node->d.branch.right); + node->type = COGL_ATLAS_EMPTY_LEAF; + } + else + break; + } + + /* There is now more free space and one less rectangle */ + atlas->space_remaining += rectangle->width * rectangle->height; + g_assert (atlas->n_rectangles > 0); + atlas->n_rectangles--; + } +} + +guint +cogl_atlas_get_width (CoglAtlas *atlas) +{ + return atlas->root->rectangle.width; +} + +guint +cogl_atlas_get_height (CoglAtlas *atlas) +{ + return atlas->root->rectangle.height; +} + +guint +cogl_atlas_get_remaining_space (CoglAtlas *atlas) +{ + return atlas->space_remaining; +} + +guint +cogl_atlas_get_n_rectangles (CoglAtlas *atlas) +{ + return atlas->n_rectangles; +} + +static void +cogl_atlas_internal_foreach (CoglAtlas *atlas, + CoglAtlasInternalForeachCb callback, + gpointer data) +{ + /* Stack of nodes to search in */ + CoglAtlasStackEntry *node_stack; + + /* Start with the root node */ + node_stack = cogl_atlas_stack_push (NULL, atlas->root, 0); + + /* Iterate all nodes depth-first */ + while (node_stack) + { + CoglAtlasNode *node = node_stack->node; + + switch (node->type) + { + case COGL_ATLAS_BRANCH: + if (node_stack->next_index == 0) + { + /* Next time we come back to this node, go to the right */ + node_stack->next_index = 1; + + /* Explore the left branch next */ + node_stack = cogl_atlas_stack_push (node_stack, + node->d.branch.left, + 0); + } + else if (node_stack->next_index == 1) + { + /* Next time we come back to this node, stop processing it */ + node_stack->next_index = 2; + + /* Explore the right branch next */ + node_stack = cogl_atlas_stack_push (node_stack, + node->d.branch.right, + 0); + } + else + { + /* We're finished with this node so we can call the callback */ + callback (node, data); + node_stack = cogl_atlas_stack_pop (node_stack); + } + break; + + default: + /* Some sort of leaf node, just call the callback */ + callback (node, data); + node_stack = cogl_atlas_stack_pop (node_stack); + break; + } + } + + /* The stack should now be empty */ + g_assert (node_stack == NULL); +} + +typedef struct _CoglAtlasForeachClosure +{ + CoglAtlasCallback callback; + gpointer data; +} CoglAtlasForeachClosure; + +static void +cogl_atlas_foreach_cb (CoglAtlasNode *node, gpointer data) +{ + CoglAtlasForeachClosure *closure = data; + + if (node->type == COGL_ATLAS_FILLED_LEAF) + closure->callback (&node->rectangle, node->d.data, closure->data); +} + +void +cogl_atlas_foreach (CoglAtlas *atlas, + CoglAtlasCallback callback, + gpointer data) +{ + CoglAtlasForeachClosure closure; + + closure.callback = callback; + closure.data = data; + + cogl_atlas_internal_foreach (atlas, cogl_atlas_foreach_cb, &closure); +} + +static void +cogl_atlas_free_cb (CoglAtlasNode *node, gpointer data) +{ + CoglAtlas *atlas = data; + + if (node->type == COGL_ATLAS_FILLED_LEAF && atlas->value_destroy_func) + atlas->value_destroy_func (node->d.data); + + cogl_atlas_node_free (node); +} + +void +cogl_atlas_free (CoglAtlas *atlas) +{ + cogl_atlas_internal_foreach (atlas, cogl_atlas_free_cb, atlas); + g_free (atlas); +} diff --git a/cogl/cogl-atlas.h b/cogl/cogl-atlas.h new file mode 100644 index 000000000..37ccc898c --- /dev/null +++ b/cogl/cogl-atlas.h @@ -0,0 +1,76 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_ATLAS_H +#define __COGL_ATLAS_H + +#include + +typedef struct _CoglAtlas CoglAtlas; +typedef struct _CoglAtlasRectangle CoglAtlasRectangle; + +typedef void (* CoglAtlasCallback) (const CoglAtlasRectangle *rectangle, + gpointer rectangle_data, + gpointer user_data); + +struct _CoglAtlasRectangle +{ + guint x, y; + guint width, height; +}; + +CoglAtlas * +cogl_atlas_new (guint width, guint height, + GDestroyNotify value_destroy_func); + +gboolean +cogl_atlas_add_rectangle (CoglAtlas *atlas, + guint width, guint height, + gpointer data, + CoglAtlasRectangle *rectangle); + +void +cogl_atlas_remove_rectangle (CoglAtlas *atlas, + const CoglAtlasRectangle *rectangle); + +guint +cogl_atlas_get_width (CoglAtlas *atlas); + +guint +cogl_atlas_get_height (CoglAtlas *atlas); + +guint +cogl_atlas_get_remaining_space (CoglAtlas *atlas); + +guint +cogl_atlas_get_n_rectangles (CoglAtlas *atlas); + +void +cogl_atlas_foreach (CoglAtlas *atlas, + CoglAtlasCallback callback, + gpointer data); + +void +cogl_atlas_free (CoglAtlas *atlas); + +#endif /* __COGL_ATLAS_H */ diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index c78b2821c..d144fe557 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -147,6 +147,11 @@ cogl_create_context (void) cogl_enable (enable_flags); _cogl_flush_face_winding (); + _context->atlas_alpha = NULL; + _context->atlas_no_alpha = NULL; + _context->atlas_alpha_texture = COGL_INVALID_HANDLE; + _context->atlas_no_alpha_texture = COGL_INVALID_HANDLE; + return TRUE; } @@ -188,6 +193,15 @@ _cogl_destroy_context () if (_context->default_material) cogl_handle_unref (_context->default_material); + if (_context->atlas_alpha) + cogl_atlas_free (_context->atlas_alpha); + if (_context->atlas_no_alpha) + cogl_atlas_free (_context->atlas_no_alpha); + if (_context->atlas_alpha_texture) + cogl_handle_unref (_context->atlas_alpha_texture); + if (_context->atlas_no_alpha_texture) + cogl_handle_unref (_context->atlas_no_alpha_texture); + g_free (_context); } diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h index 717f3534e..f4fff5b78 100644 --- a/cogl/cogl-context.h +++ b/cogl/cogl-context.h @@ -30,6 +30,7 @@ #include "cogl-clip-stack.h" #include "cogl-matrix-stack.h" #include "cogl-material-private.h" +#include "cogl-atlas.h" typedef struct { @@ -111,6 +112,13 @@ typedef struct CoglHandle texture_download_material; + /* Separate atlases for textures with and without alpha */ + CoglAtlas *atlas_alpha; + CoglAtlas *atlas_no_alpha; + /* Textures for the two atlases */ + CoglHandle atlas_alpha_texture; + CoglHandle atlas_no_alpha_texture; + CoglContextDriver drv; } CoglContext; diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 5b94b1dbe..b316a52cf 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -40,6 +40,7 @@ #include "cogl-texture-2d-sliced-private.h" #include "cogl-texture-2d-private.h" #include "cogl-sub-texture-private.h" +#include "cogl-atlas-texture-private.h" #include "cogl-material.h" #include "cogl-context.h" #include "cogl-handle.h" @@ -65,6 +66,7 @@ cogl_is_texture (CoglHandle handle) return FALSE; return (obj->klass->type == _cogl_handle_texture_2d_get_type () || + obj->klass->type == _cogl_handle_atlas_texture_get_type () || obj->klass->type == _cogl_handle_texture_2d_sliced_get_type () || obj->klass->type == _cogl_handle_sub_texture_get_type ()); } @@ -366,18 +368,22 @@ cogl_texture_new_from_bitmap (CoglHandle bmp_handle, { CoglHandle tex; - /* First try creating a fast-path non-sliced texture */ - tex = _cogl_texture_2d_new_from_bitmap (bmp_handle, - flags, - internal_format); + /* First try putting the texture in the atlas */ + if ((tex = _cogl_atlas_texture_new_from_bitmap (bmp_handle, + flags, + internal_format))) + return tex; - /* If it fails resort to sliced textures */ - if (tex == COGL_INVALID_HANDLE) - tex = _cogl_texture_2d_sliced_new_from_bitmap (bmp_handle, - flags, - internal_format); + /* If that doesn't work try a fast path 2D texture */ + if ((tex = _cogl_texture_2d_new_from_bitmap (bmp_handle, + flags, + internal_format))) + return tex; - return tex; + /* Otherwise create a sliced texture */ + return _cogl_texture_2d_sliced_new_from_bitmap (bmp_handle, + flags, + internal_format); } CoglHandle diff --git a/cogl/cogl-types.h b/cogl/cogl-types.h index 30c73d4f7..3d5e3eb02 100644 --- a/cogl/cogl-types.h +++ b/cogl/cogl-types.h @@ -275,7 +275,8 @@ struct _CoglTextureVertex typedef enum { COGL_TEXTURE_NONE = 0, COGL_TEXTURE_NO_AUTO_MIPMAP = 1 << 0, - COGL_TEXTURE_NO_SLICING = 1 << 1 + COGL_TEXTURE_NO_SLICING = 1 << 1, + COGL_TEXTURE_NO_ATLAS = 1 << 2 } CoglTextureFlags; /** From ec547b7ce03a021346e7ff1d3e3c30999025a9ce Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 4 Dec 2009 18:24:15 +0000 Subject: [PATCH 12/33] cogl-atlas-texture: Support reorganizing the atlas when it is full When space can't be found in the atlas for a new texture it will now try to reorganize the atlas to make space. A new CoglAtlas is created and all of the textures are readded in decreasing size order. If the textures still don't fit then the size of the atlas is doubled until either we find a space or we reach the texture size limits. If we successfully find an organization that fits then all of the textures will be migrated to a new texture. This involves copying the texture data into CPU memory and then uploading it again. Potentially it could eventually use a PBO or an FBO to transfer the image without going through the CPU. The algorithm for laying out the textures works a lot better if the rectangles are added in order so we might eventually want some API for creating multiple textures in one go to avoid reorganizing the atlas as far as possible. --- cogl/cogl-atlas-texture.c | 311 ++++++++++++++++++++++++++++++++++---- 1 file changed, 278 insertions(+), 33 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index fb52e91c8..32bcbe688 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -39,6 +39,8 @@ #include "cogl-texture-driver.h" #include "cogl-atlas.h" +#include + static void _cogl_atlas_texture_free (CoglAtlasTexture *sub_tex); COGL_HANDLE_DEFINE (AtlasTexture, atlas_texture); @@ -319,32 +321,283 @@ _cogl_atlas_texture_get_height (CoglTexture *tex) return cogl_texture_get_height (atlas_tex->sub_texture); } +static CoglHandle +_cogl_atlas_texture_create_sub_texture (CoglHandle full_texture, + const CoglAtlasRectangle *rectangle) +{ + /* Create a subtexture for the given rectangle not including the + 1-pixel border */ + gfloat tex_width = cogl_texture_get_width (full_texture); + gfloat tex_height = cogl_texture_get_height (full_texture); + gfloat tx1 = (rectangle->x + 1) / tex_width; + gfloat ty1 = (rectangle->y + 1) / tex_height; + gfloat tx2 = (rectangle->x + rectangle->width - 1) / tex_width; + gfloat ty2 = (rectangle->y + rectangle->height - 1) / tex_height; + + return cogl_texture_new_from_sub_texture (full_texture, tx1, ty1, tx2, ty2); +} + +typedef struct _CoglAtlasTextureRepositionData +{ + /* The current texture which already has a position */ + CoglAtlasTexture *texture; + /* The new position of the texture */ + CoglAtlasRectangle new_position; +} CoglAtlasTextureRepositionData; + +static void +_cogl_atlas_texture_migrate (guint n_textures, + CoglAtlasTextureRepositionData *textures, + CoglHandle old_texture, + CoglHandle new_texture, + CoglAtlasTexture *skip_texture) +{ + guint i; + guint8 *data; + CoglPixelFormat format; + gint bpp; + guint old_height, old_width; + + format = cogl_texture_get_format (old_texture); + bpp = _cogl_get_format_bpp (format); + old_width = cogl_texture_get_width (old_texture); + old_height = cogl_texture_get_height (old_texture); + + /* Get the existing data for the texture. FIXME: we should use a PBO + or maybe copy the texture data via an FBO */ + data = g_malloc (bpp * + cogl_texture_get_width (old_texture) * + cogl_texture_get_height (old_texture)); + cogl_texture_get_data (old_texture, format, + old_width * bpp, + data); + + for (i = 0; i < n_textures; i++) + { + /* Skip the texture that is being added because it doesn't contain + any data yet */ + if (textures[i].texture != skip_texture) + { + cogl_texture_set_region (new_texture, + textures[i].texture->rectangle.x, + textures[i].texture->rectangle.y, + textures[i].new_position.x, + textures[i].new_position.y, + textures[i].new_position.width, + textures[i].new_position.height, + old_width, old_height, + format, + old_width * bpp, + data); + /* Update the sub texture */ + cogl_handle_unref (textures[i].texture->sub_texture); + textures[i].texture->sub_texture = + _cogl_atlas_texture_create_sub_texture (new_texture, + &textures[i].new_position); + } + + /* Update the texture position */ + textures[i].texture->rectangle = textures[i].new_position; + } + + g_free (data); +} + +typedef struct _CoglAtlasTextureGetRectanglesData +{ + CoglAtlasTextureRepositionData *textures; + /* Number of textures found so far */ + guint n_textures; +} CoglAtlasTextureGetRectanglesData; + +static void +_cogl_atlas_texture_get_rectangles_cb (const CoglAtlasRectangle *rectangle, + gpointer rectangle_data, + gpointer user_data) +{ + CoglAtlasTextureGetRectanglesData *data = user_data; + + data->textures[data->n_textures++].texture = rectangle_data; +} + +static void +_cogl_atlas_texture_get_next_size (guint *atlas_width, + guint *atlas_height) +{ + /* Double the size of the texture by increasing whichever dimension + is smaller */ + if (*atlas_width < *atlas_height) + *atlas_width <<= 1; + else + *atlas_height <<= 1; +} + +static CoglAtlas * +_cogl_atlas_texture_create_atlas (guint atlas_width, + guint atlas_height, + guint n_textures, + CoglAtlasTextureRepositionData *textures) +{ + GLint max_texture_size = 1024; + + GE( glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size) ); + + /* Sanity check that we're not going to get stuck in an infinite + loop if the maximum texture size has the high bit set */ + if ((max_texture_size & (1 << (sizeof (GLint) * 8 - 2)))) + max_texture_size >>= 1; + + /* Keep trying increasingly larger atlases until we can fit all of + the textures */ + while (atlas_width < max_texture_size && atlas_height < max_texture_size) + { + CoglAtlas *new_atlas = cogl_atlas_new (atlas_width, atlas_height, NULL); + guint i; + + /* Add all of the textures and keep track of the new position */ + for (i = 0; i < n_textures; i++) + if (!cogl_atlas_add_rectangle (new_atlas, + textures[i].texture->rectangle.width, + textures[i].texture->rectangle.height, + textures[i].texture, + &textures[i].new_position)) + break; + + /* If the atlas can contain all of the textures then we have a + winner */ + if (i >= n_textures) + return new_atlas; + + cogl_atlas_free (new_atlas); + _cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height); + } + + /* If we get here then there's no atlas that can accommodate all of + the rectangles */ + + return NULL; +} + +static int +_cogl_atlas_texture_compare_size_cb (const void *a, + const void *b) +{ + const CoglAtlasTextureRepositionData *ta = a; + const CoglAtlasTextureRepositionData *tb = b; + guint a_size, b_size; + + a_size = ta->texture->rectangle.width * ta->texture->rectangle.height; + b_size = tb->texture->rectangle.width * tb->texture->rectangle.height; + + return a_size < b_size ? 1 : a_size > b_size ? -1 : 0; +} + static gboolean _cogl_atlas_texture_reserve_space (CoglPixelFormat format, CoglAtlas **atlas_ptr, CoglHandle *atlas_tex_ptr, - gpointer rectangle_data, + CoglAtlasTexture *new_sub_tex, guint width, - guint height, - CoglAtlasRectangle *rectangle) + guint height) { + CoglAtlasTextureGetRectanglesData data; + CoglAtlas *new_atlas; + CoglHandle new_tex; + guint atlas_width, atlas_height; + gboolean ret; + _COGL_GET_CONTEXT (ctx, FALSE); - /* Create the atlas if we haven't already */ + /* Check if we can fit the rectangle into the existing atlas */ + if (*atlas_ptr && cogl_atlas_add_rectangle (*atlas_ptr, width, height, + new_sub_tex, + &new_sub_tex->rectangle)) + return TRUE; + + /* We need to reorganise the atlas so we'll get an array of all the + textures currently in the atlas. */ + data.n_textures = 0; if (*atlas_ptr == NULL) - *atlas_ptr = cogl_atlas_new (256, 256, NULL); + data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData)); + else + { + data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData) * + (cogl_atlas_get_n_rectangles (*atlas_ptr) + 1)); + cogl_atlas_foreach (*atlas_ptr, _cogl_atlas_texture_get_rectangles_cb, + &data); + } - /* Create the texture if we haven't already */ - if (*atlas_tex_ptr == NULL) - *atlas_tex_ptr = _cogl_texture_2d_new_with_size (256, 256, - COGL_TEXTURE_NONE, - format); + /* Add the new rectangle as a dummy texture so that it can be + positioned with the rest */ + data.textures[data.n_textures++].texture = new_sub_tex; - /* Try to grab the space in the atlas */ - /* FIXME: This should try to reorganise the atlas to make space and - grow it if necessary. */ - return cogl_atlas_add_rectangle (*atlas_ptr, width, height, - rectangle_data, rectangle); + /* The atlasing algorithm works a lot better if the rectangles are + added in decreasing order of size so we'll first sort the + array */ + qsort (data.textures, data.n_textures, + sizeof (CoglAtlasTextureRepositionData), + _cogl_atlas_texture_compare_size_cb); + + /* Try to create a new atlas that can contain all of the textures */ + if (*atlas_ptr) + { + atlas_width = cogl_atlas_get_width (*atlas_ptr); + atlas_height = cogl_atlas_get_height (*atlas_ptr); + + /* If there is enough space in the existing for the new + rectangle in the existing atlas we'll start with the same + size, otherwise we'll immediately double it */ + if (cogl_atlas_get_remaining_space (*atlas_ptr) < width * height) + _cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height); + } + else + { + /* Start with an initial size of 256x256 */ + atlas_width = 256; + atlas_height = 256; + } + + new_atlas = _cogl_atlas_texture_create_atlas (atlas_width, atlas_height, + data.n_textures, data.textures); + + /* If we can't create an atlas with the texture then give up */ + if (new_atlas == NULL) + ret = FALSE; + else + { + /* We need to migrate the existing textures into a new texture */ + new_tex = + _cogl_texture_2d_new_with_size (cogl_atlas_get_width (new_atlas), + cogl_atlas_get_height (new_atlas), + COGL_TEXTURE_NONE, + format); + + if (*atlas_ptr) + { + /* Move all the textures to the right position in the new + texture. This will also update the texture's rectangle */ + _cogl_atlas_texture_migrate (data.n_textures, + data.textures, + *atlas_tex_ptr, + new_tex, + new_sub_tex); + cogl_atlas_free (*atlas_ptr); + cogl_handle_unref (*atlas_tex_ptr); + } + else + /* We know there's only one texture so we can just directly + update the rectangle from its new position */ + data.textures[0].texture->rectangle = data.textures[0].new_position; + + *atlas_ptr = new_atlas; + *atlas_tex_ptr = new_tex; + + ret = TRUE; + } + + g_free (data.textures); + + return ret; } CoglHandle @@ -357,8 +610,6 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, CoglTextureUploadData upload_data; CoglAtlas **atlas_ptr; CoglHandle *atlas_tex_ptr; - CoglAtlasRectangle rectangle; - gfloat tx1, ty1, tx2, ty2; _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); @@ -412,16 +663,18 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, /* We need to allocate the texture now because we need the pointer to set as the data for the rectangle in the atlas */ atlas_tex = g_new (CoglAtlasTexture, 1); + /* We need to fill in the texture size now because it is used in the + reserve_space function below. We add two pixels for the border */ + atlas_tex->rectangle.width = upload_data.bitmap.width + 2; + atlas_tex->rectangle.height = upload_data.bitmap.height + 2; /* Try to make some space in the atlas for the texture */ if (!_cogl_atlas_texture_reserve_space (internal_format, atlas_ptr, atlas_tex_ptr, atlas_tex, - /* Add two pixels for the border */ - upload_data.bitmap.width + 2, - upload_data.bitmap.height + 2, - &rectangle)) + atlas_tex->rectangle.width, + atlas_tex->rectangle.height)) { g_free (atlas_tex); _cogl_texture_upload_data_free (&upload_data); @@ -430,26 +683,18 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, if (!_cogl_texture_upload_data_convert (&upload_data, internal_format)) { - cogl_atlas_remove_rectangle (*atlas_ptr, &rectangle); + cogl_atlas_remove_rectangle (*atlas_ptr, &atlas_tex->rectangle); g_free (atlas_tex); _cogl_texture_upload_data_free (&upload_data); return COGL_INVALID_HANDLE; } - tx1 = (rectangle.x + 1) / (gfloat) cogl_atlas_get_width (*atlas_ptr); - ty1 = (rectangle.y + 1) / (gfloat) cogl_atlas_get_height (*atlas_ptr); - tx2 = ((rectangle.x + rectangle.width - 1) / - (gfloat) cogl_atlas_get_width (*atlas_ptr)); - ty2 = ((rectangle.y + rectangle.height - 1) / - (gfloat) cogl_atlas_get_height (*atlas_ptr)); - atlas_tex->_parent.vtable = &cogl_atlas_texture_vtable; atlas_tex->format = internal_format; - atlas_tex->rectangle = rectangle; atlas_tex->in_atlas = TRUE; - atlas_tex->sub_texture = cogl_texture_new_from_sub_texture (*atlas_tex_ptr, - tx1, ty1, - tx2, ty2); + atlas_tex->sub_texture = + _cogl_atlas_texture_create_sub_texture (*atlas_tex_ptr, + &atlas_tex->rectangle); /* Defer to set_region so that we can share the code for copying the edge pixels to the border */ From f5d43d9b024f1e94e6e7adfef4c3916a9da3d39d Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 4 Dec 2009 18:55:53 +0000 Subject: [PATCH 13/33] cogl-texture-atlas: Add some debugging notes This adds an 'atlas' category to the COGL_DEBUG environment variable. When enabled Cogl will display messages when textures are added to the atlas and when the atlas is reorganized. --- cogl/cogl-atlas-texture.c | 62 +++++++++++++++++++++++++++++++++++---- cogl/cogl-debug.c | 3 +- cogl/cogl-debug.h | 3 +- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 32bcbe688..6b7a504c2 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -86,10 +86,24 @@ _cogl_atlas_texture_free (CoglAtlasTexture *atlas_tex) /* Remove the texture from the atlas */ if (atlas_tex->in_atlas) - cogl_atlas_remove_rectangle ((atlas_tex->format & COGL_A_BIT) ? - ctx->atlas_alpha : - ctx->atlas_no_alpha, - &atlas_tex->rectangle); + { + CoglAtlas *atlas = ((atlas_tex->format & COGL_A_BIT) ? + ctx->atlas_alpha : + ctx->atlas_no_alpha); + + cogl_atlas_remove_rectangle (atlas, &atlas_tex->rectangle); + + COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i", + atlas_tex->rectangle.width, + atlas_tex->rectangle.height); + COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", + cogl_atlas_get_width (atlas), + cogl_atlas_get_height (atlas), + cogl_atlas_get_n_rectangles (atlas), + cogl_atlas_get_remaining_space (atlas) * 100 / + (cogl_atlas_get_width (atlas) * + cogl_atlas_get_height (atlas))); + } cogl_handle_unref (atlas_tex->sub_texture); } @@ -512,7 +526,16 @@ _cogl_atlas_texture_reserve_space (CoglPixelFormat format, if (*atlas_ptr && cogl_atlas_add_rectangle (*atlas_ptr, width, height, new_sub_tex, &new_sub_tex->rectangle)) - return TRUE; + { + COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", + cogl_atlas_get_width (*atlas_ptr), + cogl_atlas_get_height (*atlas_ptr), + cogl_atlas_get_n_rectangles (*atlas_ptr), + cogl_atlas_get_remaining_space (*atlas_ptr) * 100 / + (cogl_atlas_get_width (*atlas_ptr) * + cogl_atlas_get_height (*atlas_ptr))); + return TRUE; + } /* We need to reorganise the atlas so we'll get an array of all the textures currently in the atlas. */ @@ -562,7 +585,10 @@ _cogl_atlas_texture_reserve_space (CoglPixelFormat format, /* If we can't create an atlas with the texture then give up */ if (new_atlas == NULL) - ret = FALSE; + { + COGL_NOTE (ATLAS, "Could not fit texture in the atlas"); + ret = FALSE; + } else { /* We need to migrate the existing textures into a new texture */ @@ -572,6 +598,17 @@ _cogl_atlas_texture_reserve_space (CoglPixelFormat format, COGL_TEXTURE_NONE, format); + COGL_NOTE (ATLAS, + "Atlas %s with size %ix%i", + *atlas_ptr == NULL || + cogl_atlas_get_width (*atlas_ptr) != + cogl_atlas_get_width (new_atlas) || + cogl_atlas_get_height (*atlas_ptr) != + cogl_atlas_get_height (new_atlas) ? + "resized" : "reorganized", + cogl_atlas_get_width (new_atlas), + cogl_atlas_get_height (new_atlas)); + if (*atlas_ptr) { /* Move all the textures to the right position in the new @@ -592,6 +629,14 @@ _cogl_atlas_texture_reserve_space (CoglPixelFormat format, *atlas_ptr = new_atlas; *atlas_tex_ptr = new_tex; + COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", + cogl_atlas_get_width (*atlas_ptr), + cogl_atlas_get_height (*atlas_ptr), + cogl_atlas_get_n_rectangles (*atlas_ptr), + cogl_atlas_get_remaining_space (*atlas_ptr) * 100 / + (cogl_atlas_get_width (*atlas_ptr) * + cogl_atlas_get_height (*atlas_ptr))); + ret = TRUE; } @@ -641,10 +686,15 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, return COGL_INVALID_HANDLE; } + COGL_NOTE (ATLAS, "Adding texture of size %ix%i", bmp->width, bmp->height); + /* If the texture is in a strange format then we can't use it */ if (internal_format != COGL_PIXEL_FORMAT_RGB_888 && (internal_format & ~COGL_PREMULT_BIT) != COGL_PIXEL_FORMAT_RGBA_8888) { + COGL_NOTE (ATLAS, "Texture can not be added because the " + "format is unsupported"); + _cogl_texture_upload_data_free (&upload_data); return COGL_INVALID_HANDLE; } diff --git a/cogl/cogl-debug.c b/cogl/cogl-debug.c index 1265595f9..f653ee4bb 100644 --- a/cogl/cogl-debug.c +++ b/cogl/cogl-debug.c @@ -47,7 +47,8 @@ static const GDebugKey cogl_debug_keys[] = { { "batching", COGL_DEBUG_BATCHING }, { "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM }, { "matrices", COGL_DEBUG_MATRICES }, - { "force-scanline-paths", COGL_DEBUG_FORCE_SCANLINE_PATHS } + { "force-scanline-paths", COGL_DEBUG_FORCE_SCANLINE_PATHS }, + { "atlas", COGL_DEBUG_ATLAS } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/cogl/cogl-debug.h b/cogl/cogl-debug.h index bc450aa99..087672039 100644 --- a/cogl/cogl-debug.h +++ b/cogl/cogl-debug.h @@ -45,7 +45,8 @@ typedef enum { COGL_DEBUG_BATCHING = 1 << 13, COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM = 1 << 14, COGL_DEBUG_MATRICES = 1 << 15, - COGL_DEBUG_FORCE_SCANLINE_PATHS = 1 << 16 + COGL_DEBUG_FORCE_SCANLINE_PATHS = 1 << 16, + COGL_DEBUG_ATLAS = 1 << 17 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG From 636cef1bd64754e544971c7d691d6179b7d11109 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 4 Dec 2009 19:43:39 +0000 Subject: [PATCH 14/33] cogl-atlas: Add a debug option to visualize the atlas This adds a 'dump-atlas-image' debug category. When enabled, CoglAtlas will use Cairo to create a png which visualizes the leaf rectangles of the atlas. --- cogl/cogl-atlas.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++ cogl/cogl-debug.c | 3 +- cogl/cogl-debug.h | 3 +- 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/cogl/cogl-atlas.c b/cogl/cogl-atlas.c index d368d2aa8..82217e25d 100644 --- a/cogl/cogl-atlas.c +++ b/cogl/cogl-atlas.c @@ -31,6 +31,7 @@ #include #include "cogl-atlas.h" +#include "cogl-debug.h" /* Implements a data structure which keeps track of unused sub-rectangles within a larger rectangle using a binary tree @@ -39,6 +40,16 @@ http://www.blackpawn.com/texts/lightmaps/default.html */ +#ifdef COGL_ENABLE_DEBUG + +/* The cairo header is only used for debugging to generate an image of + the atlas */ +#include + +static void cogl_atlas_dump_image (CoglAtlas *atlas); + +#endif /* COGL_ENABLE_DEBUG */ + typedef struct _CoglAtlasNode CoglAtlasNode; typedef struct _CoglAtlasStackEntry CoglAtlasStackEntry; @@ -320,6 +331,11 @@ cogl_atlas_add_rectangle (CoglAtlas *atlas, atlas->space_remaining -= width * height; atlas->n_rectangles++; +#ifdef COGL_ENABLE_DEBUG + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE)) + cogl_atlas_dump_image (atlas); +#endif + return TRUE; } else @@ -388,6 +404,11 @@ cogl_atlas_remove_rectangle (CoglAtlas *atlas, g_assert (atlas->n_rectangles > 0); atlas->n_rectangles--; } + +#ifdef COGL_ENABLE_DEBUG + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE)) + cogl_atlas_dump_image (atlas); +#endif } guint @@ -518,3 +539,58 @@ cogl_atlas_free (CoglAtlas *atlas) cogl_atlas_internal_foreach (atlas, cogl_atlas_free_cb, atlas); g_free (atlas); } + +#ifdef COGL_ENABLE_DEBUG + +static void +cogl_atlas_dump_image_cb (CoglAtlasNode *node, gpointer data) +{ + cairo_t *cr = data; + + if (node->type == COGL_ATLAS_FILLED_LEAF || + node->type == COGL_ATLAS_EMPTY_LEAF) + { + /* Fill the rectangle using a different colour depending on + whether the rectangle is used */ + if (node->type == COGL_ATLAS_FILLED_LEAF) + cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); + else + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); + + cairo_rectangle (cr, + node->rectangle.x, + node->rectangle.y, + node->rectangle.width, + node->rectangle.height); + + cairo_fill_preserve (cr); + + /* Draw a white outline around the rectangle */ + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_stroke (cr); + } +} + +static void +cogl_atlas_dump_image (CoglAtlas *atlas) +{ + /* This dumps a png to help visualize the atlas. Each leaf rectangle + is drawn with a white outline. Unused leaves are filled in black + and used leaves are blue */ + + cairo_surface_t *surface = + cairo_image_surface_create (CAIRO_FORMAT_RGB24, + cogl_atlas_get_width (atlas), + cogl_atlas_get_height (atlas)); + cairo_t *cr = cairo_create (surface); + + cogl_atlas_internal_foreach (atlas, cogl_atlas_dump_image_cb, cr); + + cairo_destroy (cr); + + cairo_surface_write_to_png (surface, "cogl-atlas-dump.png"); + + cairo_surface_destroy (surface); +} + +#endif /* COGL_ENABLE_DEBUG */ diff --git a/cogl/cogl-debug.c b/cogl/cogl-debug.c index f653ee4bb..bba75e560 100644 --- a/cogl/cogl-debug.c +++ b/cogl/cogl-debug.c @@ -48,7 +48,8 @@ static const GDebugKey cogl_debug_keys[] = { { "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM }, { "matrices", COGL_DEBUG_MATRICES }, { "force-scanline-paths", COGL_DEBUG_FORCE_SCANLINE_PATHS }, - { "atlas", COGL_DEBUG_ATLAS } + { "atlas", COGL_DEBUG_ATLAS }, + { "dump-atlas-image", COGL_DEBUG_DUMP_ATLAS_IMAGE } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/cogl/cogl-debug.h b/cogl/cogl-debug.h index 087672039..864f50a60 100644 --- a/cogl/cogl-debug.h +++ b/cogl/cogl-debug.h @@ -46,7 +46,8 @@ typedef enum { COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM = 1 << 14, COGL_DEBUG_MATRICES = 1 << 15, COGL_DEBUG_FORCE_SCANLINE_PATHS = 1 << 16, - COGL_DEBUG_ATLAS = 1 << 17 + COGL_DEBUG_ATLAS = 1 << 17, + COGL_DEBUG_DUMP_ATLAS_IMAGE = 1 << 18 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG From 3ebe48105d0254a4d5b4be6831975c0aa7914290 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Sat, 5 Dec 2009 13:24:01 +0000 Subject: [PATCH 15/33] cogl-atlas-texture: Try to do texture blits using an FBO When reorganizing the textures, we can avoid downloading the entire texture data if we bind the source texture in a framebuffer object and copy the destination using glCopyTexSubImage2D. This is also implemented using a much faster path in Mesa. Currently it is calling the GL framebuffer API directly but ideally it would use the Cogl offscreen API. However there is no way to tell Cogl not to create a stencil renderbuffer which seems like a waste in this situation. If FBOs are not available it will fallback to reading back the entire texture data as before. --- cogl/cogl-atlas-texture.c | 196 ++++++++++++++++++++++++++++++++------ 1 file changed, 167 insertions(+), 29 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 6b7a504c2..8fb9ae9c8 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -41,12 +41,169 @@ #include +#ifdef HAVE_COGL_GLES2 + +#include "../gles/cogl-gles2-wrapper.h" + +#else /* HAVE_COGL_GLES2 */ + +#define glGenFramebuffers ctx->drv.pf_glGenFramebuffers +#define glBindFramebuffer ctx->drv.pf_glBindFramebuffer +#define glFramebufferTexture2D ctx->drv.pf_glFramebufferTexture2D +#define glCheckFramebufferStatus ctx->drv.pf_glCheckFramebufferStatus +#define glDeleteFramebuffers ctx->drv.pf_glDeleteFramebuffers + +#endif /* HAVE_COGL_GLES2 */ + +#ifndef GL_FRAMEBUFFER +#define GL_FRAMEBUFFER 0x8D40 +#endif +#ifndef GL_FRAMEBUFFER_BINDING +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#endif +#ifndef GL_COLOR_ATTACHMENT0 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#endif +#ifndef GL_FRAMEBUFFER_COMPLETE +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#endif + static void _cogl_atlas_texture_free (CoglAtlasTexture *sub_tex); COGL_HANDLE_DEFINE (AtlasTexture, atlas_texture); static const CoglTextureVtable cogl_atlas_texture_vtable; +/* If we want to do mulitple blits from a texture (such as when + reorganizing the atlas) then it's quicker to download all of the + data once and upload multiple times from that. This struct is used + to keep the image data for a series of blits */ + +typedef struct _CoglAtlasTextureBlitData +{ + CoglHandle src_tex, dst_tex; + + /* If we're using an FBO to blit, then FBO will be non-zero and + old_fbo will be the previous framebuffer binding */ + GLuint fbo, old_fbo; + + /* If we're not using an FBO then we g_malloc a buffer and copy the + complete texture data in */ + unsigned char *image_data; + CoglPixelFormat format; + gint bpp; + guint src_height, src_width; + + GLenum dst_gl_target; +} CoglAtlasTextureBlitData; + +static void +_cogl_atlas_texture_blit_begin (CoglAtlasTextureBlitData *data, + CoglHandle dst_tex, + CoglHandle src_tex) +{ + GLenum src_gl_target; + GLuint src_gl_texture; + GLuint dst_gl_texture; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + data->dst_tex = dst_tex; + data->src_tex = src_tex; + data->fbo = 0; + + /* If we can use an FBO then we don't need to download the data and + we can tell GL to blit directly between the textures */ + if (cogl_features_available (COGL_FEATURE_OFFSCREEN) && + !cogl_texture_is_sliced (dst_tex) && + cogl_texture_get_gl_texture (src_tex, &src_gl_texture, &src_gl_target) && + cogl_texture_get_gl_texture (dst_tex, &dst_gl_texture, + &data->dst_gl_target)) + { + /* Ideally we would use the cogl-offscreen API here, but I'd + rather avoid creating a stencil renderbuffer which you can't + currently do */ + /* Preserve the previous framebuffer binding so we don't trample + on cogl-offscreen */ + data->old_fbo = 0; + GE( glGetIntegerv (GL_FRAMEBUFFER_BINDING, (GLint *) &data->old_fbo) ); + + _cogl_texture_set_filters (src_tex, GL_NEAREST, GL_NEAREST); + + /* Create an FBO to read from the src texture */ + GE( glGenFramebuffers (1, &data->fbo) ); + GE( glBindFramebuffer (GL_FRAMEBUFFER, data->fbo) ); + GE( glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + src_gl_target, src_gl_texture, 0) ); + if (glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + /* The FBO failed for whatever reason so we'll fallback to + reading the texture data */ + GE( glBindFramebuffer (GL_FRAMEBUFFER, data->old_fbo) ); + GE( glDeleteFramebuffers (1, &data->fbo) ); + data->fbo = 0; + } + + GE( glBindTexture (data->dst_gl_target, dst_gl_texture) ); + } + + if (data->fbo) + COGL_NOTE (ATLAS, "Blit set up using an FBO"); + else + { + /* We need to retrieve the entire texture data (there is no + glGetTexSubImage2D) */ + + data->format = cogl_texture_get_format (src_tex); + data->bpp = _cogl_get_format_bpp (data->format); + data->src_width = cogl_texture_get_width (src_tex); + data->src_height = cogl_texture_get_height (src_tex); + + data->image_data = g_malloc (data->bpp * data->src_width * + data->src_height); + cogl_texture_get_data (src_tex, data->format, + data->src_width * data->bpp, data->image_data); + } +} + +static void +_cogl_atlas_texture_blit (CoglAtlasTextureBlitData *data, + guint src_x, + guint src_y, + guint dst_x, + guint dst_y, + guint width, + guint height) +{ + /* If we have an FBO then we can do a fast blit */ + if (data->fbo) + GE( glCopyTexSubImage2D (data->dst_gl_target, 0, dst_x, dst_y, src_x, src_y, + width, height) ); + else + cogl_texture_set_region (data->dst_tex, + src_x, src_y, + dst_x, dst_y, + width, height, + data->src_width, data->src_height, + data->format, + data->src_width * data->bpp, + data->image_data); +} + +static void +_cogl_atlas_texture_blit_end (CoglAtlasTextureBlitData *data) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (data->fbo) + { + GE( glBindFramebuffer (GL_FRAMEBUFFER, data->old_fbo) ); + GE( glDeleteFramebuffers (1, &data->fbo) ); + } + else + g_free (data->image_data); +} + static void _cogl_atlas_texture_foreach_sub_texture_in_region ( CoglTexture *tex, @@ -367,24 +524,9 @@ _cogl_atlas_texture_migrate (guint n_textures, CoglAtlasTexture *skip_texture) { guint i; - guint8 *data; - CoglPixelFormat format; - gint bpp; - guint old_height, old_width; + CoglAtlasTextureBlitData blit_data; - format = cogl_texture_get_format (old_texture); - bpp = _cogl_get_format_bpp (format); - old_width = cogl_texture_get_width (old_texture); - old_height = cogl_texture_get_height (old_texture); - - /* Get the existing data for the texture. FIXME: we should use a PBO - or maybe copy the texture data via an FBO */ - data = g_malloc (bpp * - cogl_texture_get_width (old_texture) * - cogl_texture_get_height (old_texture)); - cogl_texture_get_data (old_texture, format, - old_width * bpp, - data); + _cogl_atlas_texture_blit_begin (&blit_data, new_texture, old_texture); for (i = 0; i < n_textures; i++) { @@ -392,17 +534,13 @@ _cogl_atlas_texture_migrate (guint n_textures, any data yet */ if (textures[i].texture != skip_texture) { - cogl_texture_set_region (new_texture, - textures[i].texture->rectangle.x, - textures[i].texture->rectangle.y, - textures[i].new_position.x, - textures[i].new_position.y, - textures[i].new_position.width, - textures[i].new_position.height, - old_width, old_height, - format, - old_width * bpp, - data); + _cogl_atlas_texture_blit (&blit_data, + textures[i].texture->rectangle.x, + textures[i].texture->rectangle.y, + textures[i].new_position.x, + textures[i].new_position.y, + textures[i].new_position.width, + textures[i].new_position.height); /* Update the sub texture */ cogl_handle_unref (textures[i].texture->sub_texture); textures[i].texture->sub_texture = @@ -414,7 +552,7 @@ _cogl_atlas_texture_migrate (guint n_textures, textures[i].texture->rectangle = textures[i].new_position; } - g_free (data); + _cogl_atlas_texture_blit_end (&blit_data); } typedef struct _CoglAtlasTextureGetRectanglesData From 9139f21e093d9d809ecaa16a0e1d84afea93da32 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Sat, 5 Dec 2009 13:48:03 +0000 Subject: [PATCH 16/33] cogl-atlas-texture: Remove textures from the atlas when mipmapping is required Mipmaps don't work very well in the current atlas because there is not enough padding between the textures. If ensure_mipmaps is called it will now create a new texture and migrate the atlased texture to it. It will use the same blit mechanism as when migrating so it will try to use an FBO for a fast blit. However if this is not possible it will end up downloading the data for the entire atlas which is not ideal. --- cogl/cogl-atlas-texture.c | 66 +++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 8fb9ae9c8..4d7bf6f9d 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -237,16 +237,17 @@ _cogl_atlas_texture_set_wrap_mode_parameter (CoglTexture *tex, } static void -_cogl_atlas_texture_free (CoglAtlasTexture *atlas_tex) +_cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - /* Remove the texture from the atlas */ if (atlas_tex->in_atlas) { - CoglAtlas *atlas = ((atlas_tex->format & COGL_A_BIT) ? - ctx->atlas_alpha : - ctx->atlas_no_alpha); + CoglAtlas *atlas; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + atlas = ((atlas_tex->format & COGL_A_BIT) ? + ctx->atlas_alpha : + ctx->atlas_no_alpha); cogl_atlas_remove_rectangle (atlas, &atlas_tex->rectangle); @@ -260,7 +261,15 @@ _cogl_atlas_texture_free (CoglAtlasTexture *atlas_tex) cogl_atlas_get_remaining_space (atlas) * 100 / (cogl_atlas_get_width (atlas) * cogl_atlas_get_height (atlas))); + + atlas_tex->in_atlas = FALSE; } +} + +static void +_cogl_atlas_texture_free (CoglAtlasTexture *atlas_tex) +{ + _cogl_atlas_texture_remove_from_atlas (atlas_tex); cogl_handle_unref (atlas_tex->sub_texture); } @@ -332,8 +341,47 @@ _cogl_atlas_texture_ensure_mipmaps (CoglTexture *tex) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); - /* FIXME: If mipmaps are required then we need to migrate the - texture out of the atlas because it will show artifacts */ + /* Mipmaps do not work well with the current atlas so instead we'll + just migrate the texture out and use a regular texture */ + if (atlas_tex->in_atlas) + { + CoglHandle atlas_texture; + CoglAtlasTextureBlitData blit_data; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + COGL_NOTE (ATLAS, "Migrating texture out of the atlas"); + + cogl_handle_unref (atlas_tex->sub_texture); + + if ((atlas_tex->format & COGL_A_BIT)) + atlas_texture = ctx->atlas_alpha_texture; + else + atlas_texture = ctx->atlas_no_alpha_texture; + + /* Create a new texture at the right size, not including the + border */ + atlas_tex->sub_texture = + cogl_texture_new_with_size (atlas_tex->rectangle.width - 2, + atlas_tex->rectangle.height - 2, + COGL_TEXTURE_NO_ATLAS, + atlas_tex->format); + + /* Blit the data out of the atlas to the new texture. If FBOs + aren't available this will end up having to copy the entire + atlas texture */ + _cogl_atlas_texture_blit_begin (&blit_data, atlas_tex->sub_texture, + atlas_texture); + _cogl_atlas_texture_blit (&blit_data, + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y + 1, + 0, 0, + atlas_tex->rectangle.width - 2, + atlas_tex->rectangle.height - 2); + _cogl_atlas_texture_blit_end (&blit_data); + + _cogl_atlas_texture_remove_from_atlas (atlas_tex); + } /* Forward on to the sub texture */ _cogl_texture_ensure_mipmaps (atlas_tex->sub_texture); From 568b6fbda1b3992baeddedf9f9856185ca826036 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Sat, 5 Dec 2009 14:20:00 +0000 Subject: [PATCH 17/33] cogl-material: Ensure mipmaps before doing anything else on a texture When the texture is in the atlas, ensuring the mipmaps can effectively make it become a completely different texture so we should do this before getting the GL handle. --- cogl/cogl-material.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/cogl/cogl-material.c b/cogl/cogl-material.c index 1a641a776..cbccf3724 100644 --- a/cogl/cogl-material.c +++ b/cogl/cogl-material.c @@ -1400,7 +1400,17 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, tex_handle = layer->texture; if (tex_handle != COGL_INVALID_HANDLE) - cogl_texture_get_gl_texture (tex_handle, &gl_texture, &gl_target); + { + if (is_mipmap_filter (layer->min_filter) + || is_mipmap_filter (layer->mag_filter)) + _cogl_texture_ensure_mipmaps (tex_handle); + + _cogl_texture_set_filters (tex_handle, + layer->min_filter, + layer->mag_filter); + + cogl_texture_get_gl_texture (tex_handle, &gl_texture, &gl_target); + } else { new_gl_layer_info.fallback = TRUE; @@ -1431,13 +1441,6 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, GE (glActiveTexture (GL_TEXTURE0 + i)); unit = _cogl_get_texture_unit (i); - _cogl_texture_set_filters (layer->texture, - layer->min_filter, - layer->mag_filter); - if (is_mipmap_filter (layer->min_filter) - || is_mipmap_filter (layer->mag_filter)) - _cogl_texture_ensure_mipmaps (layer->texture); - /* FIXME: We could be more clever here and only bind the texture if it is different from gl_layer_info->gl_texture to avoid redundant GL calls. However a few other places in Cogl and From f1c289378e7f557ad5227417c22d7c0ce4fcecf2 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 11 Jan 2010 16:21:56 +0000 Subject: [PATCH 18/33] cogl-texture-2d: Fix the coordinate wrapping for negative coordinates The formula to wrap the coordinates to the [0,1] range was broken when the coordinates were negative. --- cogl/cogl-texture-2d.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c index e605bb73e..f6bf603b2 100644 --- a/cogl/cogl-texture-2d.c +++ b/cogl/cogl-texture-2d.c @@ -66,8 +66,8 @@ _cogl_texture_2d_wrap_coords (float t_1, float t_2, t_2 -= int_part; if (signbit (int_part)) { - *out_t_1 = 1.0f - t_1; - *out_t_2 = 1.0f - t_2; + *out_t_1 = 1.0f + t_1; + *out_t_2 = 1.0f + t_2; } else { From 963afa88c51ba69820e570560fffc5c1650d16a6 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 11 Jan 2010 16:23:38 +0000 Subject: [PATCH 19/33] cogl-texture: Fix manual repeating for negative coordinates When calculating the next integer position for negative coordinates it would not increment if the position is already a multiple of one so we need to manually add one. --- cogl/cogl-texture.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index c1bae7af5..85a6de7cc 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -228,12 +228,13 @@ static void _cogl_texture_iter_update (CoglTextureIter *iter) { gfloat t_2; + float frac_part; - modff (iter->pos, &iter->next_pos); + frac_part = modff (iter->pos, &iter->next_pos); /* modff rounds the int part towards zero so we need to add one if we're meant to be heading away from zero */ - if (iter->pos >= 0.0f) + if (iter->pos >= 0.0f || frac_part == 0.0f) iter->next_pos += 1.0f; if (iter->next_pos > iter->end) From ae7825275e0b67a2cdab3d3208e16b2b3c11498c Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 18 Jan 2010 09:22:04 +0000 Subject: [PATCH 20/33] cogl: Make CoglSubTexture only work for quad rendering The sub texture backend doesn't work well as a completely general texture backend because for example when rendering with cogl_polygon it needs to be able to tranform arbitrary texture coordinates without reference to the other coordintes. This can't be done when the texture coordinates are a multiple of one because sometimes the coordinate should represent the left or top edge and sometimes it should represent the bottom or top edge. For example if the s coordinates are 0 and 1 then 1 represents the right edge but if they are 1 and 2 then 1 represents the left edge. Instead the sub-textures are now documented not to support coordinates outside the range [0,1]. The coordinates for the sub-region are now represented as integers as this helps avoid rounding issues. The region can no longer be a super-region of the texture as this simplifies the code quite a lot. There are two new texture virtual functions: transform_quad_coords_to_gl - This transforms two pairs of coordinates representing a quad. It will return FALSE if the coordinates can not be transformed. The sub texture backend uses this to detect coordinates that require repeating which causes cogl-primitives to use manual repeating. ensure_non_quad_rendering - This is used in cogl_polygon and cogl_vertex_buffer to inform the texture backend that transform_quad_to_gl is going to be used. The atlas backend migrates the texture out of the atlas when it hits this. --- cogl/cogl-atlas-texture.c | 57 +++- cogl/cogl-primitives.c | 21 +- cogl/cogl-sub-texture-private.h | 15 +- cogl/cogl-sub-texture.c | 496 +++++++++++--------------------- cogl/cogl-texture-2d-sliced.c | 21 ++ cogl/cogl-texture-2d.c | 17 ++ cogl/cogl-texture-private.h | 10 + cogl/cogl-texture.c | 33 ++- cogl/cogl-texture.h | 22 +- cogl/cogl-vertex-buffer.c | 5 + 10 files changed, 324 insertions(+), 373 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 4d7bf6f9d..4bbff22ea 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -34,6 +34,7 @@ #include "cogl-texture-private.h" #include "cogl-atlas-texture-private.h" #include "cogl-texture-2d-private.h" +#include "cogl-sub-texture-private.h" #include "cogl-context.h" #include "cogl-handle.h" #include "cogl-texture-driver.h" @@ -312,6 +313,17 @@ _cogl_atlas_texture_transform_coords_to_gl (CoglTexture *tex, _cogl_texture_transform_coords_to_gl (atlas_tex->sub_texture, s, t); } +static gboolean +_cogl_atlas_texture_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return _cogl_texture_transform_quad_coords_to_gl (atlas_tex->sub_texture, + coords); +} + static gboolean _cogl_atlas_texture_get_gl_texture (CoglTexture *tex, GLuint *out_gl_handle, @@ -337,12 +349,9 @@ _cogl_atlas_texture_set_filters (CoglTexture *tex, } static void -_cogl_atlas_texture_ensure_mipmaps (CoglTexture *tex) +_cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) { - CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); - - /* Mipmaps do not work well with the current atlas so instead we'll - just migrate the texture out and use a regular texture */ + /* Make sure this texture is not in the atlas */ if (atlas_tex->in_atlas) { CoglHandle atlas_texture; @@ -382,11 +391,34 @@ _cogl_atlas_texture_ensure_mipmaps (CoglTexture *tex) _cogl_atlas_texture_remove_from_atlas (atlas_tex); } +} + +static void +_cogl_atlas_texture_ensure_mipmaps (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Mipmaps do not work well with the current atlas so instead we'll + just migrate the texture out and use a regular texture */ + _cogl_atlas_texture_migrate_out_of_atlas (atlas_tex); /* Forward on to the sub texture */ _cogl_texture_ensure_mipmaps (atlas_tex->sub_texture); } +static void +_cogl_atlas_texture_ensure_non_quad_rendering (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Sub textures can't support non-quad rendering so we'll just + migrate the texture out */ + _cogl_atlas_texture_migrate_out_of_atlas (atlas_tex); + + /* Forward on to the sub texture */ + _cogl_texture_ensure_non_quad_rendering (atlas_tex->sub_texture); +} + static gboolean _cogl_atlas_texture_set_region (CoglTexture *tex, int src_x, @@ -546,14 +578,11 @@ _cogl_atlas_texture_create_sub_texture (CoglHandle full_texture, { /* Create a subtexture for the given rectangle not including the 1-pixel border */ - gfloat tex_width = cogl_texture_get_width (full_texture); - gfloat tex_height = cogl_texture_get_height (full_texture); - gfloat tx1 = (rectangle->x + 1) / tex_width; - gfloat ty1 = (rectangle->y + 1) / tex_height; - gfloat tx2 = (rectangle->x + rectangle->width - 1) / tex_width; - gfloat ty2 = (rectangle->y + rectangle->height - 1) / tex_height; - - return cogl_texture_new_from_sub_texture (full_texture, tx1, ty1, tx2, ty2); + return _cogl_sub_texture_new (full_texture, + rectangle->x + 1, + rectangle->y + 1, + rectangle->width - 2, + rectangle->height - 2); } typedef struct _CoglAtlasTextureRepositionData @@ -958,9 +987,11 @@ cogl_atlas_texture_vtable = _cogl_atlas_texture_is_sliced, _cogl_atlas_texture_can_hardware_repeat, _cogl_atlas_texture_transform_coords_to_gl, + _cogl_atlas_texture_transform_quad_coords_to_gl, _cogl_atlas_texture_get_gl_texture, _cogl_atlas_texture_set_filters, _cogl_atlas_texture_ensure_mipmaps, + _cogl_atlas_texture_ensure_non_quad_rendering, _cogl_atlas_texture_set_wrap_mode_parameter, _cogl_atlas_texture_get_format, _cogl_atlas_texture_get_gl_format, diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c index b23d0158b..1215725ec 100644 --- a/cogl/cogl-primitives.c +++ b/cogl/cogl-primitives.c @@ -278,14 +278,14 @@ _cogl_multitexture_quad_single_primitive (float x_1, the coordinates (such as in the sub texture backend). This should be safe to call because we know that the texture only has one slice. */ - for (coord_num = 0; coord_num < 2; coord_num++) - { - float *s = out_tex_coords + coord_num * 2; - float *t = s + 1; - _cogl_texture_transform_coords_to_gl (tex_handle, s, t); - if (*s < 0.0f || *s > 1.0f || *t < 0.0f || *t > 1.0f) - need_repeat = TRUE; - } + if (!_cogl_texture_transform_quad_coords_to_gl (tex_handle, + out_tex_coords)) + /* If the backend can't support these coordinates then bail out */ + return FALSE; + for (coord_num = 0; coord_num < 4; coord_num++) + if (out_tex_coords[coord_num] < 0.0f || + out_tex_coords[coord_num] > 1.0f) + need_repeat = TRUE; /* If the texture has waste or we are using GL_TEXTURE_RECT we * can't handle texture repeating so we can't use the layer if @@ -867,6 +867,11 @@ cogl_polygon (const CoglTextureVertex *vertices, if (tex_handle == COGL_INVALID_HANDLE) continue; + /* Give the texture a chance to know that we're rendering + non-quad shaped primitives. If the texture is in an atlas it + will be migrated */ + _cogl_texture_ensure_non_quad_rendering (tex_handle); + if (i == 0 && cogl_texture_is_sliced (tex_handle)) { #if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GLES2) diff --git a/cogl/cogl-sub-texture-private.h b/cogl/cogl-sub-texture-private.h index 899f61e8c..c8fa4bbc9 100644 --- a/cogl/cogl-sub-texture-private.h +++ b/cogl/cogl-sub-texture-private.h @@ -37,12 +37,11 @@ struct _CoglSubTexture CoglHandle full_texture; - /* The texture coordinates of the subregion of full_texture */ - gfloat tx1, ty1; - gfloat tx2, ty2; - - /* Are all of the texture coordinates a multiple of one? */ - gboolean tex_coords_are_a_multiple; + /* The region represented by this sub-texture */ + gint sub_x; + gint sub_y; + gint sub_width; + gint sub_height; }; GQuark @@ -50,7 +49,7 @@ _cogl_handle_sub_texture_get_type (void); CoglHandle _cogl_sub_texture_new (CoglHandle full_texture, - gfloat tx1, gfloat ty1, - gfloat tx2, gfloat ty2); + gint sub_x, gint sub_y, + gint sub_width, gint sub_height); #endif /* __COGL_SUB_TEXTURE_H */ diff --git a/cogl/cogl-sub-texture.c b/cogl/cogl-sub-texture.c index d0213fe88..164a23f47 100644 --- a/cogl/cogl-sub-texture.c +++ b/cogl/cogl-sub-texture.c @@ -46,63 +46,105 @@ COGL_HANDLE_DEFINE (SubTexture, sub_texture); static const CoglTextureVtable cogl_sub_texture_vtable; -/* Maps from the texture coordinates of this texture to the texture - coordinates of the full texture */ - static void -_cogl_sub_texture_map_coordinate_pair (CoglSubTexture *sub_tex, - gfloat *tx, gfloat *ty) +_cogl_sub_texture_map_range (gfloat *t1, gfloat *t2, + gint sub_offset, + gint sub_size, + gint full_size) { - *tx = *tx * (sub_tex->tx2 - sub_tex->tx1) + sub_tex->tx1; - *ty = *ty * (sub_tex->ty2 - sub_tex->ty1) + sub_tex->ty1; + gfloat t1_frac, t1_int, t2_frac, t2_int; + + t1_frac = modff (*t1, &t1_int); + t2_frac = modff (*t2, &t2_int); + + if (t1_frac < 0.0f) + { + t1_frac += 1.0f; + t1_int -= 1.0f; + } + if (t2_frac < 0.0f) + { + t2_frac += 1.0f; + t2_int -= 1.0f; + } + + /* If one of the coordinates is zero we need to make sure it is + still greater than the other coordinate if it was originally so + we'll flip it to the other side */ + if (*t1 < *t2) + { + if (t2_frac == 0.0f) + { + t2_frac = 1.0f; + t2_int -= 1.0f; + } + } + else + { + if (t1_frac == 0.0f) + { + t1_frac = 1.0f; + t1_int -= 1.0f; + } + } + + /* Convert the fractional part leaving the integer part intact */ + t1_frac = (sub_offset + t1_frac * sub_size) / full_size; + *t1 = t1_frac + t1_int; + + t2_frac = (sub_offset + t2_frac * sub_size) / full_size; + *t2 = t2_frac + t2_int; } static void -_cogl_sub_texture_map_coordinate_set (CoglSubTexture *sub_tex, - gfloat *tx1, gfloat *ty1, - gfloat *tx2, gfloat *ty2) +_cogl_sub_texture_map_quad (CoglSubTexture *sub_tex, + gfloat *coords) { - _cogl_sub_texture_map_coordinate_pair (sub_tex, tx1, ty1); - _cogl_sub_texture_map_coordinate_pair (sub_tex, tx2, ty2); + guint full_width = cogl_texture_get_width (sub_tex->full_texture); + guint full_height = cogl_texture_get_height (sub_tex->full_texture); + + _cogl_sub_texture_map_range (coords + 0, coords + 2, + sub_tex->sub_x, sub_tex->sub_width, + full_width); + _cogl_sub_texture_map_range (coords + 1, coords + 3, + sub_tex->sub_y, sub_tex->sub_height, + full_height); } /* Maps from the texture coordinates of the full texture to the texture coordinates of the sub texture */ -static void -_cogl_sub_texture_unmap_coordinate_pair (CoglSubTexture *sub_tex, - gfloat *coords) +static gfloat +_cogl_sub_texture_unmap_coord (gfloat t, + gint sub_offset, + gint sub_size, + gint full_size) { - if (sub_tex->tx1 == sub_tex->tx2) - coords[0] = sub_tex->tx1; - else - coords[0] = (coords[0] - sub_tex->tx1) / (sub_tex->tx2 - sub_tex->tx1); + gfloat frac_part, int_part; - if (sub_tex->ty1 == sub_tex->ty2) - coords[0] = sub_tex->ty1; + /* Convert the fractional part leaving the integer part in tact */ + frac_part = modff (t, &int_part); + + if (signbit (frac_part)) + frac_part = ((1.0f + frac_part) * full_size - + sub_offset - sub_size) / sub_size; else - coords[1] = (coords[1] - sub_tex->ty1) / (sub_tex->ty2 - sub_tex->ty1); + frac_part = (frac_part * full_size - sub_offset) / sub_size; + + return frac_part + int_part; } static void -_cogl_sub_texture_unmap_coordinate_set (CoglSubTexture *sub_tex, - gfloat *coords) +_cogl_sub_texture_unmap_coords (CoglSubTexture *sub_tex, + gfloat *s, + gfloat *t) { - _cogl_sub_texture_unmap_coordinate_pair (sub_tex, coords); - _cogl_sub_texture_unmap_coordinate_pair (sub_tex, coords + 2); -} + guint full_width = cogl_texture_get_width (sub_tex->full_texture); + guint full_height = cogl_texture_get_height (sub_tex->full_texture); -static gboolean -_cogl_sub_texture_same_int_part (float t1, float t2) -{ - float int_part1, int_part2; - float frac_part1, frac_part2; - - frac_part1 = modff (t1, &int_part1); - frac_part2 = modff (t2, &int_part2); - - return (int_part1 == int_part2 || - ((frac_part1 == 0.0f || frac_part2 == 0.0f) && - ABS (int_part1 - int_part2) == 1.0f)); + *s = _cogl_sub_texture_unmap_coord (*s, sub_tex->sub_x, sub_tex->sub_width, + full_width); + *t = _cogl_sub_texture_unmap_coord (*t, sub_tex->sub_y, sub_tex->sub_height, + full_height); } typedef struct _CoglSubTextureForeachData @@ -126,7 +168,12 @@ _cogl_sub_texture_foreach_cb (CoglHandle handle, memcpy (virtual_coords, full_virtual_coords, sizeof (virtual_coords)); /* Convert the virtual coords from the full-texture space to the sub texture space */ - _cogl_sub_texture_unmap_coordinate_set (data->sub_tex, virtual_coords); + _cogl_sub_texture_unmap_coords (data->sub_tex, + &virtual_coords[0], + &virtual_coords[1]); + _cogl_sub_texture_unmap_coords (data->sub_tex, + &virtual_coords[2], + &virtual_coords[3]); data->callback (handle, gl_handle, gl_target, slice_coords, virtual_coords, @@ -142,11 +189,7 @@ _cogl_sub_texture_manual_repeat_cb (const float *coords, memcpy (mapped_coords, coords, sizeof (mapped_coords)); - _cogl_sub_texture_map_coordinate_set (data->sub_tex, - &mapped_coords[0], - &mapped_coords[1], - &mapped_coords[2], - &mapped_coords[3]); + _cogl_sub_texture_map_quad (data->sub_tex, mapped_coords); _cogl_texture_foreach_sub_texture_in_region (data->sub_tex->full_texture, mapped_coords[0], @@ -174,30 +217,10 @@ _cogl_sub_texture_foreach_sub_texture_in_region ( data.callback = callback; data.user_data = user_data; - /* If there is no repeating or the sub texture coordinates are a - multiple of the whole texture then we can just directly map the - texture coordinates */ - if (sub_tex->tex_coords_are_a_multiple || - (_cogl_sub_texture_same_int_part (virtual_tx_1, virtual_tx_2) && - _cogl_sub_texture_same_int_part (virtual_ty_1, virtual_ty_2))) - { - _cogl_sub_texture_map_coordinate_set (sub_tex, - &virtual_tx_1, - &virtual_ty_1, - &virtual_tx_2, - &virtual_ty_2); - - _cogl_texture_foreach_sub_texture_in_region - (sub_tex->full_texture, - virtual_tx_1, virtual_ty_1, - virtual_tx_2, virtual_ty_2, - _cogl_sub_texture_foreach_cb, &data); - } - else - _cogl_texture_iterate_manual_repeats (_cogl_sub_texture_manual_repeat_cb, - virtual_tx_1, virtual_ty_1, - virtual_tx_2, virtual_ty_2, - &data); + _cogl_texture_iterate_manual_repeats (_cogl_sub_texture_manual_repeat_cb, + virtual_tx_1, virtual_ty_1, + virtual_tx_2, virtual_ty_2, + &data); } static void @@ -219,12 +242,21 @@ _cogl_sub_texture_free (CoglSubTexture *sub_tex) CoglHandle _cogl_sub_texture_new (CoglHandle full_texture, - gfloat tx1, gfloat ty1, - gfloat tx2, gfloat ty2) + gint sub_x, gint sub_y, + gint sub_width, gint sub_height) { CoglSubTexture *sub_tex; CoglTexture *tex; - gfloat integer_part; + guint full_width, full_height; + + full_width = cogl_texture_get_width (full_texture); + full_height = cogl_texture_get_width (full_texture); + + /* The region must specify a non-zero subset of the full texture */ + g_return_val_if_fail (sub_x >= 0 && sub_y >= 0, COGL_INVALID_HANDLE); + g_return_val_if_fail (sub_width > 0 && sub_height > 0, COGL_INVALID_HANDLE); + g_return_val_if_fail (sub_x + sub_width <= full_width, COGL_INVALID_HANDLE); + g_return_val_if_fail (sub_y + sub_height <= full_height, COGL_INVALID_HANDLE); sub_tex = g_new (CoglSubTexture, 1); @@ -233,18 +265,10 @@ _cogl_sub_texture_new (CoglHandle full_texture, sub_tex->full_texture = cogl_handle_ref (full_texture); - sub_tex->tx1 = tx1; - sub_tex->ty1 = ty1; - sub_tex->tx2 = tx2; - sub_tex->ty2 = ty2; - - /* Track whether the texture coords are a multiple of one because in - that case we can use hardware repeating */ - sub_tex->tex_coords_are_a_multiple - = (modff (tx1, &integer_part) == 0.0f && - modff (ty1, &integer_part) == 0.0f && - modff (tx2, &integer_part) == 0.0f && - modff (ty2, &integer_part) == 0.0f); + sub_tex->sub_x = sub_x; + sub_tex->sub_y = sub_y; + sub_tex->sub_width = sub_width; + sub_tex->sub_height = sub_height; return _cogl_sub_texture_handle_new (sub_tex); } @@ -270,11 +294,12 @@ _cogl_sub_texture_can_hardware_repeat (CoglTexture *tex) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); - /* We can hardware repeat if the full texture can hardware repeat - and the coordinates for the subregion are all a multiple of the - full size of the texture (ie, they have no fractional part) */ - - return (sub_tex->tex_coords_are_a_multiple && + /* We can hardware repeat if the subtexture actually represents all of the + of the full texture */ + return (sub_tex->sub_width == + cogl_texture_get_width (sub_tex->full_texture) && + sub_tex->sub_height == + cogl_texture_get_height (sub_tex->full_texture) && _cogl_texture_can_hardware_repeat (sub_tex->full_texture)); } @@ -285,8 +310,34 @@ _cogl_sub_texture_transform_coords_to_gl (CoglTexture *tex, { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); - _cogl_sub_texture_map_coordinate_pair (sub_tex, s, t); - _cogl_texture_transform_coords_to_gl (sub_tex->full_texture, s, t); + /* This won't work if the sub texture is not the size of the full + texture and the coordinates are outside the range [0,1] */ + *s = ((*s * sub_tex->sub_width + sub_tex->sub_x) / + cogl_texture_get_width (sub_tex->full_texture)); + *t = ((*t * sub_tex->sub_height + sub_tex->sub_y) / + cogl_texture_get_height (sub_tex->full_texture)); + + return _cogl_texture_transform_coords_to_gl (sub_tex->full_texture, s, t); +} + +static gboolean +_cogl_sub_texture_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + int i; + + /* We can't support repeating with this method. In this case + cogl-primitives will resort to manual repeating */ + for (i = 0; i < 4; i++) + if (coords[i] < 0.0f || coords[i] > 1.0f) + return FALSE; + + _cogl_sub_texture_map_quad (sub_tex, coords); + + _cogl_texture_transform_quad_coords_to_gl (sub_tex->full_texture, coords); + + return TRUE; } static gboolean @@ -320,67 +371,8 @@ _cogl_sub_texture_ensure_mipmaps (CoglTexture *tex) } static void -_cogl_sub_texture_get_next_chunk (int pos, int end, - int tex_size, - int *chunk_start, int *chunk_end) +_cogl_sub_texture_ensure_non_quad_rendering (CoglTexture *tex) { - /* pos and end may be negative or greater than the size of the - texture. We want to calculate the next largest range we can copy - in one chunk */ - - if (pos < 0) - /* The behaviour of % for negative numbers is implementation - dependant in C89 so we have to do this */ - *chunk_start = (tex_size - pos) % tex_size; - else - *chunk_start = pos % tex_size; - - /* If the region is larger than the remaining size of the texture - then we need to crop it */ - if (end - pos > tex_size - *chunk_start) - end = pos + tex_size - *chunk_start; - - if (end < 0) - *chunk_end = (tex_size - end) % tex_size; - else - *chunk_end = end % tex_size; - - if (*chunk_end == 0) - *chunk_end = tex_size; -} - -static void -_cogl_sub_texture_get_x_pixel_pos (CoglSubTexture *sub_tex, - gint *px1, gint *px2) -{ - gint full_width = cogl_texture_get_width (sub_tex->full_texture); - - *px1 = full_width * sub_tex->tx1; - *px2 = full_width * sub_tex->tx2; - - if (*px1 > *px2) - { - gint temp = *px1; - *px1 = *px2; - *px2 = temp; - } -} - -static void -_cogl_sub_texture_get_y_pixel_pos (CoglSubTexture *sub_tex, - gint *py1, gint *py2) -{ - gint full_height = cogl_texture_get_height (sub_tex->full_texture); - - *py1 = full_height * sub_tex->ty1; - *py2 = full_height * sub_tex->ty2; - - if (*py1 > *py2) - { - gint temp = *py1; - *py1 = *py2; - *py2 = temp; - } } static gboolean @@ -397,144 +389,17 @@ _cogl_sub_texture_set_region (CoglTexture *tex, unsigned int rowstride, const guint8 *data) { - CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); - gint full_tex_width, full_tex_height; - gint bpp; - gint px1, py1, px2, py2; - gint it_x, it_y; - gint src_x1, src_y1, src_x2, src_y2; - CoglBitmap source_bmp; - CoglBitmap temp_bmp; - gboolean source_bmp_owner = FALSE; - CoglPixelFormat closest_format; - GLenum closest_gl_format; - GLenum closest_gl_type; - gboolean success; - CoglPixelFormat tex_format; + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); - /* 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; - - /* FIXME: If the sub texture coordinates are swapped around then we - should flip the bitmap */ - - _cogl_sub_texture_get_x_pixel_pos (sub_tex, &px1, &px2); - _cogl_sub_texture_get_y_pixel_pos (sub_tex, &py1, &py2); - - full_tex_width = cogl_texture_get_width (sub_tex->full_texture); - full_tex_height = cogl_texture_get_height (sub_tex->full_texture); - - /* Init source bitmap */ - source_bmp.width = width; - source_bmp.height = height; - source_bmp.format = format; - source_bmp.data = (guchar*) data; - - /* Rowstride from texture 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 */ - tex_format = cogl_texture_get_format (sub_tex->full_texture); - closest_format = _cogl_pixel_format_to_gl (tex_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; - } - - for (it_y = py1; it_y < py2; it_y += src_y2 - src_y1) - { - _cogl_sub_texture_get_next_chunk (it_y, py2, full_tex_width, - &src_y1, &src_y2); - - for (it_x = px1; it_x < px2; it_x += src_x2 - src_x1) - { - gint virt_x_1, virt_y_1, virt_width, virt_height; - gint copy_dst_x, copy_dst_y, copy_dst_width, copy_dst_height; - - _cogl_sub_texture_get_next_chunk (it_x, px2, full_tex_height, - &src_x1, &src_x2); - - /* Offset of the chunk from the left edge in the virtual sub - texture coordinates */ - virt_x_1 = it_x - px1; - /* Pixel width covered by this chunk */ - virt_width = src_x2 - src_x1; - /* Offset of the chunk from the top edge in the virtual sub - texture coordinates */ - virt_y_1 = it_y - py1; - /* Pixel height covered by this chunk */ - virt_height = src_y2 - src_y1; - - /* Check if this chunk intersects with the update region */ - if (dst_x + dst_width <= virt_x_1 || - dst_x >= virt_x_1 + virt_width || - dst_y + dst_height <= it_y - py1 || - dst_y >= virt_y_1 + virt_height) - continue; - - /* Calculate the intersection in virtual coordinates */ - copy_dst_width = dst_width; - if (dst_x < virt_x_1) - { - copy_dst_width -= virt_x_1 - dst_x; - copy_dst_x = virt_x_1; - } - else - copy_dst_x = dst_x; - if (copy_dst_width + copy_dst_x > virt_x_1 + virt_width) - copy_dst_width = virt_x_1 + virt_width - copy_dst_x; - - copy_dst_height = dst_height; - if (dst_y < virt_y_1) - { - copy_dst_height -= virt_y_1 - dst_y; - copy_dst_y = virt_y_1; - } - else - copy_dst_y = dst_y; - if (copy_dst_height + copy_dst_y > virt_y_1 + virt_height) - copy_dst_height = virt_y_1 + virt_height - copy_dst_y; - - /* Update the region in the full texture */ - cogl_texture_set_region (sub_tex->full_texture, - src_x + copy_dst_x - dst_x, - src_y + copy_dst_y - dst_y, - src_x1 + copy_dst_x - virt_x_1, - src_y1 + copy_dst_y - virt_y_1, - copy_dst_width, - copy_dst_height, - width, - height, - format, - rowstride, - data); - } - } - - /* Free data if owner */ - if (source_bmp_owner) - g_free (source_bmp.data); - - return TRUE; + return cogl_texture_set_region (sub_tex->full_texture, + src_x, src_y, + dst_x + sub_tex->sub_x, + dst_y + sub_tex->sub_y, + dst_width, dst_height, + width, height, + format, + rowstride, + data); } static void @@ -571,7 +436,6 @@ _cogl_sub_texture_get_data (CoglTexture *tex, guint8 *full_data; int byte_size, full_size; gint bpp; - gint px1, py1, px2, py2; gint full_tex_width, full_tex_height; /* FIXME: This gets the full data from the full texture and then @@ -583,19 +447,16 @@ _cogl_sub_texture_get_data (CoglTexture *tex, if (format == COGL_PIXEL_FORMAT_ANY) format = cogl_texture_get_format (sub_tex->full_texture); - _cogl_sub_texture_get_x_pixel_pos (sub_tex, &px1, &px2); - _cogl_sub_texture_get_y_pixel_pos (sub_tex, &py1, &py2); - full_tex_width = cogl_texture_get_width (sub_tex->full_texture); full_tex_height = cogl_texture_get_height (sub_tex->full_texture); /* Rowstride from texture width if none specified */ bpp = _cogl_get_format_bpp (format); if (rowstride == 0) - rowstride = px2 - px1; + rowstride = sub_tex->sub_width * bpp; /* Return byte size if only that requested */ - byte_size = (py2 - py1) * rowstride; + byte_size = sub_tex->sub_height * rowstride; if (data == NULL) return byte_size; @@ -606,32 +467,15 @@ _cogl_sub_texture_get_data (CoglTexture *tex, full_rowstride, full_data); if (full_size) - { - int dst_x, dst_y; - int src_x1, src_y1; - int src_x2, src_y2; - - for (dst_y = py1; dst_y < py2; dst_y += src_y2 - src_y1) - { - _cogl_sub_texture_get_next_chunk (dst_y, py2, full_tex_width, - &src_y1, &src_y2); - - for (dst_x = px1; dst_x < px2; dst_x += src_x2 - src_x1) - { - _cogl_sub_texture_get_next_chunk (dst_x, px2, full_tex_height, - &src_x1, &src_x2); - - _cogl_sub_texture_copy_region (data, full_data, - dst_x - px1, dst_y - py1, - src_x1, src_y1, - src_x2 - src_x1, - src_y2 - src_y1, - rowstride, - full_rowstride, - bpp); - } - } - } + _cogl_sub_texture_copy_region (data, full_data, + 0, 0, + sub_tex->sub_x, + sub_tex->sub_y, + sub_tex->sub_width, + sub_tex->sub_height, + rowstride, + full_rowstride, + bpp); else byte_size = 0; @@ -660,22 +504,16 @@ static gint _cogl_sub_texture_get_width (CoglTexture *tex) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); - gint px1, px2; - _cogl_sub_texture_get_x_pixel_pos (sub_tex, &px1, &px2); - - return px2 - px1; + return sub_tex->sub_width; } static gint _cogl_sub_texture_get_height (CoglTexture *tex) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); - gint py1, py2; - _cogl_sub_texture_get_y_pixel_pos (sub_tex, &py1, &py2); - - return py2 - py1; + return sub_tex->sub_height; } static const CoglTextureVtable @@ -688,9 +526,11 @@ cogl_sub_texture_vtable = _cogl_sub_texture_is_sliced, _cogl_sub_texture_can_hardware_repeat, _cogl_sub_texture_transform_coords_to_gl, + _cogl_sub_texture_transform_quad_coords_to_gl, _cogl_sub_texture_get_gl_texture, _cogl_sub_texture_set_filters, _cogl_sub_texture_ensure_mipmaps, + _cogl_sub_texture_ensure_non_quad_rendering, _cogl_sub_texture_set_wrap_mode_parameter, _cogl_sub_texture_get_format, _cogl_sub_texture_get_gl_format, diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c index 0f53bc1dc..7743f9f2f 100644 --- a/cogl/cogl-texture-2d-sliced.c +++ b/cogl/cogl-texture-2d-sliced.c @@ -1266,6 +1266,19 @@ _cogl_texture_2d_sliced_transform_coords_to_gl (CoglTexture *tex, #endif } +static gboolean +_cogl_texture_2d_sliced_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + if (_cogl_texture_2d_sliced_is_sliced (tex)) + return FALSE; + + _cogl_texture_2d_sliced_transform_coords_to_gl (tex, coords + 0, coords + 1); + _cogl_texture_2d_sliced_transform_coords_to_gl (tex, coords + 2, coords + 3); + + return TRUE; +} + static gboolean _cogl_texture_2d_sliced_get_gl_texture (CoglTexture *tex, GLuint *out_gl_handle, @@ -1362,6 +1375,12 @@ _cogl_texture_2d_sliced_ensure_mipmaps (CoglTexture *tex) tex_2ds->mipmaps_dirty = FALSE; } +static void +_cogl_texture_2d_sliced_ensure_non_quad_rendering (CoglTexture *tex) +{ + /* Nothing needs to be done */ +} + static gboolean _cogl_texture_2d_sliced_set_region (CoglTexture *tex, int src_x, @@ -1673,9 +1692,11 @@ cogl_texture_2d_sliced_vtable = _cogl_texture_2d_sliced_is_sliced, _cogl_texture_2d_sliced_can_hardware_repeat, _cogl_texture_2d_sliced_transform_coords_to_gl, + _cogl_texture_2d_sliced_transform_quad_coords_to_gl, _cogl_texture_2d_sliced_get_gl_texture, _cogl_texture_2d_sliced_set_filters, _cogl_texture_2d_sliced_ensure_mipmaps, + _cogl_texture_2d_sliced_ensure_non_quad_rendering, _cogl_texture_2d_sliced_set_wrap_mode_parameter, _cogl_texture_2d_sliced_get_format, _cogl_texture_2d_sliced_get_gl_format, diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c index f6bf603b2..cd3f3a1ef 100644 --- a/cogl/cogl-texture-2d.c +++ b/cogl/cogl-texture-2d.c @@ -342,6 +342,15 @@ _cogl_texture_2d_transform_coords_to_gl (CoglTexture *tex, anything */ } +static gboolean +_cogl_texture_2d_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + /* The texture coordinates map directly so we don't need to do + anything */ + return TRUE; +} + static gboolean _cogl_texture_2d_get_gl_texture (CoglTexture *tex, GLuint *out_gl_handle, @@ -399,6 +408,12 @@ _cogl_texture_2d_ensure_mipmaps (CoglTexture *tex) tex_2d->mipmaps_dirty = FALSE; } +static void +_cogl_texture_2d_ensure_non_quad_rendering (CoglTexture *tex) +{ + /* Nothing needs to be done */ +} + static gboolean _cogl_texture_2d_set_region (CoglTexture *tex, int src_x, @@ -612,9 +627,11 @@ cogl_texture_2d_vtable = _cogl_texture_2d_is_sliced, _cogl_texture_2d_can_hardware_repeat, _cogl_texture_2d_transform_coords_to_gl, + _cogl_texture_2d_transform_quad_coords_to_gl, _cogl_texture_2d_get_gl_texture, _cogl_texture_2d_set_filters, _cogl_texture_2d_ensure_mipmaps, + _cogl_texture_2d_ensure_non_quad_rendering, _cogl_texture_2d_set_wrap_mode_parameter, _cogl_texture_2d_get_format, _cogl_texture_2d_get_gl_format, diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index ad6449d6d..e59374334 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -84,6 +84,8 @@ struct _CoglTextureVtable void (* transform_coords_to_gl) (CoglTexture *tex, float *s, float *t); + gboolean (* transform_quad_coords_to_gl) (CoglTexture *tex, + float *coords); gboolean (* get_gl_texture) (CoglTexture *tex, GLuint *out_gl_handle, @@ -94,6 +96,7 @@ struct _CoglTextureVtable GLenum mag_filter); void (* ensure_mipmaps) (CoglTexture *tex); + void (* ensure_non_quad_rendering) (CoglTexture *tex); void (* set_wrap_mode_parameter) (CoglTexture *tex, GLenum wrap_mode); @@ -137,6 +140,10 @@ void _cogl_texture_transform_coords_to_gl (CoglHandle handle, float *s, float *t); +gboolean +_cogl_texture_transform_quad_coords_to_gl (CoglHandle handle, + float *coords); + GLenum _cogl_texture_get_gl_format (CoglHandle handle); @@ -152,6 +159,9 @@ _cogl_texture_set_filters (CoglHandle handle, void _cogl_texture_ensure_mipmaps (CoglHandle handle); +void +_cogl_texture_ensure_non_quad_rendering (CoglHandle handle); + /* Utility functions to help uploading a bitmap. These are intended to * be used by CoglTexture implementations or drivers... */ diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 85a6de7cc..09acb0182 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -428,12 +428,13 @@ cogl_texture_new_from_foreign (GLuint gl_handle, CoglHandle cogl_texture_new_from_sub_texture (CoglHandle full_texture, - gfloat tx1, - gfloat ty1, - gfloat tx2, - gfloat ty2) + gint sub_x, + gint sub_y, + gint sub_width, + gint sub_height) { - return _cogl_sub_texture_new (full_texture, tx1, ty1, tx2, ty2); + return _cogl_sub_texture_new (full_texture, sub_x, sub_y, + sub_width, sub_height); } guint @@ -574,6 +575,15 @@ _cogl_texture_transform_coords_to_gl (CoglHandle handle, tex->vtable->transform_coords_to_gl (tex, s, t); } +gboolean +_cogl_texture_transform_quad_coords_to_gl (CoglHandle handle, + float *coords) +{ + CoglTexture *tex = COGL_TEXTURE (handle); + + return tex->vtable->transform_quad_coords_to_gl (tex, coords); +} + GLenum _cogl_texture_get_gl_format (CoglHandle handle) { @@ -625,6 +635,19 @@ _cogl_texture_ensure_mipmaps (CoglHandle handle) tex->vtable->ensure_mipmaps (tex); } +void +_cogl_texture_ensure_non_quad_rendering (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return; + + tex = COGL_TEXTURE (handle); + + return tex->vtable->ensure_non_quad_rendering (tex); +} + gboolean cogl_texture_set_region (CoglHandle handle, gint src_x, diff --git a/cogl/cogl-texture.h b/cogl/cogl-texture.h index 2ab90c841..c0becf944 100644 --- a/cogl/cogl-texture.h +++ b/cogl/cogl-texture.h @@ -320,18 +320,18 @@ gboolean cogl_texture_set_region (CoglHandle handle, /** * cogl_texture_new_from_sub_texture: * @full_texture: a #CoglHandle to an existing texture - * @tx1: X coordinate of the top-left of the subregion - * @ty1: Y coordinate of the top-left of the subregion - * @tx2: X coordinate of the bottom-right of the subregion - * @ty2: Y coordinate of the bottom-right of the subregion + * @sub_x: X coordinate of the top-left of the subregion + * @sub_y: Y coordinate of the top-left of the subregion + * @sub_width: Width in pixels of the subregion + * @sub_height: Height in pixels of the subregion * * Creates a new texture which represents a subregion of another * texture. The GL resources will be shared so that no new texture * data is actually allocated. * - * You can also specify texture coordinates outside the range of [0,1] - * to make a texture that represents a repeated version of another - * texture. + * Sub textures have undefined behaviour texture coordinates outside + * of the range [0,1] are used. They also do not work with + * CoglVertexBuffers. * * Return value: a #CoglHandle to the new texture. * @@ -339,10 +339,10 @@ gboolean cogl_texture_set_region (CoglHandle handle, */ CoglHandle cogl_texture_new_from_sub_texture (CoglHandle full_texture, - gfloat tx1, - gfloat ty1, - gfloat tx2, - gfloat ty2); + gint sub_x, + gint sub_y, + gint sub_width, + gint sub_height); #ifndef COGL_DISABLE_DEPRECATED diff --git a/cogl/cogl-vertex-buffer.c b/cogl/cogl-vertex-buffer.c index 26d09ce01..943b77776 100644 --- a/cogl/cogl-vertex-buffer.c +++ b/cogl/cogl-vertex-buffer.c @@ -1637,6 +1637,11 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer) if (tex_handle == COGL_INVALID_HANDLE) continue; + /* Give the texture a chance to know that we're rendering + non-quad shaped primitives. If the texture is in an atlas it + will be migrated */ + _cogl_texture_ensure_non_quad_rendering (tex_handle); + if (!_cogl_texture_can_hardware_repeat (tex_handle)) { g_warning ("Disabling layer %d of the current source material, " From 12b45aaa3067ccb1bcf6785fc59bfd43b0164120 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 14 Jan 2010 17:57:43 +0000 Subject: [PATCH 21/33] cogl-primitives: Ensure the mipmaps for a layer before logging quads With the atlas texture backend ensuring the mipmaps can make it become a completely different texture which will have different texture coordinates or may even be sliced. Therefore we need to ensure the mipmaps before deciding which quads to log in the journal. This adds a new private function to cogl-material which ensures the mipmaps if needed. --- cogl/cogl-material-private.h | 6 ++++++ cogl/cogl-material.c | 19 +++++++++++++++---- cogl/cogl-primitives.c | 6 ++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/cogl/cogl-material-private.h b/cogl/cogl-material-private.h index c70ea304f..a9cf77a17 100644 --- a/cogl/cogl-material-private.h +++ b/cogl/cogl-material-private.h @@ -204,6 +204,12 @@ typedef enum _CoglMaterialLayerFlags */ gulong _cogl_material_layer_get_flags (CoglHandle layer_handle); +/* + * Ensures the mipmaps are available for the texture in the layer if + * the filter settings would require it + */ +void _cogl_material_layer_ensure_mipmaps (CoglHandle layer_handler); + /* * CoglMaterialFlushFlag: * @COGL_MATERIAL_FLUSH_FALLBACK_MASK: The fallback_layers member is set to diff --git a/cogl/cogl-material.c b/cogl/cogl-material.c index 48de2a77d..35e898f77 100644 --- a/cogl/cogl-material.c +++ b/cogl/cogl-material.c @@ -1326,6 +1326,19 @@ _cogl_material_layer_flush_gl_sampler_state (CoglMaterialLayer *layer, _cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE); } +void +_cogl_material_layer_ensure_mipmaps (CoglHandle layer_handle) +{ + CoglMaterialLayer *layer; + + layer = _cogl_material_layer_pointer_from_handle (layer_handle); + + if (layer->texture && + (is_mipmap_filter (layer->min_filter) || + is_mipmap_filter (layer->mag_filter))) + _cogl_texture_ensure_mipmaps (layer->texture); +} + /* * _cogl_material_flush_layers_gl_state: * @fallback_mask: is a bitmask of the material layers that need to be @@ -1397,6 +1410,8 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, #endif CoglTextureUnit *unit; + _cogl_material_layer_ensure_mipmaps (layer_handle); + new_gl_layer_info.layer0_overridden = layer0_override_texture ? TRUE : FALSE; new_gl_layer_info.fallback = @@ -1407,10 +1422,6 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, tex_handle = layer->texture; if (tex_handle != COGL_INVALID_HANDLE) { - if (is_mipmap_filter (layer->min_filter) - || is_mipmap_filter (layer->mag_filter)) - _cogl_texture_ensure_mipmaps (tex_handle); - _cogl_texture_set_filters (tex_handle, layer->min_filter, layer->mag_filter); diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c index 1215725ec..726cebaf4 100644 --- a/cogl/cogl-primitives.c +++ b/cogl/cogl-primitives.c @@ -401,6 +401,12 @@ _cogl_rectangles_with_multitexture_coords ( != COGL_MATERIAL_LAYER_TYPE_TEXTURE) continue; + /* We need to ensure the mipmaps are ready before deciding + anything else about the texture because it could become + something completely different if it needs to be migrated out + of the atlas */ + _cogl_material_layer_ensure_mipmaps (layer); + tex_handle = cogl_material_layer_get_texture (layer); /* COGL_INVALID_HANDLE textures are handled by From a78246b4032f362dc98fbd11e86a8fe350669693 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 18 Jan 2010 10:53:00 +0000 Subject: [PATCH 22/33] cogl-atlas-texture: Fix premultiplied texture formats When uploading texture data it was just calling cogl_texture_set_data on the large texture. This would attempt to convert the data to the format of the large texture. All of the textures with alpha channels are stored together regardless of whether they are premultiplied so this was causing premultiplied textures to be unpremultiplied again. It now just uploads the data ignoring the premult bit of the format so that it only gets converted once. --- cogl/cogl-atlas-texture.c | 234 ++++++++++++++++++++++++++------------ 1 file changed, 159 insertions(+), 75 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 4bbff22ea..1400919d5 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -419,6 +419,90 @@ _cogl_atlas_texture_ensure_non_quad_rendering (CoglTexture *tex) _cogl_texture_ensure_non_quad_rendering (atlas_tex->sub_texture); } +static gboolean +_cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, + 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) +{ + CoglHandle big_texture; + + _COGL_GET_CONTEXT (ctx, FALSE); + + big_texture = ((atlas_tex->format & COGL_A_BIT) ? + ctx->atlas_alpha_texture : ctx->atlas_no_alpha_texture); + + /* Copy the central data */ + if (!cogl_texture_set_region (big_texture, + src_x, src_y, + dst_x + atlas_tex->rectangle.x + 1, + dst_y + atlas_tex->rectangle.y + 1, + dst_width, + dst_height, + width, height, + format, + rowstride, + data)) + return FALSE; + + /* Update the left edge pixels */ + if (dst_x == 0 && + !cogl_texture_set_region (big_texture, + src_x, src_y, + atlas_tex->rectangle.x, + dst_y + atlas_tex->rectangle.y + 1, + 1, dst_height, + width, height, + format, rowstride, + data)) + return FALSE; + /* Update the right edge pixels */ + if (dst_x + dst_width == atlas_tex->rectangle.width - 2 && + !cogl_texture_set_region (big_texture, + src_x + dst_width - 1, src_y, + atlas_tex->rectangle.x + + atlas_tex->rectangle.width - 1, + dst_y + atlas_tex->rectangle.y + 1, + 1, dst_height, + width, height, + format, rowstride, + data)) + return FALSE; + /* Update the top edge pixels */ + if (dst_y == 0 && + !cogl_texture_set_region (big_texture, + src_x, src_y, + dst_x + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y, + dst_width, 1, + width, height, + format, rowstride, + data)) + return FALSE; + /* Update the bottom edge pixels */ + if (dst_y + dst_height == atlas_tex->rectangle.height - 2 && + !cogl_texture_set_region (big_texture, + src_x, src_y + dst_height - 1, + dst_x + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y + + atlas_tex->rectangle.height - 1, + dst_width, 1, + width, height, + format, rowstride, + data)) + return FALSE; + + return TRUE; +} + static gboolean _cogl_atlas_texture_set_region (CoglTexture *tex, int src_x, @@ -439,74 +523,71 @@ _cogl_atlas_texture_set_region (CoglTexture *tex, pixels to the border */ if (atlas_tex->in_atlas) { - CoglHandle big_texture; + 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; - _COGL_GET_CONTEXT (ctx, FALSE); - - big_texture = ((atlas_tex->format & COGL_A_BIT) ? - ctx->atlas_alpha_texture : ctx->atlas_no_alpha_texture); - - /* Copy the central data */ - if (!cogl_texture_set_region (big_texture, - src_x, src_y, - dst_x + atlas_tex->rectangle.x + 1, - dst_y + atlas_tex->rectangle.y + 1, - dst_width, - dst_height, - width, height, - format, - rowstride, - data)) + /* Check for valid format */ + if (format == COGL_PIXEL_FORMAT_ANY) return FALSE; - /* Update the left edge pixels */ - if (dst_x == 0 && - !cogl_texture_set_region (big_texture, - src_x, src_y, - atlas_tex->rectangle.x, - dst_y + atlas_tex->rectangle.y + 1, - 1, dst_height, - width, height, - format, rowstride, - data)) - return FALSE; - /* Update the right edge pixels */ - if (dst_x + dst_width == atlas_tex->rectangle.width - 2 && - !cogl_texture_set_region (big_texture, - src_x + dst_width - 1, src_y, - atlas_tex->rectangle.x + - atlas_tex->rectangle.width - 1, - dst_y + atlas_tex->rectangle.y + 1, - 1, dst_height, - width, height, - format, rowstride, - data)) - return FALSE; - /* Update the top edge pixels */ - if (dst_y == 0 && - !cogl_texture_set_region (big_texture, - src_x, src_y, - dst_x + atlas_tex->rectangle.x + 1, - atlas_tex->rectangle.y, - dst_width, 1, - width, height, - format, rowstride, - data)) - return FALSE; - /* Update the bottom edge pixels */ - if (dst_y + dst_height == atlas_tex->rectangle.height - 2 && - !cogl_texture_set_region (big_texture, - src_x, src_y + dst_height - 1, - dst_x + atlas_tex->rectangle.x + 1, - atlas_tex->rectangle.y + - atlas_tex->rectangle.height - 1, - dst_width, 1, - width, height, - format, rowstride, - data)) - return FALSE; + /* Shortcut out early if the image is empty */ + if (width == 0 || height == 0) + return TRUE; - 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 (atlas_tex->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; + } + + /* Upload the data ignoring the premult bit */ + success = + _cogl_atlas_texture_set_region_with_border (atlas_tex, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + source_bmp.width, + source_bmp.height, + source_bmp.format & + ~COGL_PREMULT_BIT, + source_bmp.rowstride, + source_bmp.data); + + /* Free data if owner */ + if (source_bmp_owner) + g_free (source_bmp.data); + + return success; } else /* Otherwise we can just forward on to the sub texture */ @@ -962,17 +1043,20 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, &atlas_tex->rectangle); /* Defer to set_region so that we can share the code for copying the - edge pixels to the border */ - _cogl_atlas_texture_set_region (COGL_TEXTURE (atlas_tex), - 0, 0, - 0, 0, - upload_data.bitmap.width, - upload_data.bitmap.height, - upload_data.bitmap.width, - upload_data.bitmap.height, - upload_data.bitmap.format, - upload_data.bitmap.rowstride, - upload_data.bitmap.data); + edge pixels to the border. We don't want to pass the actual + format of the converted texture because otherwise it will get + unpremultiplied. */ + _cogl_atlas_texture_set_region_with_border (atlas_tex, + 0, 0, + 0, 0, + upload_data.bitmap.width, + upload_data.bitmap.height, + upload_data.bitmap.width, + upload_data.bitmap.height, + upload_data.bitmap.format & + ~COGL_PREMULT_BIT, + upload_data.bitmap.rowstride, + upload_data.bitmap.data); return _cogl_atlas_texture_handle_new (atlas_tex); } From 41a915ec002342d881f0bdbc6ecb99bc73537587 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 19 Jan 2010 17:06:28 +0000 Subject: [PATCH 23/33] cogl-atlas-texture: Don't create atlas textures with the premult bit Previously the atlas textures were being created with whatever format the first sub texture is in. Only three formats are supported so this only matters if the first texture is a premultiplied alpha texture. Instead it now masks out the premultiplied bit so that the textures are always either RGB_888 or RGBA_8888. --- cogl/cogl-atlas-texture.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 1400919d5..202f1e41a 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -1015,7 +1015,8 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, atlas_tex->rectangle.height = upload_data.bitmap.height + 2; /* Try to make some space in the atlas for the texture */ - if (!_cogl_atlas_texture_reserve_space (internal_format, + if (!_cogl_atlas_texture_reserve_space (internal_format & + ~COGL_PREMULT_BIT, atlas_ptr, atlas_tex_ptr, atlas_tex, From 67fc6ead78645b112485e3e5854113e1c9dd0074 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 21 Jan 2010 15:34:19 +0000 Subject: [PATCH 24/33] cogl-atlas-texture: Add a debug option to disable the atlas If the user specifies the 'disable-atlas' debug option then no texture will be put in the atlas. --- cogl/cogl-atlas-texture.c | 5 +++++ cogl/cogl-debug.c | 3 ++- cogl/cogl-debug.h | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 202f1e41a..7d22918f0 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -956,6 +956,11 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); + /* Don't put textures in the atlas if the user has explicitly + requested to disable it */ + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_ATLAS)) + return COGL_INVALID_HANDLE; + /* We can't put the texture in the atlas if there are any special flags. This precludes textures with COGL_TEXTURE_NO_ATLAS and COGL_TEXTURE_NO_SLICING from being atlased */ diff --git a/cogl/cogl-debug.c b/cogl/cogl-debug.c index bba75e560..1ee180569 100644 --- a/cogl/cogl-debug.c +++ b/cogl/cogl-debug.c @@ -49,7 +49,8 @@ static const GDebugKey cogl_debug_keys[] = { { "matrices", COGL_DEBUG_MATRICES }, { "force-scanline-paths", COGL_DEBUG_FORCE_SCANLINE_PATHS }, { "atlas", COGL_DEBUG_ATLAS }, - { "dump-atlas-image", COGL_DEBUG_DUMP_ATLAS_IMAGE } + { "dump-atlas-image", COGL_DEBUG_DUMP_ATLAS_IMAGE }, + { "disable-atlas", COGL_DEBUG_DISABLE_ATLAS } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/cogl/cogl-debug.h b/cogl/cogl-debug.h index 864f50a60..0c6fb4de7 100644 --- a/cogl/cogl-debug.h +++ b/cogl/cogl-debug.h @@ -47,7 +47,8 @@ typedef enum { COGL_DEBUG_MATRICES = 1 << 15, COGL_DEBUG_FORCE_SCANLINE_PATHS = 1 << 16, COGL_DEBUG_ATLAS = 1 << 17, - COGL_DEBUG_DUMP_ATLAS_IMAGE = 1 << 18 + COGL_DEBUG_DUMP_ATLAS_IMAGE = 1 << 18, + COGL_DEBUG_DISABLE_ATLAS = 1 << 19 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG From 406c203b422bd492235ffa91816671f4fd26e44c Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 29 Jan 2010 12:19:42 +0000 Subject: [PATCH 25/33] cogl-atlas-texture: Use a single atlas for both RGB and RGBA textures The internal format of the atlas texture is still set to the appropriate format so Cogl will disable blending for textures that are intended to be RGB. This should end up ignoring the alpha channel from the texture in the atlas. This makes the code slightly easier to maintain and should also improve the chances of batching. --- cogl/cogl-atlas-texture.c | 134 ++++++++++++++------------------------ cogl/cogl-context.c | 18 ++--- cogl/cogl-context.h | 8 +-- 3 files changed, 57 insertions(+), 103 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 7d22918f0..545c68db5 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -242,26 +242,20 @@ _cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex) { if (atlas_tex->in_atlas) { - CoglAtlas *atlas; - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - atlas = ((atlas_tex->format & COGL_A_BIT) ? - ctx->atlas_alpha : - ctx->atlas_no_alpha); - - cogl_atlas_remove_rectangle (atlas, &atlas_tex->rectangle); + cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle); COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i", atlas_tex->rectangle.width, atlas_tex->rectangle.height); COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", - cogl_atlas_get_width (atlas), - cogl_atlas_get_height (atlas), - cogl_atlas_get_n_rectangles (atlas), - cogl_atlas_get_remaining_space (atlas) * 100 / - (cogl_atlas_get_width (atlas) * - cogl_atlas_get_height (atlas))); + cogl_atlas_get_width (ctx->atlas), + cogl_atlas_get_height (ctx->atlas), + cogl_atlas_get_n_rectangles (ctx->atlas), + cogl_atlas_get_remaining_space (ctx->atlas) * 100 / + (cogl_atlas_get_width (ctx->atlas) * + cogl_atlas_get_height (ctx->atlas))); atlas_tex->in_atlas = FALSE; } @@ -354,7 +348,6 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) /* Make sure this texture is not in the atlas */ if (atlas_tex->in_atlas) { - CoglHandle atlas_texture; CoglAtlasTextureBlitData blit_data; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -363,11 +356,6 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) cogl_handle_unref (atlas_tex->sub_texture); - if ((atlas_tex->format & COGL_A_BIT)) - atlas_texture = ctx->atlas_alpha_texture; - else - atlas_texture = ctx->atlas_no_alpha_texture; - /* Create a new texture at the right size, not including the border */ atlas_tex->sub_texture = @@ -380,7 +368,7 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) aren't available this will end up having to copy the entire atlas texture */ _cogl_atlas_texture_blit_begin (&blit_data, atlas_tex->sub_texture, - atlas_texture); + ctx->atlas_texture); _cogl_atlas_texture_blit (&blit_data, atlas_tex->rectangle.x + 1, atlas_tex->rectangle.y + 1, @@ -433,15 +421,10 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, unsigned int rowstride, const guint8 *data) { - CoglHandle big_texture; - _COGL_GET_CONTEXT (ctx, FALSE); - big_texture = ((atlas_tex->format & COGL_A_BIT) ? - ctx->atlas_alpha_texture : ctx->atlas_no_alpha_texture); - /* Copy the central data */ - if (!cogl_texture_set_region (big_texture, + if (!cogl_texture_set_region (ctx->atlas_texture, src_x, src_y, dst_x + atlas_tex->rectangle.x + 1, dst_y + atlas_tex->rectangle.y + 1, @@ -455,7 +438,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, /* Update the left edge pixels */ if (dst_x == 0 && - !cogl_texture_set_region (big_texture, + !cogl_texture_set_region (ctx->atlas_texture, src_x, src_y, atlas_tex->rectangle.x, dst_y + atlas_tex->rectangle.y + 1, @@ -466,7 +449,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, return FALSE; /* Update the right edge pixels */ if (dst_x + dst_width == atlas_tex->rectangle.width - 2 && - !cogl_texture_set_region (big_texture, + !cogl_texture_set_region (ctx->atlas_texture, src_x + dst_width - 1, src_y, atlas_tex->rectangle.x + atlas_tex->rectangle.width - 1, @@ -478,7 +461,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, return FALSE; /* Update the top edge pixels */ if (dst_y == 0 && - !cogl_texture_set_region (big_texture, + !cogl_texture_set_region (ctx->atlas_texture, src_x, src_y, dst_x + atlas_tex->rectangle.x + 1, atlas_tex->rectangle.y, @@ -489,7 +472,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, return FALSE; /* Update the bottom edge pixels */ if (dst_y + dst_height == atlas_tex->rectangle.height - 2 && - !cogl_texture_set_region (big_texture, + !cogl_texture_set_region (ctx->atlas_texture, src_x, src_y + dst_height - 1, dst_x + atlas_tex->rectangle.x + 1, atlas_tex->rectangle.y + @@ -654,7 +637,7 @@ _cogl_atlas_texture_get_height (CoglTexture *tex) } static CoglHandle -_cogl_atlas_texture_create_sub_texture (CoglHandle full_texture, +_cogl_atlas_texture_create_sub_texture (CoglHandle full_texture, const CoglAtlasRectangle *rectangle) { /* Create a subtexture for the given rectangle not including the @@ -803,10 +786,7 @@ _cogl_atlas_texture_compare_size_cb (const void *a, } static gboolean -_cogl_atlas_texture_reserve_space (CoglPixelFormat format, - CoglAtlas **atlas_ptr, - CoglHandle *atlas_tex_ptr, - CoglAtlasTexture *new_sub_tex, +_cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex, guint width, guint height) { @@ -819,30 +799,31 @@ _cogl_atlas_texture_reserve_space (CoglPixelFormat format, _COGL_GET_CONTEXT (ctx, FALSE); /* Check if we can fit the rectangle into the existing atlas */ - if (*atlas_ptr && cogl_atlas_add_rectangle (*atlas_ptr, width, height, + if (ctx->atlas && cogl_atlas_add_rectangle (ctx->atlas, width, height, new_sub_tex, &new_sub_tex->rectangle)) { COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", - cogl_atlas_get_width (*atlas_ptr), - cogl_atlas_get_height (*atlas_ptr), - cogl_atlas_get_n_rectangles (*atlas_ptr), - cogl_atlas_get_remaining_space (*atlas_ptr) * 100 / - (cogl_atlas_get_width (*atlas_ptr) * - cogl_atlas_get_height (*atlas_ptr))); + cogl_atlas_get_width (ctx->atlas), + cogl_atlas_get_height (ctx->atlas), + cogl_atlas_get_n_rectangles (ctx->atlas), + cogl_atlas_get_remaining_space (ctx->atlas) * 100 / + (cogl_atlas_get_width (ctx->atlas) * + cogl_atlas_get_height (ctx->atlas))); + return TRUE; } /* We need to reorganise the atlas so we'll get an array of all the textures currently in the atlas. */ data.n_textures = 0; - if (*atlas_ptr == NULL) + if (ctx->atlas == NULL) data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData)); else { data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData) * - (cogl_atlas_get_n_rectangles (*atlas_ptr) + 1)); - cogl_atlas_foreach (*atlas_ptr, _cogl_atlas_texture_get_rectangles_cb, + (cogl_atlas_get_n_rectangles (ctx->atlas) + 1)); + cogl_atlas_foreach (ctx->atlas, _cogl_atlas_texture_get_rectangles_cb, &data); } @@ -858,15 +839,15 @@ _cogl_atlas_texture_reserve_space (CoglPixelFormat format, _cogl_atlas_texture_compare_size_cb); /* Try to create a new atlas that can contain all of the textures */ - if (*atlas_ptr) + if (ctx->atlas) { - atlas_width = cogl_atlas_get_width (*atlas_ptr); - atlas_height = cogl_atlas_get_height (*atlas_ptr); + atlas_width = cogl_atlas_get_width (ctx->atlas); + atlas_height = cogl_atlas_get_height (ctx->atlas); /* If there is enough space in the existing for the new rectangle in the existing atlas we'll start with the same size, otherwise we'll immediately double it */ - if (cogl_atlas_get_remaining_space (*atlas_ptr) < width * height) + if (cogl_atlas_get_remaining_space (ctx->atlas) < width * height) _cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height); } else @@ -892,46 +873,46 @@ _cogl_atlas_texture_reserve_space (CoglPixelFormat format, _cogl_texture_2d_new_with_size (cogl_atlas_get_width (new_atlas), cogl_atlas_get_height (new_atlas), COGL_TEXTURE_NONE, - format); + COGL_PIXEL_FORMAT_RGBA_8888); COGL_NOTE (ATLAS, "Atlas %s with size %ix%i", - *atlas_ptr == NULL || - cogl_atlas_get_width (*atlas_ptr) != + ctx->atlas == NULL || + cogl_atlas_get_width (ctx->atlas) != cogl_atlas_get_width (new_atlas) || - cogl_atlas_get_height (*atlas_ptr) != + cogl_atlas_get_height (ctx->atlas) != cogl_atlas_get_height (new_atlas) ? "resized" : "reorganized", cogl_atlas_get_width (new_atlas), cogl_atlas_get_height (new_atlas)); - if (*atlas_ptr) + if (ctx->atlas) { /* Move all the textures to the right position in the new texture. This will also update the texture's rectangle */ _cogl_atlas_texture_migrate (data.n_textures, data.textures, - *atlas_tex_ptr, + ctx->atlas_texture, new_tex, new_sub_tex); - cogl_atlas_free (*atlas_ptr); - cogl_handle_unref (*atlas_tex_ptr); + cogl_atlas_free (ctx->atlas); + cogl_handle_unref (ctx->atlas_texture); } else /* We know there's only one texture so we can just directly update the rectangle from its new position */ data.textures[0].texture->rectangle = data.textures[0].new_position; - *atlas_ptr = new_atlas; - *atlas_tex_ptr = new_tex; + ctx->atlas = new_atlas; + ctx->atlas_texture = new_tex; COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", - cogl_atlas_get_width (*atlas_ptr), - cogl_atlas_get_height (*atlas_ptr), - cogl_atlas_get_n_rectangles (*atlas_ptr), - cogl_atlas_get_remaining_space (*atlas_ptr) * 100 / - (cogl_atlas_get_width (*atlas_ptr) * - cogl_atlas_get_height (*atlas_ptr))); + cogl_atlas_get_width (ctx->atlas), + cogl_atlas_get_height (ctx->atlas), + cogl_atlas_get_n_rectangles (ctx->atlas), + cogl_atlas_get_remaining_space (ctx->atlas) * 100 / + (cogl_atlas_get_width (ctx->atlas) * + cogl_atlas_get_height (ctx->atlas))); ret = TRUE; } @@ -949,8 +930,6 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, CoglAtlasTexture *atlas_tex; CoglBitmap *bmp = (CoglBitmap *) bmp_handle; CoglTextureUploadData upload_data; - CoglAtlas **atlas_ptr; - CoglHandle *atlas_tex_ptr; _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); @@ -1000,17 +979,6 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, return COGL_INVALID_HANDLE; } - if ((internal_format & COGL_A_BIT)) - { - atlas_ptr = &ctx->atlas_alpha; - atlas_tex_ptr = &ctx->atlas_alpha_texture; - } - else - { - atlas_ptr = &ctx->atlas_no_alpha; - atlas_tex_ptr = &ctx->atlas_no_alpha_texture; - } - /* We need to allocate the texture now because we need the pointer to set as the data for the rectangle in the atlas */ atlas_tex = g_new (CoglAtlasTexture, 1); @@ -1020,11 +988,7 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, atlas_tex->rectangle.height = upload_data.bitmap.height + 2; /* Try to make some space in the atlas for the texture */ - if (!_cogl_atlas_texture_reserve_space (internal_format & - ~COGL_PREMULT_BIT, - atlas_ptr, - atlas_tex_ptr, - atlas_tex, + if (!_cogl_atlas_texture_reserve_space (atlas_tex, atlas_tex->rectangle.width, atlas_tex->rectangle.height)) { @@ -1035,7 +999,7 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, if (!_cogl_texture_upload_data_convert (&upload_data, internal_format)) { - cogl_atlas_remove_rectangle (*atlas_ptr, &atlas_tex->rectangle); + cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle); g_free (atlas_tex); _cogl_texture_upload_data_free (&upload_data); return COGL_INVALID_HANDLE; @@ -1045,7 +1009,7 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, atlas_tex->format = internal_format; atlas_tex->in_atlas = TRUE; atlas_tex->sub_texture = - _cogl_atlas_texture_create_sub_texture (*atlas_tex_ptr, + _cogl_atlas_texture_create_sub_texture (ctx->atlas_texture, &atlas_tex->rectangle); /* Defer to set_region so that we can share the code for copying the diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index d144fe557..850789b80 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -147,10 +147,8 @@ cogl_create_context (void) cogl_enable (enable_flags); _cogl_flush_face_winding (); - _context->atlas_alpha = NULL; - _context->atlas_no_alpha = NULL; - _context->atlas_alpha_texture = COGL_INVALID_HANDLE; - _context->atlas_no_alpha_texture = COGL_INVALID_HANDLE; + _context->atlas = NULL; + _context->atlas_texture = COGL_INVALID_HANDLE; return TRUE; } @@ -193,14 +191,10 @@ _cogl_destroy_context () if (_context->default_material) cogl_handle_unref (_context->default_material); - if (_context->atlas_alpha) - cogl_atlas_free (_context->atlas_alpha); - if (_context->atlas_no_alpha) - cogl_atlas_free (_context->atlas_no_alpha); - if (_context->atlas_alpha_texture) - cogl_handle_unref (_context->atlas_alpha_texture); - if (_context->atlas_no_alpha_texture) - cogl_handle_unref (_context->atlas_no_alpha_texture); + if (_context->atlas) + cogl_atlas_free (_context->atlas); + if (_context->atlas_texture) + cogl_handle_unref (_context->atlas_texture); g_free (_context); } diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h index f4fff5b78..ce84b01cb 100644 --- a/cogl/cogl-context.h +++ b/cogl/cogl-context.h @@ -112,12 +112,8 @@ typedef struct CoglHandle texture_download_material; - /* Separate atlases for textures with and without alpha */ - CoglAtlas *atlas_alpha; - CoglAtlas *atlas_no_alpha; - /* Textures for the two atlases */ - CoglHandle atlas_alpha_texture; - CoglHandle atlas_no_alpha_texture; + CoglAtlas *atlas; + CoglHandle atlas_texture; CoglContextDriver drv; } CoglContext; From a6f061e41fa97267c35e15869ba91d21de2ad1f2 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 29 Jan 2010 15:15:08 +0000 Subject: [PATCH 26/33] cogl: Do the premult conversion in-place rather than copying to a new buffer The premult part of _cogl_convert_premult has now been split out as _cogl_convert_premult_status. _cogl_convert_premult has been renamed to _cogl_convert_format to make it less confusing. The premult conversion is now done in-place instead of copying the buffer. Previously it was copying the buffer once for the format conversion and then copying it again for the premult conversion. The premult conversion never changes the size of the buffer so it's quite easy to do in place. We can also use the separated out function independently. --- cogl/cogl-atlas-texture.c | 6 +- cogl/cogl-bitmap-fallback.c | 142 +++++++++++++--------------------- cogl/cogl-bitmap-pixbuf.c | 6 +- cogl/cogl-bitmap-private.h | 22 +++--- cogl/cogl-bitmap.c | 102 +++++++++--------------- cogl/cogl-texture-2d-sliced.c | 12 +-- cogl/cogl-texture-2d.c | 12 +-- cogl/cogl-texture.c | 6 +- 8 files changed, 123 insertions(+), 185 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 545c68db5..efd8052a5 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -543,9 +543,9 @@ _cogl_atlas_texture_set_region (CoglTexture *tex, if (closest_format != format) { /* Convert to required format */ - success = _cogl_bitmap_convert_and_premult (&source_bmp, - &temp_bmp, - closest_format); + success = _cogl_bitmap_convert_format_and_premult (&source_bmp, + &temp_bmp, + closest_format); /* Swap bitmaps if succeeded */ if (!success) return FALSE; diff --git a/cogl/cogl-bitmap-fallback.c b/cogl/cogl-bitmap-fallback.c index ddff6ea40..097cc5292 100644 --- a/cogl/cogl-bitmap-fallback.c +++ b/cogl/cogl-bitmap-fallback.c @@ -150,7 +150,7 @@ _cogl_rgba_to_abgr (const guchar *src, guchar *dst) /* (Un)Premultiplication */ inline static void -_cogl_unpremult_alpha_0 (const guchar *src, guchar *dst) +_cogl_unpremult_alpha_0 (guchar *dst) { dst[0] = 0; dst[1] = 0; @@ -159,58 +159,58 @@ _cogl_unpremult_alpha_0 (const guchar *src, guchar *dst) } inline static void -_cogl_unpremult_alpha_last (const guchar *src, guchar *dst) +_cogl_unpremult_alpha_last (guchar *dst) { - guchar alpha = src[3]; + guchar alpha = dst[3]; - dst[0] = (src[0] * 255) / alpha; - dst[1] = (src[1] * 255) / alpha; - dst[2] = (src[2] * 255) / alpha; - dst[3] = alpha; + dst[0] = (dst[0] * 255) / alpha; + dst[1] = (dst[1] * 255) / alpha; + dst[2] = (dst[2] * 255) / alpha; } inline static void -_cogl_unpremult_alpha_first (const guchar *src, guchar *dst) +_cogl_unpremult_alpha_first (guchar *dst) { - guchar alpha = src[0]; + guchar alpha = dst[0]; - dst[0] = alpha; - dst[1] = (src[1] * 255) / alpha; - dst[2] = (src[2] * 255) / alpha; - dst[3] = (src[3] * 255) / alpha; + dst[1] = (dst[1] * 255) / alpha; + dst[2] = (dst[2] * 255) / alpha; + dst[3] = (dst[3] * 255) / alpha; } /* No division form of floor((c*a + 128)/255) (I first encountered * this in the RENDER implementation in the X server.) Being exact * is important for a == 255 - we want to get exactly c. */ -#define MULT(d,c,a,t) G_STMT_START { t = c * a + 128; d = ((t >> 8) + t) >> 8; } G_STMT_END +#define MULT(d,a,t) \ + G_STMT_START { \ + t = d * a + 128; \ + d = ((t >> 8) + t) >> 8; \ + } G_STMT_END inline static void -_cogl_premult_alpha_last (const guchar *src, guchar *dst) +_cogl_premult_alpha_last (guchar *dst) { - guchar alpha = src[3]; + guchar alpha = dst[3]; /* Using a separate temporary per component has given slightly better * code generation with GCC in the past; it shouldn't do any worse in * any case. */ guint t1, t2, t3; - MULT(dst[0], src[0], alpha, t1); - MULT(dst[1], src[1], alpha, t2); - MULT(dst[2], src[2], alpha, t3); - dst[3] = alpha; + MULT(dst[0], alpha, t1); + MULT(dst[1], alpha, t2); + MULT(dst[2], alpha, t3); } inline static void -_cogl_premult_alpha_first (const guchar *src, guchar *dst) +_cogl_premult_alpha_first (guchar *dst) { - guchar alpha = src[0]; + guchar alpha = dst[0]; guint t1, t2, t3; - dst[0] = alpha; - MULT(dst[1], src[1], alpha, t1); - MULT(dst[2], src[2], alpha, t2); - MULT(dst[3], src[3], alpha, t3); + MULT(dst[1], alpha, t1); + MULT(dst[2], alpha, t2); + MULT(dst[3], alpha, t3); } #undef MULT @@ -342,56 +342,39 @@ _cogl_bitmap_fallback_convert (const CoglBitmap *bmp, } gboolean -_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp) +_cogl_bitmap_fallback_unpremult (CoglBitmap *bmp) { - guchar *src; - guchar *dst; - gint bpp; + guchar *p; gint x,y; /* Make sure format supported for un-premultiplication */ if (!_cogl_bitmap_fallback_can_unpremult (bmp->format)) return FALSE; - bpp = _cogl_get_format_bpp (bmp->format); - - /* Initialize destination bitmap */ - *dst_bmp = *bmp; - dst_bmp->format = (bmp->format & COGL_UNPREMULT_MASK); - - /* Allocate a new buffer to hold converted data */ - dst_bmp->data = g_malloc (sizeof(guchar) - * dst_bmp->height - * dst_bmp->rowstride); - for (y = 0; y < bmp->height; y++) { - src = (guchar*)bmp->data + y * bmp->rowstride; - dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride; + p = (guchar*) bmp->data + y * bmp->rowstride; if (bmp->format & COGL_AFIRST_BIT) { for (x = 0; x < bmp->width; x++) { - if (src[0] == 0) - _cogl_unpremult_alpha_0 (src, dst); + if (p[0] == 0) + _cogl_unpremult_alpha_0 (p); else - _cogl_unpremult_alpha_first (src, dst); - src += bpp; - dst += bpp; + _cogl_unpremult_alpha_first (p); + p += 4; } } else { for (x = 0; x < bmp->width; x++) { - if (src[0] == 0) - _cogl_unpremult_alpha_0 (src, dst); + if (p[3] == 0) + _cogl_unpremult_alpha_0 (p); else - _cogl_unpremult_alpha_last (src, dst); - src += bpp; - dst += bpp; + _cogl_unpremult_alpha_last (p); + p += 4; } } } @@ -400,52 +383,35 @@ _cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, } gboolean -_cogl_bitmap_fallback_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp) +_cogl_bitmap_fallback_premult (CoglBitmap *bmp) { - guchar *src; - guchar *dst; - gint bpp; + guchar *p; gint x,y; /* Make sure format supported for un-premultiplication */ if (!_cogl_bitmap_fallback_can_premult (bmp->format)) return FALSE; - bpp = _cogl_get_format_bpp (bmp->format); - - /* Initialize destination bitmap */ - *dst_bmp = *bmp; - dst_bmp->format |= COGL_PREMULT_BIT; - - /* Allocate a new buffer to hold converted data */ - dst_bmp->data = g_malloc (sizeof(guchar) - * dst_bmp->height - * dst_bmp->rowstride); - for (y = 0; y < bmp->height; y++) { - src = (guchar*)bmp->data + y * bmp->rowstride; - dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride; + p = (guchar*) bmp->data + y * bmp->rowstride; if (bmp->format & COGL_AFIRST_BIT) - { - for (x = 0; x < bmp->width; x++) - { - _cogl_premult_alpha_first (src, dst); - src += bpp; - dst += bpp; - } - } + { + for (x = 0; x < bmp->width; x++) + { + _cogl_premult_alpha_first (p); + p += 4; + } + } else - { - for (x = 0; x < bmp->width; x++) - { - _cogl_premult_alpha_last (src, dst); - src += bpp; - dst += bpp; - } - } + { + for (x = 0; x < bmp->width; x++) + { + _cogl_premult_alpha_last (p); + p += 4; + } + } } return TRUE; diff --git a/cogl/cogl-bitmap-pixbuf.c b/cogl/cogl-bitmap-pixbuf.c index 79ed62a7f..b1a756a24 100644 --- a/cogl/cogl-bitmap-pixbuf.c +++ b/cogl/cogl-bitmap-pixbuf.c @@ -64,15 +64,13 @@ _cogl_bitmap_convert (const CoglBitmap *bmp, } gboolean -_cogl_bitmap_unpremult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp) +_cogl_bitmap_unpremult (CoglBitmap *dst_bmp) { return FALSE; } gboolean -_cogl_bitmap_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp) +_cogl_bitmap_premult (CoglBitmap *dst_bmp) { return FALSE; } diff --git a/cogl/cogl-bitmap-private.h b/cogl/cogl-bitmap-private.h index b6e425733..803e10bb0 100644 --- a/cogl/cogl-bitmap-private.h +++ b/cogl/cogl-bitmap-private.h @@ -68,20 +68,16 @@ _cogl_bitmap_fallback_convert (const CoglBitmap *bmp, CoglPixelFormat dst_format); gboolean -_cogl_bitmap_unpremult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp); +_cogl_bitmap_unpremult (CoglBitmap *dst_bmp); gboolean -_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp); +_cogl_bitmap_fallback_unpremult (CoglBitmap *dst_bmp); gboolean -_cogl_bitmap_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp); +_cogl_bitmap_premult (CoglBitmap *dst_bmp); gboolean -_cogl_bitmap_fallback_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp); +_cogl_bitmap_fallback_premult (CoglBitmap *dst_bmp); gboolean _cogl_bitmap_from_file (CoglBitmap *bmp, @@ -93,9 +89,13 @@ _cogl_bitmap_fallback_from_file (CoglBitmap *bmp, const gchar *filename); gboolean -_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp, - CoglPixelFormat dst_format); +_cogl_bitmap_convert_premult_status (CoglBitmap *bmp, + CoglPixelFormat dst_format); + +gboolean +_cogl_bitmap_convert_format_and_premult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format); void _cogl_bitmap_copy_subregion (CoglBitmap *src, diff --git a/cogl/cogl-bitmap.c b/cogl/cogl-bitmap.c index 3dc7fd534..fe3cbdb72 100644 --- a/cogl/cogl-bitmap.c +++ b/cogl/cogl-bitmap.c @@ -61,84 +61,58 @@ _cogl_get_format_bpp (CoglPixelFormat format) } gboolean -_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp, - CoglPixelFormat dst_format) +_cogl_bitmap_convert_premult_status (CoglBitmap *bmp, + CoglPixelFormat dst_format) { - CoglBitmap tmp_bmp = *bmp; - CoglBitmap new_bmp = *bmp; - gboolean new_bmp_owner = FALSE; + /* Do we need to unpremultiply? */ + if ((bmp->format & COGL_PREMULT_BIT) > 0 && + (dst_format & COGL_PREMULT_BIT) == 0) + /* Try unpremultiplying using imaging library */ + return (_cogl_bitmap_unpremult (bmp) + /* ... or try fallback */ + || _cogl_bitmap_fallback_unpremult (bmp)); + /* Do we need to premultiply? */ + if ((bmp->format & COGL_PREMULT_BIT) == 0 && + (dst_format & COGL_PREMULT_BIT) > 0) + /* Try premultiplying using imaging library */ + return (_cogl_bitmap_premult (bmp) + /* ... or try fallback */ + || _cogl_bitmap_fallback_premult (bmp)); + + return TRUE; +} + +gboolean +_cogl_bitmap_convert_format_and_premult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format) +{ /* Is base format different (not considering premult status)? */ if ((bmp->format & COGL_UNPREMULT_MASK) != (dst_format & COGL_UNPREMULT_MASK)) { /* Try converting using imaging library */ - if (!_cogl_bitmap_convert (&new_bmp, &tmp_bmp, dst_format)) - { - /* ... or try fallback */ - if (!_cogl_bitmap_fallback_convert (&new_bmp, &tmp_bmp, dst_format)) - return FALSE; - } - - /* Update bitmap with new data */ - new_bmp = tmp_bmp; - new_bmp_owner = TRUE; + if (!_cogl_bitmap_convert (bmp, dst_bmp, dst_format)) + { + /* ... or try fallback */ + if (!_cogl_bitmap_fallback_convert (bmp, dst_bmp, dst_format)) + return FALSE; + } } - - /* Do we need to unpremultiply */ - if ((bmp->format & COGL_PREMULT_BIT) > 0 && - (dst_format & COGL_PREMULT_BIT) == 0) + else { - /* Try unpremultiplying using imaging library */ - if (!_cogl_bitmap_unpremult (&new_bmp, &tmp_bmp)) - { - /* ... or try fallback */ - if (!_cogl_bitmap_fallback_unpremult (&new_bmp, &tmp_bmp)) - { - if (new_bmp_owner) - g_free (new_bmp.data); - - return FALSE; - } - } - - /* Update bitmap with new data */ - if (new_bmp_owner) - g_free (new_bmp.data); - - new_bmp = tmp_bmp; - new_bmp_owner = TRUE; + /* Copy the bitmap so that we can premultiply in-place */ + *dst_bmp = *bmp; + dst_bmp->data = g_memdup (bmp->data, bmp->rowstride * bmp->height); } - /* Do we need to premultiply */ - if ((bmp->format & COGL_PREMULT_BIT) == 0 && - (dst_format & COGL_PREMULT_BIT) > 0) + if (!_cogl_bitmap_convert_premult_status (dst_bmp, dst_format)) { - /* Try premultiplying using imaging library */ - if (!_cogl_bitmap_premult (&new_bmp, &tmp_bmp)) - { - /* ... or try fallback */ - if (!_cogl_bitmap_fallback_premult (&new_bmp, &tmp_bmp)) - { - if (new_bmp_owner) - g_free (new_bmp.data); - - return FALSE; - } - } - - /* Update bitmap with new data */ - if (new_bmp_owner) - g_free (new_bmp.data); - - new_bmp = tmp_bmp; - new_bmp_owner = TRUE; + g_free (dst_bmp->data); + return FALSE; } - /* Output new bitmap info */ - *dst_bmp = new_bmp; - return TRUE; } diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c index 7743f9f2f..9716062d2 100644 --- a/cogl/cogl-texture-2d-sliced.c +++ b/cogl/cogl-texture-2d-sliced.c @@ -1433,9 +1433,9 @@ _cogl_texture_2d_sliced_set_region (CoglTexture *tex, if (closest_format != format) { /* Convert to required format */ - success = _cogl_bitmap_convert_and_premult (&source_bmp, - &temp_bmp, - closest_format); + success = _cogl_bitmap_convert_format_and_premult (&source_bmp, + &temp_bmp, + closest_format); /* Swap bitmaps if succeeded */ if (!success) return FALSE; @@ -1635,9 +1635,9 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, if (closest_format != format) { /* Convert to requested format */ - success = _cogl_bitmap_convert_and_premult (&target_bmp, - &new_bmp, - format); + success = _cogl_bitmap_convert_format_and_premult (&target_bmp, + &new_bmp, + format); /* Free intermediate data and return if failed */ g_free (target_bmp.data); diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c index cd3f3a1ef..8c8a31c5a 100644 --- a/cogl/cogl-texture-2d.c +++ b/cogl/cogl-texture-2d.c @@ -466,9 +466,9 @@ _cogl_texture_2d_set_region (CoglTexture *tex, if (closest_format != format) { /* Convert to required format */ - success = _cogl_bitmap_convert_and_premult (&source_bmp, - &temp_bmp, - closest_format); + success = _cogl_bitmap_convert_format_and_premult (&source_bmp, + &temp_bmp, + closest_format); /* Swap bitmaps if succeeded */ if (!success) return FALSE; @@ -570,9 +570,9 @@ _cogl_texture_2d_get_data (CoglTexture *tex, if (closest_format != format) { /* Convert to requested format */ - success = _cogl_bitmap_convert_and_premult (&target_bmp, - &new_bmp, - format); + success = _cogl_bitmap_convert_format_and_premult (&target_bmp, + &new_bmp, + format); /* Free intermediate data and return if failed */ g_free (target_bmp.data); diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 09acb0182..fe349f725 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -193,9 +193,9 @@ _cogl_texture_upload_data_convert (CoglTextureUploadData *data, /* Convert to internal format */ if (internal_format != data->bitmap.format) { - success = _cogl_bitmap_convert_and_premult (&data->bitmap, - &new_bitmap, - internal_format); + success = _cogl_bitmap_convert_format_and_premult (&data->bitmap, + &new_bitmap, + internal_format); if (!success) return FALSE; From e20c98e54899497fce26f60e10a8787e1ed6fec4 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 1 Feb 2010 12:11:58 +0000 Subject: [PATCH 27/33] cogl: Let GL do the format conversion when uploading texture data Cogl accepts a pixel format for both the data in memory and the internal format to be used for the texture. If they do not match then it would convert them using the CoglBitmap functions before uploading the data. However, GL also lets you specify both formats so it makes more sense to let GL do the conversion. The driver may need the texture in a specific format so it may end up being converted anyway. The cogl_texture_upload_data functions have been removed and replaced with a single function to prepare the bitmap. This will only do the premultiplication conversion because that is the only part that GL can't do directly. --- cogl/cogl-atlas-texture.c | 111 +++++++++-------- cogl/cogl-texture-2d-sliced.c | 220 +++++++++++++++++++--------------- cogl/cogl-texture-2d.c | 92 +++++++------- cogl/cogl-texture-private.h | 44 ++----- cogl/cogl-texture.c | 134 ++++++++++----------- 5 files changed, 289 insertions(+), 312 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index efd8052a5..0c018f056 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -508,11 +508,8 @@ _cogl_atlas_texture_set_region (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; + CoglBitmap tmp_bmp; + gboolean tmp_bmp_owner = FALSE; gboolean success; /* Check for valid format */ @@ -533,25 +530,14 @@ _cogl_atlas_texture_set_region (CoglTexture *tex, 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 (atlas_tex->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_format_and_premult (&source_bmp, - &temp_bmp, - closest_format); - - /* Swap bitmaps if succeeded */ - if (!success) return FALSE; - source_bmp = temp_bmp; - source_bmp_owner = TRUE; - } + /* Prepare the bitmap so that it will do the premultiplication + conversion */ + _cogl_texture_prepare_for_upload (&source_bmp, + atlas_tex->format, + NULL, + &tmp_bmp, + &tmp_bmp_owner, + NULL, NULL, NULL); /* Upload the data ignoring the premult bit */ success = @@ -559,16 +545,16 @@ _cogl_atlas_texture_set_region (CoglTexture *tex, src_x, src_y, dst_x, dst_y, dst_width, dst_height, - source_bmp.width, - source_bmp.height, - source_bmp.format & + tmp_bmp.width, + tmp_bmp.height, + tmp_bmp.format & ~COGL_PREMULT_BIT, - source_bmp.rowstride, - source_bmp.data); + tmp_bmp.rowstride, + tmp_bmp.data); /* Free data if owner */ - if (source_bmp_owner) - g_free (source_bmp.data); + if (tmp_bmp_owner) + g_free (tmp_bmp.data); return success; } @@ -927,9 +913,13 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, CoglTextureFlags flags, CoglPixelFormat internal_format) { - CoglAtlasTexture *atlas_tex; - CoglBitmap *bmp = (CoglBitmap *) bmp_handle; - CoglTextureUploadData upload_data; + CoglAtlasTexture *atlas_tex; + CoglBitmap *bmp = (CoglBitmap *) bmp_handle; + CoglBitmap dst_bmp; + gboolean dst_bmp_owner; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); @@ -956,18 +946,18 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, if (!cogl_features_available (COGL_FEATURE_TEXTURE_READ_PIXELS)) return COGL_INVALID_HANDLE; - upload_data.bitmap = *bmp; - upload_data.bitmap_owner = FALSE; - - if (!_cogl_texture_upload_data_prepare_format (&upload_data, - &internal_format)) - { - _cogl_texture_upload_data_free (&upload_data); - return COGL_INVALID_HANDLE; - } - COGL_NOTE (ATLAS, "Adding texture of size %ix%i", bmp->width, bmp->height); + if (!_cogl_texture_prepare_for_upload (bmp, + internal_format, + &internal_format, + NULL, + NULL, + &gl_intformat, + &gl_format, + &gl_type)) + return COGL_INVALID_HANDLE; + /* If the texture is in a strange format then we can't use it */ if (internal_format != COGL_PIXEL_FORMAT_RGB_888 && (internal_format & ~COGL_PREMULT_BIT) != COGL_PIXEL_FORMAT_RGBA_8888) @@ -975,7 +965,6 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, COGL_NOTE (ATLAS, "Texture can not be added because the " "format is unsupported"); - _cogl_texture_upload_data_free (&upload_data); return COGL_INVALID_HANDLE; } @@ -984,8 +973,8 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, atlas_tex = g_new (CoglAtlasTexture, 1); /* We need to fill in the texture size now because it is used in the reserve_space function below. We add two pixels for the border */ - atlas_tex->rectangle.width = upload_data.bitmap.width + 2; - atlas_tex->rectangle.height = upload_data.bitmap.height + 2; + atlas_tex->rectangle.width = bmp->width + 2; + atlas_tex->rectangle.height = bmp->height + 2; /* Try to make some space in the atlas for the texture */ if (!_cogl_atlas_texture_reserve_space (atlas_tex, @@ -993,15 +982,20 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, atlas_tex->rectangle.height)) { g_free (atlas_tex); - _cogl_texture_upload_data_free (&upload_data); return COGL_INVALID_HANDLE; } - if (!_cogl_texture_upload_data_convert (&upload_data, internal_format)) + if (!_cogl_texture_prepare_for_upload (bmp, + internal_format, + &internal_format, + &dst_bmp, + &dst_bmp_owner, + &gl_intformat, + &gl_format, + &gl_type)) { cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle); g_free (atlas_tex); - _cogl_texture_upload_data_free (&upload_data); return COGL_INVALID_HANDLE; } @@ -1019,14 +1013,17 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, _cogl_atlas_texture_set_region_with_border (atlas_tex, 0, 0, 0, 0, - upload_data.bitmap.width, - upload_data.bitmap.height, - upload_data.bitmap.width, - upload_data.bitmap.height, - upload_data.bitmap.format & + dst_bmp.width, + dst_bmp.height, + dst_bmp.width, + dst_bmp.height, + dst_bmp.format & ~COGL_PREMULT_BIT, - upload_data.bitmap.rowstride, - upload_data.bitmap.data); + dst_bmp.rowstride, + dst_bmp.data); + + if (dst_bmp_owner) + g_free (dst_bmp.data); return _cogl_atlas_texture_handle_new (atlas_tex); } diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c index 9716062d2..0cfef1f58 100644 --- a/cogl/cogl-texture-2d-sliced.c +++ b/cogl/cogl-texture-2d-sliced.c @@ -213,7 +213,10 @@ _cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds, static gboolean _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, - CoglTextureUploadData *upload_data) + CoglBitmap *bmp, + GLenum gl_intformat, + GLenum gl_format, + GLenum gl_type) { CoglSpan *x_span; CoglSpan *y_span; @@ -222,11 +225,10 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, gint x,y; guchar *waste_buf; - bpp = _cogl_get_format_bpp (upload_data->bitmap.format); + bpp = _cogl_get_format_bpp (bmp->format); - waste_buf = - _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds, - upload_data->bitmap.format); + waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds, + bmp->format); /* Iterate vertical slices */ for (y = 0; y < tex_2ds->slice_y_spans->len; ++y) @@ -252,27 +254,26 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, 0, /* dst y */ x_span->size - x_span->waste, /* width */ y_span->size - y_span->waste, /* height */ - &upload_data->bitmap, - upload_data->gl_format, - upload_data->gl_type); + bmp, + gl_format, + gl_type); /* Keep a copy of the first pixel if needed */ if (tex_2ds->first_pixels) { memcpy (tex_2ds->first_pixels[slice_num].data, - upload_data->bitmap.data + x_span->start * bpp - + y_span->start * upload_data->bitmap.rowstride, + bmp->data + x_span->start * bpp + + y_span->start * bmp->rowstride, bpp); - tex_2ds->first_pixels[slice_num].gl_format = - upload_data->gl_format; - tex_2ds->first_pixels[slice_num].gl_type = upload_data->gl_type; + tex_2ds->first_pixels[slice_num].gl_format = gl_format; + tex_2ds->first_pixels[slice_num].gl_type = gl_type; } /* Fill the waste with a copies of the rightmost pixels */ if (x_span->waste > 0) { - const guchar *src = upload_data->bitmap.data - + y_span->start * upload_data->bitmap.rowstride + const guchar *src = bmp->data + + y_span->start * bmp->rowstride + (x_span->start + x_span->size - x_span->waste - 1) * bpp; guchar *dst = waste_buf; guint wx, wy; @@ -284,7 +285,7 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, memcpy (dst, src, bpp); dst += bpp; } - src += upload_data->bitmap.rowstride; + src += bmp->rowstride; } _cogl_texture_driver_prep_gl_for_pixels_upload ( @@ -296,15 +297,15 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, 0, x_span->waste, y_span->size - y_span->waste, - upload_data->gl_format, upload_data->gl_type, + gl_format, gl_type, waste_buf) ); } if (y_span->waste > 0) { - const guchar *src = upload_data->bitmap.data + const guchar *src = bmp->data + ((y_span->start + y_span->size - y_span->waste - 1) - * upload_data->bitmap.rowstride) + * bmp->rowstride) + x_span->start * bpp; guchar *dst = waste_buf; guint wy, wx; @@ -330,7 +331,7 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, y_span->size - y_span->waste, x_span->size, y_span->waste, - upload_data->gl_format, upload_data->gl_type, + gl_format, gl_type, waste_buf) ); } } @@ -685,7 +686,10 @@ _cogl_texture_2d_sliced_set_wrap_mode_parameter (CoglTexture *tex, static gboolean _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, - const CoglTextureUploadData *upload_data) + gint width, gint height, + GLenum gl_intformat, + GLenum gl_format, + GLenum gl_type) { gint max_width; gint max_height; @@ -703,15 +707,15 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, /* Initialize size of largest slice according to supported features */ if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT)) { - max_width = upload_data->bitmap.width; - max_height = upload_data->bitmap.height; + max_width = width; + max_height = height; tex_2ds->gl_target = GL_TEXTURE_2D; slices_for_size = _cogl_rect_slices_for_size; } else { - max_width = cogl_util_next_p2 (upload_data->bitmap.width); - max_height = cogl_util_next_p2 (upload_data->bitmap.height); + max_width = cogl_util_next_p2 (width); + max_height = cogl_util_next_p2 (height); tex_2ds->gl_target = GL_TEXTURE_2D; slices_for_size = _cogl_pot_slices_for_size; } @@ -723,8 +727,8 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, /* Check if size supported else bail out */ if (!_cogl_texture_driver_size_supported (tex_2ds->gl_target, - upload_data->gl_intformat, - upload_data->gl_type, + gl_intformat, + gl_type, max_width, max_height)) { @@ -746,19 +750,19 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, /* Add a single span for width and height */ span.start = 0; span.size = max_width; - span.waste = max_width - upload_data->bitmap.width; + span.waste = max_width - width; g_array_append_val (tex_2ds->slice_x_spans, span); span.size = max_height; - span.waste = max_height - upload_data->bitmap.height; + span.waste = max_height - 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_2ds->gl_target, - upload_data->gl_intformat, - upload_data->gl_type, + gl_intformat, + gl_type, max_width, max_height)) { @@ -773,11 +777,11 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, } /* Determine the slices required to cover the bitmap area */ - n_x_slices = slices_for_size (upload_data->bitmap.width, + n_x_slices = slices_for_size (width, max_width, tex_2ds->max_waste, NULL); - n_y_slices = slices_for_size (upload_data->bitmap.height, + n_y_slices = slices_for_size (height, max_height, tex_2ds->max_waste, NULL); @@ -791,11 +795,11 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, n_y_slices); /* Fill span arrays with info */ - slices_for_size (upload_data->bitmap.width, + slices_for_size (width, max_width, tex_2ds->max_waste, tex_2ds->slice_x_spans); - slices_for_size (upload_data->bitmap.height, + slices_for_size (height, max_height, tex_2ds->max_waste, tex_2ds->slice_y_spans); } @@ -845,15 +849,15 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, /* Setup texture parameters */ GE( _cogl_texture_driver_bind (tex_2ds->gl_target, gl_handles[y * n_x_slices + x], - upload_data->gl_intformat) ); + gl_intformat) ); _cogl_texture_driver_try_setting_gl_border_color (tex_2ds->gl_target, transparent_color); /* Pass NULL data to init size and internal format */ - GE( glTexImage2D (tex_2ds->gl_target, 0, upload_data->gl_intformat, + GE( glTexImage2D (tex_2ds->gl_target, 0, gl_intformat, x_span->size, y_span->size, 0, - upload_data->gl_format, upload_data->gl_type, 0) ); + gl_format, gl_type, 0) ); } } @@ -893,11 +897,14 @@ _cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds) static gboolean _cogl_texture_2d_sliced_upload_from_data - (CoglTexture2DSliced *tex_2ds, - CoglTextureUploadData *upload_data, - CoglPixelFormat internal_format) + (CoglTexture2DSliced *tex_2ds, + CoglBitmap *bmp, + CoglPixelFormat internal_format) { CoglTexture *tex = COGL_TEXTURE (tex_2ds); + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; tex->vtable = &cogl_texture_2d_sliced_vtable; @@ -914,36 +921,72 @@ _cogl_texture_2d_sliced_upload_from_data tex_2ds->min_filter = GL_FALSE; tex_2ds->mag_filter = GL_FALSE; - if (upload_data->bitmap.data) + if (bmp->data) { - if (!_cogl_texture_upload_data_prepare (upload_data, internal_format)) + CoglBitmap dst_bmp; + gboolean dst_bmp_owner; + + if (!_cogl_texture_prepare_for_upload (bmp, + internal_format, + &internal_format, + &dst_bmp, + &dst_bmp_owner, + &gl_intformat, + &gl_format, + &gl_type)) return FALSE; /* Create slices for the given format and size */ - if (!_cogl_texture_2d_sliced_slices_create (tex_2ds, upload_data)) - return FALSE; + if (!_cogl_texture_2d_sliced_slices_create (tex_2ds, + bmp->width, + bmp->height, + gl_intformat, + gl_format, + gl_type)) + { + if (dst_bmp_owner) + g_free (dst_bmp.data); - if (!_cogl_texture_2d_sliced_upload_to_gl (tex_2ds, upload_data)) - return FALSE; + return FALSE; + } + + if (!_cogl_texture_2d_sliced_upload_to_gl (tex_2ds, + bmp, + gl_intformat, + gl_format, + gl_type)) + { + if (dst_bmp_owner) + g_free (dst_bmp.data); + + return FALSE; + } + + if (dst_bmp_owner) + g_free (dst_bmp.data); } else { /* Find closest GL format match */ - upload_data->bitmap.format = - _cogl_pixel_format_to_gl (internal_format, - &upload_data->gl_intformat, - &upload_data->gl_format, - &upload_data->gl_type); + _cogl_pixel_format_to_gl (internal_format, + &gl_intformat, + &gl_format, + &gl_type); /* Create slices for the given format and size */ - if (!_cogl_texture_2d_sliced_slices_create (tex_2ds, upload_data)) + if (!_cogl_texture_2d_sliced_slices_create (tex_2ds, + bmp->width, + bmp->height, + gl_intformat, + gl_format, + gl_type)) return FALSE; } - tex_2ds->gl_format = upload_data->gl_intformat; - tex_2ds->width = upload_data->bitmap.width; - tex_2ds->height = upload_data->bitmap.height; - tex_2ds->format = upload_data->bitmap.format; + tex_2ds->gl_format = gl_intformat; + tex_2ds->width = bmp->width; + tex_2ds->height = bmp->height; + tex_2ds->format = bmp->format; return TRUE; } @@ -956,7 +999,7 @@ _cogl_texture_2d_sliced_new_with_size (unsigned int width, { CoglTexture2DSliced *tex_2ds; CoglTexture *tex; - CoglTextureUploadData upload_data; + CoglBitmap bmp; /* Since no data, we need some internal format */ if (internal_format == COGL_PIXEL_FORMAT_ANY) @@ -967,28 +1010,24 @@ _cogl_texture_2d_sliced_new_with_size (unsigned int width, tex = COGL_TEXTURE (tex_2ds); - upload_data.bitmap.width = width; - upload_data.bitmap.height = height; - upload_data.bitmap.data = NULL; - upload_data.bitmap_owner = FALSE; + bmp.width = width; + bmp.height = height; + bmp.data = NULL; if ((flags & COGL_TEXTURE_NO_SLICING)) tex_2ds->max_waste = -1; else tex_2ds->max_waste = COGL_TEXTURE_MAX_WASTE; - if (!_cogl_texture_2d_sliced_upload_from_data (tex_2ds, &upload_data, + if (!_cogl_texture_2d_sliced_upload_from_data (tex_2ds, &bmp, internal_format)) { _cogl_texture_2d_sliced_free (tex_2ds); - _cogl_texture_upload_data_free (&upload_data); return COGL_INVALID_HANDLE; } tex_2ds->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; - _cogl_texture_upload_data_free (&upload_data); - return _cogl_texture_2d_sliced_handle_new (tex_2ds); } @@ -1000,7 +1039,6 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, CoglTexture2DSliced *tex_2ds; CoglTexture *tex; CoglBitmap *bmp = (CoglBitmap *)bmp_handle; - CoglTextureUploadData upload_data; g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); @@ -1009,9 +1047,6 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, tex = COGL_TEXTURE (tex_2ds); - upload_data.bitmap = *bmp; - upload_data.bitmap_owner = FALSE; - if (flags & COGL_TEXTURE_NO_SLICING) tex_2ds->max_waste = -1; else @@ -1025,18 +1060,15 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, * CoglHandle is returned, it should also be destroyed * with cogl_handle_unref at some point! */ - if (!_cogl_texture_2d_sliced_upload_from_data (tex_2ds, &upload_data, + if (!_cogl_texture_2d_sliced_upload_from_data (tex_2ds, bmp, internal_format)) { _cogl_texture_2d_sliced_free (tex_2ds); - _cogl_texture_upload_data_free (&upload_data); return COGL_INVALID_HANDLE; } tex_2ds->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; - _cogl_texture_upload_data_free (&upload_data); - return _cogl_texture_2d_sliced_handle_new (tex_2ds); } @@ -1398,12 +1430,10 @@ _cogl_texture_2d_sliced_set_region (CoglTexture *tex, CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); gint bpp; CoglBitmap source_bmp; - CoglBitmap temp_bmp; - gboolean source_bmp_owner = FALSE; - CoglPixelFormat closest_format; + CoglBitmap tmp_bmp; + gboolean tmp_bmp_owner = FALSE; GLenum closest_gl_format; GLenum closest_gl_type; - gboolean success; /* Check for valid format */ if (format == COGL_PIXEL_FORMAT_ANY) @@ -1423,38 +1453,30 @@ _cogl_texture_2d_sliced_set_region (CoglTexture *tex, 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_2ds->format, - NULL, /* don't need */ - &closest_gl_format, - &closest_gl_type); + /* Prepare the bitmap so that it will do the premultiplication + conversion */ + _cogl_texture_prepare_for_upload (&source_bmp, + tex_2ds->format, + NULL, + &tmp_bmp, + &tmp_bmp_owner, + NULL, + &closest_gl_format, + &closest_gl_type); - /* If no direct match, convert */ - if (closest_format != format) - { - /* Convert to required format */ - success = _cogl_bitmap_convert_format_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, + &tmp_bmp, closest_gl_format, closest_gl_type); /* Free data if owner */ - if (source_bmp_owner) - g_free (source_bmp.data); + if (tmp_bmp_owner) + g_free (tmp_bmp.data); return TRUE; } diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c index 8c8a31c5a..8d32e310e 100644 --- a/cogl/cogl-texture-2d.c +++ b/cogl/cogl-texture-2d.c @@ -275,42 +275,43 @@ _cogl_texture_2d_new_from_bitmap (CoglHandle bmp_handle, CoglTextureFlags flags, CoglPixelFormat internal_format) { - CoglTexture2D *tex_2d; - CoglBitmap *bmp = (CoglBitmap *)bmp_handle; - CoglTextureUploadData upload_data; + CoglTexture2D *tex_2d; + CoglBitmap *bmp = (CoglBitmap *)bmp_handle; + CoglBitmap dst_bmp; + gboolean dst_bmp_owner; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); - upload_data.bitmap = *bmp; - upload_data.bitmap_owner = FALSE; + if (!_cogl_texture_prepare_for_upload (bmp, + internal_format, + &internal_format, + &dst_bmp, + &dst_bmp_owner, + &gl_intformat, + &gl_format, + &gl_type)) + return COGL_INVALID_HANDLE; - if (!_cogl_texture_upload_data_prepare_format (&upload_data, - &internal_format) || - !_cogl_texture_2d_can_create (upload_data.bitmap.width, - upload_data.bitmap.height, - internal_format) || - !_cogl_texture_upload_data_convert (&upload_data, internal_format)) - { - _cogl_texture_upload_data_free (&upload_data); - return COGL_INVALID_HANDLE; - } - - tex_2d = _cogl_texture_2d_create_base (upload_data.bitmap.width, - upload_data.bitmap.height, + tex_2d = _cogl_texture_2d_create_base (bmp->width, + bmp->height, flags, - upload_data.bitmap.format); + internal_format); GE( glGenTextures (1, &tex_2d->gl_texture) ); _cogl_texture_driver_upload_to_gl (GL_TEXTURE_2D, tex_2d->gl_texture, - &upload_data.bitmap, - upload_data.gl_intformat, - upload_data.gl_format, - upload_data.gl_type); + &dst_bmp, + gl_intformat, + gl_format, + gl_type); - tex_2d->gl_format = upload_data.gl_intformat; + tex_2d->gl_format = gl_intformat; - _cogl_texture_upload_data_free (&upload_data); + if (dst_bmp_owner) + g_free (dst_bmp.data); return _cogl_texture_2d_handle_new (tex_2d); } @@ -431,12 +432,10 @@ _cogl_texture_2d_set_region (CoglTexture *tex, CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); gint bpp; CoglBitmap source_bmp; - CoglBitmap temp_bmp; - gboolean source_bmp_owner = FALSE; - CoglPixelFormat closest_format; + CoglBitmap tmp_bmp; + gboolean tmp_bmp_owner = FALSE; GLenum closest_gl_format; GLenum closest_gl_type; - gboolean success; /* Check for valid format */ if (format == COGL_PIXEL_FORMAT_ANY) @@ -456,25 +455,16 @@ _cogl_texture_2d_set_region (CoglTexture *tex, 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_2d->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_format_and_premult (&source_bmp, - &temp_bmp, - closest_format); - - /* Swap bitmaps if succeeded */ - if (!success) return FALSE; - source_bmp = temp_bmp; - source_bmp_owner = TRUE; - } + /* Prepare the bitmap so that it will do the premultiplication + conversion */ + _cogl_texture_prepare_for_upload (&source_bmp, + tex_2d->format, + NULL, + &tmp_bmp, + &tmp_bmp_owner, + NULL, + &closest_gl_format, + &closest_gl_type); /* Send data to GL */ _cogl_texture_driver_upload_subregion_to_gl (GL_TEXTURE_2D, @@ -482,13 +472,13 @@ _cogl_texture_2d_set_region (CoglTexture *tex, src_x, src_y, dst_x, dst_y, dst_width, dst_height, - &source_bmp, + &tmp_bmp, closest_gl_format, closest_gl_type); /* Free data if owner */ - if (source_bmp_owner) - g_free (source_bmp.data); + if (tmp_bmp_owner) + g_free (tmp_bmp.data); return TRUE; } diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index e59374334..857e3f6a8 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -32,7 +32,6 @@ typedef struct _CoglTexture CoglTexture; typedef struct _CoglTextureVtable CoglTextureVtable; -typedef struct _CoglTextureUploadData CoglTextureUploadData; typedef void (*CoglTextureSliceCallback) (CoglHandle handle, GLuint gl_handle, @@ -107,17 +106,6 @@ struct _CoglTextureVtable gint (* get_height) (CoglTexture *tex); }; -/* This represents the state needed to upload texture data. There are - utility functions in cogl-texture which use this state */ -struct _CoglTextureUploadData -{ - CoglBitmap bitmap; - gboolean bitmap_owner; - GLenum gl_intformat; - GLenum gl_format; - GLenum gl_type; -}; - struct _CoglTexture { CoglHandleObject _parent; @@ -162,28 +150,20 @@ _cogl_texture_ensure_mipmaps (CoglHandle handle); void _cogl_texture_ensure_non_quad_rendering (CoglHandle handle); -/* Utility functions to help uploading a bitmap. These are intended to - * be used by CoglTexture implementations or drivers... */ - -void -_cogl_texture_upload_data_free (CoglTextureUploadData *data); - -void -_cogl_texture_upload_data_swap_bitmap (CoglTextureUploadData *data, - CoglBitmap *new_bitmap); +/* Utility function to help uploading a bitmap. If the bitmap needs + premult conversion then it will be copied and *copied_bitmap will + be set to TRUE. Otherwise dst_bmp will be set to a shallow copy of + src_bmp. The GLenums needed for uploading are returned */ gboolean -_cogl_texture_upload_data_prepare_format - (CoglTextureUploadData *data, - CoglPixelFormat *internal_format); - -gboolean -_cogl_texture_upload_data_convert (CoglTextureUploadData *data, - CoglPixelFormat internal_format); - -gboolean -_cogl_texture_upload_data_prepare (CoglTextureUploadData *data, - CoglPixelFormat internal_format); +_cogl_texture_prepare_for_upload (CoglBitmap *src_bmp, + CoglPixelFormat dst_format, + CoglPixelFormat *dst_format_out, + CoglBitmap *dst_bmp, + gboolean *copied_bitmap, + GLenum *out_glintformat, + GLenum *out_glformat, + GLenum *out_gltype); void _cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride); diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index fe349f725..7166e5406 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -100,25 +100,71 @@ cogl_texture_unref (CoglHandle handle) cogl_handle_unref (handle); } -void -_cogl_texture_upload_data_free (CoglTextureUploadData *data) +gboolean +_cogl_texture_prepare_for_upload (CoglBitmap *src_bmp, + CoglPixelFormat dst_format, + CoglPixelFormat *dst_format_out, + CoglBitmap *dst_bmp, + gboolean *copied_bitmap, + GLenum *out_glintformat, + GLenum *out_glformat, + GLenum *out_gltype) { - if (data->bitmap.data != NULL && data->bitmap_owner) - g_free (data->bitmap.data); + /* If the application hasn't specified a specific format then we'll + * pick the most appropriate. By default Cogl will use a + * premultiplied internal format. Later we will add control over + * this. */ + if (dst_format == COGL_PIXEL_FORMAT_ANY) + { + if ((src_bmp->format & COGL_A_BIT) && + src_bmp->format != COGL_PIXEL_FORMAT_A_8) + dst_format = src_bmp->format | COGL_PREMULT_BIT; + else + dst_format = src_bmp->format; + } - data->bitmap.data = NULL; - data->bitmap_owner = FALSE; -} + if (dst_bmp) + { + *copied_bitmap = FALSE; + *dst_bmp = *src_bmp; -void -_cogl_texture_upload_data_swap_bitmap (CoglTextureUploadData *data, - CoglBitmap *new_bitmap) -{ - if (data->bitmap.data != NULL && data->bitmap_owner) - g_free (data->bitmap.data); + /* If the source format does not have the same premult flag as the + dst format then we need to copy and convert it */ + if ((src_bmp->format & COGL_A_BIT) && + src_bmp->format != COGL_PIXEL_FORMAT_A_8 && + (src_bmp->format & COGL_PREMULT_BIT) != + (dst_format & COGL_PREMULT_BIT)) + { + dst_bmp->data = g_memdup (dst_bmp->data, + dst_bmp->height * dst_bmp->rowstride); + *copied_bitmap = TRUE; - data->bitmap = *new_bitmap; - data->bitmap_owner = TRUE; + if (!_cogl_bitmap_convert_premult_status (dst_bmp, + src_bmp->format ^ + COGL_PREMULT_BIT)) + { + g_free (dst_bmp->data); + return FALSE; + } + } + } + + /* Use the source format from the src bitmap type and the internal + format from the dst format type so that GL can do the + conversion */ + _cogl_pixel_format_to_gl (src_bmp->format, + NULL, /* internal format */ + out_glformat, + out_gltype); + _cogl_pixel_format_to_gl (dst_format, + out_glintformat, + NULL, + NULL); + + if (dst_format_out) + *dst_format_out = dst_format; + + return TRUE; } void @@ -157,64 +203,6 @@ _cogl_texture_set_wrap_mode_parameter (CoglHandle handle, tex->vtable->set_wrap_mode_parameter (tex, wrap_mode); } -gboolean -_cogl_texture_upload_data_prepare_format - (CoglTextureUploadData *data, - CoglPixelFormat *internal_format) -{ - /* Was there any internal conversion requested? - * By default Cogl will use a premultiplied internal format. Later we will - * add control over this. */ - if (*internal_format == COGL_PIXEL_FORMAT_ANY) - { - if ((data->bitmap.format & COGL_A_BIT) && - data->bitmap.format != COGL_PIXEL_FORMAT_A_8) - *internal_format = data->bitmap.format | COGL_PREMULT_BIT; - else - *internal_format = data->bitmap.format; - } - - /* Find closest format accepted by GL */ - *internal_format = _cogl_pixel_format_to_gl (*internal_format, - &data->gl_intformat, - &data->gl_format, - &data->gl_type); - - return TRUE; -} - -gboolean -_cogl_texture_upload_data_convert (CoglTextureUploadData *data, - CoglPixelFormat internal_format) -{ - CoglBitmap new_bitmap; - gboolean success; - - /* Convert to internal format */ - if (internal_format != data->bitmap.format) - { - success = _cogl_bitmap_convert_format_and_premult (&data->bitmap, - &new_bitmap, - internal_format); - - if (!success) - return FALSE; - - /* Update texture with new data */ - _cogl_texture_upload_data_swap_bitmap (data, &new_bitmap); - } - - return TRUE; -} - -gboolean -_cogl_texture_upload_data_prepare (CoglTextureUploadData *data, - CoglPixelFormat internal_format) -{ - return (_cogl_texture_upload_data_prepare_format (data, &internal_format) && - _cogl_texture_upload_data_convert (data, internal_format)); -} - /* This is like CoglSpanIter except it deals with floats and it effectively assumes there is only one span from 0.0 to 1.0 */ typedef struct _CoglTextureIter From e4455041c8439ce9f5b5e1f9ee9c7e0a4927a4ec Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 1 Feb 2010 13:25:19 +0000 Subject: [PATCH 28/33] cogl-material: Compare GL texture numbers for material layer textures When deciding if a material layer is equal it now compares the GL target and texture number if the textures are not sliced. This is needed to get batching across atlased textures. --- cogl/cogl-material.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/cogl/cogl-material.c b/cogl/cogl-material.c index 35e898f77..589a753db 100644 --- a/cogl/cogl-material.c +++ b/cogl/cogl-material.c @@ -1712,13 +1712,36 @@ _cogl_material_flush_gl_state (CoglHandle handle, 0, sizeof (CoglMaterialFlushOptions)); } +static gboolean +_cogl_material_texture_equal (CoglHandle texture0, CoglHandle texture1) +{ + GLenum gl_handle0, gl_handle1, gl_target0, gl_target1; + + /* If the texture handles are the same then the textures are + definitely equal */ + if (texture0 == texture1) + return TRUE; + + /* If neither texture is sliced then they could still be the same if + the are referring to the same GL texture */ + if (cogl_texture_is_sliced (texture0) || + cogl_texture_is_sliced (texture1)) + return FALSE; + + cogl_texture_get_gl_texture (texture0, &gl_handle0, &gl_target0); + cogl_texture_get_gl_texture (texture1, &gl_handle1, &gl_target1); + + return gl_handle0 == gl_handle1 && gl_target0 == gl_target1; +} + static gboolean _cogl_material_layer_equal (CoglMaterialLayer *material0_layer, CoglHandle material0_layer_texture, CoglMaterialLayer *material1_layer, CoglHandle material1_layer_texture) { - if (material0_layer_texture != material1_layer_texture) + if (!_cogl_material_texture_equal (material0_layer_texture, + material1_layer_texture)) return FALSE; if ((material0_layer->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE) != From ef9781d7dadb7431c5420764d9a33b9bddcf1a5a Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 3 Feb 2010 19:54:12 +0000 Subject: [PATCH 29/33] cogl-atlas: Make the cogl_atlas_* API internal This just adds an underscore to every entry point for the CoglAtlas API so that it's not exported. --- cogl/cogl-atlas-texture.c | 89 ++++++++++++------------ cogl/cogl-atlas.c | 140 +++++++++++++++++++------------------- cogl/cogl-atlas.h | 32 ++++----- cogl/cogl-context.c | 2 +- 4 files changed, 132 insertions(+), 131 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 0c018f056..4cc76b4d6 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -244,18 +244,18 @@ _cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle); + _cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle); COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i", atlas_tex->rectangle.width, atlas_tex->rectangle.height); COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", - cogl_atlas_get_width (ctx->atlas), - cogl_atlas_get_height (ctx->atlas), - cogl_atlas_get_n_rectangles (ctx->atlas), - cogl_atlas_get_remaining_space (ctx->atlas) * 100 / - (cogl_atlas_get_width (ctx->atlas) * - cogl_atlas_get_height (ctx->atlas))); + _cogl_atlas_get_width (ctx->atlas), + _cogl_atlas_get_height (ctx->atlas), + _cogl_atlas_get_n_rectangles (ctx->atlas), + _cogl_atlas_get_remaining_space (ctx->atlas) * 100 / + (_cogl_atlas_get_width (ctx->atlas) * + _cogl_atlas_get_height (ctx->atlas))); atlas_tex->in_atlas = FALSE; } @@ -730,16 +730,16 @@ _cogl_atlas_texture_create_atlas (guint atlas_width, the textures */ while (atlas_width < max_texture_size && atlas_height < max_texture_size) { - CoglAtlas *new_atlas = cogl_atlas_new (atlas_width, atlas_height, NULL); + CoglAtlas *new_atlas = _cogl_atlas_new (atlas_width, atlas_height, NULL); guint i; /* Add all of the textures and keep track of the new position */ for (i = 0; i < n_textures; i++) - if (!cogl_atlas_add_rectangle (new_atlas, - textures[i].texture->rectangle.width, - textures[i].texture->rectangle.height, - textures[i].texture, - &textures[i].new_position)) + if (!_cogl_atlas_add_rectangle (new_atlas, + textures[i].texture->rectangle.width, + textures[i].texture->rectangle.height, + textures[i].texture, + &textures[i].new_position)) break; /* If the atlas can contain all of the textures then we have a @@ -747,7 +747,7 @@ _cogl_atlas_texture_create_atlas (guint atlas_width, if (i >= n_textures) return new_atlas; - cogl_atlas_free (new_atlas); + _cogl_atlas_free (new_atlas); _cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height); } @@ -785,17 +785,17 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex, _COGL_GET_CONTEXT (ctx, FALSE); /* Check if we can fit the rectangle into the existing atlas */ - if (ctx->atlas && cogl_atlas_add_rectangle (ctx->atlas, width, height, + if (ctx->atlas && _cogl_atlas_add_rectangle (ctx->atlas, width, height, new_sub_tex, &new_sub_tex->rectangle)) { COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", - cogl_atlas_get_width (ctx->atlas), - cogl_atlas_get_height (ctx->atlas), - cogl_atlas_get_n_rectangles (ctx->atlas), - cogl_atlas_get_remaining_space (ctx->atlas) * 100 / - (cogl_atlas_get_width (ctx->atlas) * - cogl_atlas_get_height (ctx->atlas))); + _cogl_atlas_get_width (ctx->atlas), + _cogl_atlas_get_height (ctx->atlas), + _cogl_atlas_get_n_rectangles (ctx->atlas), + _cogl_atlas_get_remaining_space (ctx->atlas) * 100 / + (_cogl_atlas_get_width (ctx->atlas) * + _cogl_atlas_get_height (ctx->atlas))); return TRUE; } @@ -807,10 +807,11 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex, data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData)); else { - data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData) * - (cogl_atlas_get_n_rectangles (ctx->atlas) + 1)); - cogl_atlas_foreach (ctx->atlas, _cogl_atlas_texture_get_rectangles_cb, - &data); + data.textures = + g_malloc (sizeof (CoglAtlasTextureRepositionData) * + (_cogl_atlas_get_n_rectangles (ctx->atlas) + 1)); + _cogl_atlas_foreach (ctx->atlas, _cogl_atlas_texture_get_rectangles_cb, + &data); } /* Add the new rectangle as a dummy texture so that it can be @@ -827,13 +828,13 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex, /* Try to create a new atlas that can contain all of the textures */ if (ctx->atlas) { - atlas_width = cogl_atlas_get_width (ctx->atlas); - atlas_height = cogl_atlas_get_height (ctx->atlas); + atlas_width = _cogl_atlas_get_width (ctx->atlas); + atlas_height = _cogl_atlas_get_height (ctx->atlas); /* If there is enough space in the existing for the new rectangle in the existing atlas we'll start with the same size, otherwise we'll immediately double it */ - if (cogl_atlas_get_remaining_space (ctx->atlas) < width * height) + if (_cogl_atlas_get_remaining_space (ctx->atlas) < width * height) _cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height); } else @@ -856,21 +857,21 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex, { /* We need to migrate the existing textures into a new texture */ new_tex = - _cogl_texture_2d_new_with_size (cogl_atlas_get_width (new_atlas), - cogl_atlas_get_height (new_atlas), + _cogl_texture_2d_new_with_size (_cogl_atlas_get_width (new_atlas), + _cogl_atlas_get_height (new_atlas), COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_RGBA_8888); COGL_NOTE (ATLAS, "Atlas %s with size %ix%i", ctx->atlas == NULL || - cogl_atlas_get_width (ctx->atlas) != - cogl_atlas_get_width (new_atlas) || - cogl_atlas_get_height (ctx->atlas) != - cogl_atlas_get_height (new_atlas) ? + _cogl_atlas_get_width (ctx->atlas) != + _cogl_atlas_get_width (new_atlas) || + _cogl_atlas_get_height (ctx->atlas) != + _cogl_atlas_get_height (new_atlas) ? "resized" : "reorganized", - cogl_atlas_get_width (new_atlas), - cogl_atlas_get_height (new_atlas)); + _cogl_atlas_get_width (new_atlas), + _cogl_atlas_get_height (new_atlas)); if (ctx->atlas) { @@ -881,7 +882,7 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex, ctx->atlas_texture, new_tex, new_sub_tex); - cogl_atlas_free (ctx->atlas); + _cogl_atlas_free (ctx->atlas); cogl_handle_unref (ctx->atlas_texture); } else @@ -893,12 +894,12 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex, ctx->atlas_texture = new_tex; COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", - cogl_atlas_get_width (ctx->atlas), - cogl_atlas_get_height (ctx->atlas), - cogl_atlas_get_n_rectangles (ctx->atlas), - cogl_atlas_get_remaining_space (ctx->atlas) * 100 / - (cogl_atlas_get_width (ctx->atlas) * - cogl_atlas_get_height (ctx->atlas))); + _cogl_atlas_get_width (ctx->atlas), + _cogl_atlas_get_height (ctx->atlas), + _cogl_atlas_get_n_rectangles (ctx->atlas), + _cogl_atlas_get_remaining_space (ctx->atlas) * 100 / + (_cogl_atlas_get_width (ctx->atlas) * + _cogl_atlas_get_height (ctx->atlas))); ret = TRUE; } @@ -994,7 +995,7 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, &gl_format, &gl_type)) { - cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle); + _cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle); g_free (atlas_tex); return COGL_INVALID_HANDLE; } diff --git a/cogl/cogl-atlas.c b/cogl/cogl-atlas.c index 82217e25d..47c16598c 100644 --- a/cogl/cogl-atlas.c +++ b/cogl/cogl-atlas.c @@ -46,7 +46,7 @@ the atlas */ #include -static void cogl_atlas_dump_image (CoglAtlas *atlas); +static void _cogl_atlas_dump_image (CoglAtlas *atlas); #endif /* COGL_ENABLE_DEBUG */ @@ -107,23 +107,23 @@ struct _CoglAtlasStackEntry }; static CoglAtlasNode * -cogl_atlas_node_new (void) +_cogl_atlas_node_new (void) { return g_slice_new (CoglAtlasNode); } static void -cogl_atlas_node_free (CoglAtlasNode *node) +_cogl_atlas_node_free (CoglAtlasNode *node) { g_slice_free (CoglAtlasNode, node); } CoglAtlas * -cogl_atlas_new (guint width, guint height, - GDestroyNotify value_destroy_func) +_cogl_atlas_new (guint width, guint height, + GDestroyNotify value_destroy_func) { CoglAtlas *atlas = g_new (CoglAtlas, 1); - CoglAtlasNode *root = cogl_atlas_node_new (); + CoglAtlasNode *root = _cogl_atlas_node_new (); root->type = COGL_ATLAS_EMPTY_LEAF; root->parent = NULL; @@ -141,9 +141,9 @@ cogl_atlas_new (guint width, guint height, } static CoglAtlasStackEntry * -cogl_atlas_stack_push (CoglAtlasStackEntry *stack, - CoglAtlasNode *node, - gboolean next_index) +_cogl_atlas_stack_push (CoglAtlasStackEntry *stack, + CoglAtlasNode *node, + gboolean next_index) { CoglAtlasStackEntry *new_entry = g_slice_new (CoglAtlasStackEntry); @@ -155,7 +155,7 @@ cogl_atlas_stack_push (CoglAtlasStackEntry *stack, } static CoglAtlasStackEntry * -cogl_atlas_stack_pop (CoglAtlasStackEntry *stack) +_cogl_atlas_stack_pop (CoglAtlasStackEntry *stack) { CoglAtlasStackEntry *next = stack->next; @@ -165,8 +165,8 @@ cogl_atlas_stack_pop (CoglAtlasStackEntry *stack) } static CoglAtlasNode * -cogl_atlas_node_split_horizontally (CoglAtlasNode *node, - guint left_width) +_cogl_atlas_node_split_horizontally (CoglAtlasNode *node, + guint left_width) { /* Splits the node horizontally (according to emacs' definition, not vim) by converting it to a branch and adding two new leaf @@ -179,7 +179,7 @@ cogl_atlas_node_split_horizontally (CoglAtlasNode *node, if (node->rectangle.width == left_width) return node; - left_node = cogl_atlas_node_new (); + left_node = _cogl_atlas_node_new (); left_node->type = COGL_ATLAS_EMPTY_LEAF; left_node->parent = node; left_node->rectangle.x = node->rectangle.x; @@ -188,7 +188,7 @@ cogl_atlas_node_split_horizontally (CoglAtlasNode *node, left_node->rectangle.height = node->rectangle.height; node->d.branch.left = left_node; - right_node = cogl_atlas_node_new (); + right_node = _cogl_atlas_node_new (); right_node->type = COGL_ATLAS_EMPTY_LEAF; right_node->parent = node; right_node->rectangle.x = node->rectangle.x + left_width; @@ -203,8 +203,8 @@ cogl_atlas_node_split_horizontally (CoglAtlasNode *node, } static CoglAtlasNode * -cogl_atlas_node_split_vertically (CoglAtlasNode *node, - guint top_height) +_cogl_atlas_node_split_vertically (CoglAtlasNode *node, + guint top_height) { /* Splits the node vertically (according to emacs' definition, not vim) by converting it to a branch and adding two new leaf @@ -217,7 +217,7 @@ cogl_atlas_node_split_vertically (CoglAtlasNode *node, if (node->rectangle.height == top_height) return node; - top_node = cogl_atlas_node_new (); + top_node = _cogl_atlas_node_new (); top_node->type = COGL_ATLAS_EMPTY_LEAF; top_node->parent = node; top_node->rectangle.x = node->rectangle.x; @@ -226,7 +226,7 @@ cogl_atlas_node_split_vertically (CoglAtlasNode *node, top_node->rectangle.height = top_height; node->d.branch.left = top_node; - bottom_node = cogl_atlas_node_new (); + bottom_node = _cogl_atlas_node_new (); bottom_node->type = COGL_ATLAS_EMPTY_LEAF; bottom_node->parent = node; bottom_node->rectangle.x = node->rectangle.x; @@ -241,10 +241,10 @@ cogl_atlas_node_split_vertically (CoglAtlasNode *node, } gboolean -cogl_atlas_add_rectangle (CoglAtlas *atlas, - guint width, guint height, - gpointer data, - CoglAtlasRectangle *rectangle) +_cogl_atlas_add_rectangle (CoglAtlas *atlas, + guint width, guint height, + gpointer data, + CoglAtlasRectangle *rectangle) { /* Stack of nodes to search in */ CoglAtlasStackEntry *node_stack; @@ -255,7 +255,7 @@ cogl_atlas_add_rectangle (CoglAtlas *atlas, g_return_val_if_fail (width > 0 && height > 0, FALSE); /* Start with the root node */ - node_stack = cogl_atlas_stack_push (NULL, atlas->root, FALSE); + node_stack = _cogl_atlas_stack_push (NULL, atlas->root, FALSE); /* Depth-first search for an empty node that is big enough */ while (node_stack) @@ -263,7 +263,7 @@ cogl_atlas_add_rectangle (CoglAtlas *atlas, /* Pop an entry off the stack */ CoglAtlasNode *node = node_stack->node; int next_index = node_stack->next_index; - node_stack = cogl_atlas_stack_pop (node_stack); + node_stack = _cogl_atlas_stack_pop (node_stack); /* Regardless of the type of the node, there's no point descending any further if the new rectangle won't fit within @@ -281,20 +281,20 @@ cogl_atlas_add_rectangle (CoglAtlas *atlas, { if (next_index) /* Try the right branch */ - node_stack = cogl_atlas_stack_push (node_stack, - node->d.branch.right, - 0); + node_stack = _cogl_atlas_stack_push (node_stack, + node->d.branch.right, + 0); else { /* Make sure we remember to try the right branch once we've finished descending the left branch */ - node_stack = cogl_atlas_stack_push (node_stack, - node, - 1); + node_stack = _cogl_atlas_stack_push (node_stack, + node, + 1); /* Try the left branch */ - node_stack = cogl_atlas_stack_push (node_stack, - node->d.branch.left, - 0); + node_stack = _cogl_atlas_stack_push (node_stack, + node->d.branch.left, + 0); } } } @@ -302,7 +302,7 @@ cogl_atlas_add_rectangle (CoglAtlas *atlas, /* Free the stack */ while (node_stack) - node_stack = cogl_atlas_stack_pop (node_stack); + node_stack = _cogl_atlas_stack_pop (node_stack); if (found_node) { @@ -311,13 +311,13 @@ cogl_atlas_add_rectangle (CoglAtlas *atlas, if (found_node->rectangle.width - width > found_node->rectangle.height - height) { - found_node = cogl_atlas_node_split_horizontally (found_node, width); - found_node = cogl_atlas_node_split_vertically (found_node, height); + found_node = _cogl_atlas_node_split_horizontally (found_node, width); + found_node = _cogl_atlas_node_split_vertically (found_node, height); } else { - found_node = cogl_atlas_node_split_vertically (found_node, height); - found_node = cogl_atlas_node_split_horizontally (found_node, width); + found_node = _cogl_atlas_node_split_vertically (found_node, height); + found_node = _cogl_atlas_node_split_horizontally (found_node, width); } found_node->type = COGL_ATLAS_FILLED_LEAF; @@ -333,7 +333,7 @@ cogl_atlas_add_rectangle (CoglAtlas *atlas, #ifdef COGL_ENABLE_DEBUG if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE)) - cogl_atlas_dump_image (atlas); + _cogl_atlas_dump_image (atlas); #endif return TRUE; @@ -343,8 +343,8 @@ cogl_atlas_add_rectangle (CoglAtlas *atlas, } void -cogl_atlas_remove_rectangle (CoglAtlas *atlas, - const CoglAtlasRectangle *rectangle) +_cogl_atlas_remove_rectangle (CoglAtlas *atlas, + const CoglAtlasRectangle *rectangle) { CoglAtlasNode *node = atlas->root; @@ -391,8 +391,8 @@ cogl_atlas_remove_rectangle (CoglAtlas *atlas, if (node->d.branch.left->type == COGL_ATLAS_EMPTY_LEAF && node->d.branch.right->type == COGL_ATLAS_EMPTY_LEAF) { - cogl_atlas_node_free (node->d.branch.left); - cogl_atlas_node_free (node->d.branch.right); + _cogl_atlas_node_free (node->d.branch.left); + _cogl_atlas_node_free (node->d.branch.right); node->type = COGL_ATLAS_EMPTY_LEAF; } else @@ -407,44 +407,44 @@ cogl_atlas_remove_rectangle (CoglAtlas *atlas, #ifdef COGL_ENABLE_DEBUG if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE)) - cogl_atlas_dump_image (atlas); + _cogl_atlas_dump_image (atlas); #endif } guint -cogl_atlas_get_width (CoglAtlas *atlas) +_cogl_atlas_get_width (CoglAtlas *atlas) { return atlas->root->rectangle.width; } guint -cogl_atlas_get_height (CoglAtlas *atlas) +_cogl_atlas_get_height (CoglAtlas *atlas) { return atlas->root->rectangle.height; } guint -cogl_atlas_get_remaining_space (CoglAtlas *atlas) +_cogl_atlas_get_remaining_space (CoglAtlas *atlas) { return atlas->space_remaining; } guint -cogl_atlas_get_n_rectangles (CoglAtlas *atlas) +_cogl_atlas_get_n_rectangles (CoglAtlas *atlas) { return atlas->n_rectangles; } static void -cogl_atlas_internal_foreach (CoglAtlas *atlas, - CoglAtlasInternalForeachCb callback, - gpointer data) +_cogl_atlas_internal_foreach (CoglAtlas *atlas, + CoglAtlasInternalForeachCb callback, + gpointer data) { /* Stack of nodes to search in */ CoglAtlasStackEntry *node_stack; /* Start with the root node */ - node_stack = cogl_atlas_stack_push (NULL, atlas->root, 0); + node_stack = _cogl_atlas_stack_push (NULL, atlas->root, 0); /* Iterate all nodes depth-first */ while (node_stack) @@ -460,7 +460,7 @@ cogl_atlas_internal_foreach (CoglAtlas *atlas, node_stack->next_index = 1; /* Explore the left branch next */ - node_stack = cogl_atlas_stack_push (node_stack, + node_stack = _cogl_atlas_stack_push (node_stack, node->d.branch.left, 0); } @@ -470,7 +470,7 @@ cogl_atlas_internal_foreach (CoglAtlas *atlas, node_stack->next_index = 2; /* Explore the right branch next */ - node_stack = cogl_atlas_stack_push (node_stack, + node_stack = _cogl_atlas_stack_push (node_stack, node->d.branch.right, 0); } @@ -478,14 +478,14 @@ cogl_atlas_internal_foreach (CoglAtlas *atlas, { /* We're finished with this node so we can call the callback */ callback (node, data); - node_stack = cogl_atlas_stack_pop (node_stack); + node_stack = _cogl_atlas_stack_pop (node_stack); } break; default: /* Some sort of leaf node, just call the callback */ callback (node, data); - node_stack = cogl_atlas_stack_pop (node_stack); + node_stack = _cogl_atlas_stack_pop (node_stack); break; } } @@ -501,7 +501,7 @@ typedef struct _CoglAtlasForeachClosure } CoglAtlasForeachClosure; static void -cogl_atlas_foreach_cb (CoglAtlasNode *node, gpointer data) +_cogl_atlas_foreach_cb (CoglAtlasNode *node, gpointer data) { CoglAtlasForeachClosure *closure = data; @@ -510,40 +510,40 @@ cogl_atlas_foreach_cb (CoglAtlasNode *node, gpointer data) } void -cogl_atlas_foreach (CoglAtlas *atlas, - CoglAtlasCallback callback, - gpointer data) +_cogl_atlas_foreach (CoglAtlas *atlas, + CoglAtlasCallback callback, + gpointer data) { CoglAtlasForeachClosure closure; closure.callback = callback; closure.data = data; - cogl_atlas_internal_foreach (atlas, cogl_atlas_foreach_cb, &closure); + _cogl_atlas_internal_foreach (atlas, _cogl_atlas_foreach_cb, &closure); } static void -cogl_atlas_free_cb (CoglAtlasNode *node, gpointer data) +_cogl_atlas_free_cb (CoglAtlasNode *node, gpointer data) { CoglAtlas *atlas = data; if (node->type == COGL_ATLAS_FILLED_LEAF && atlas->value_destroy_func) atlas->value_destroy_func (node->d.data); - cogl_atlas_node_free (node); + _cogl_atlas_node_free (node); } void -cogl_atlas_free (CoglAtlas *atlas) +_cogl_atlas_free (CoglAtlas *atlas) { - cogl_atlas_internal_foreach (atlas, cogl_atlas_free_cb, atlas); + _cogl_atlas_internal_foreach (atlas, _cogl_atlas_free_cb, atlas); g_free (atlas); } #ifdef COGL_ENABLE_DEBUG static void -cogl_atlas_dump_image_cb (CoglAtlasNode *node, gpointer data) +_cogl_atlas_dump_image_cb (CoglAtlasNode *node, gpointer data) { cairo_t *cr = data; @@ -572,7 +572,7 @@ cogl_atlas_dump_image_cb (CoglAtlasNode *node, gpointer data) } static void -cogl_atlas_dump_image (CoglAtlas *atlas) +_cogl_atlas_dump_image (CoglAtlas *atlas) { /* This dumps a png to help visualize the atlas. Each leaf rectangle is drawn with a white outline. Unused leaves are filled in black @@ -580,11 +580,11 @@ cogl_atlas_dump_image (CoglAtlas *atlas) cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, - cogl_atlas_get_width (atlas), - cogl_atlas_get_height (atlas)); + _cogl_atlas_get_width (atlas), + _cogl_atlas_get_height (atlas)); cairo_t *cr = cairo_create (surface); - cogl_atlas_internal_foreach (atlas, cogl_atlas_dump_image_cb, cr); + _cogl_atlas_internal_foreach (atlas, _cogl_atlas_dump_image_cb, cr); cairo_destroy (cr); diff --git a/cogl/cogl-atlas.h b/cogl/cogl-atlas.h index 37ccc898c..00d4a086d 100644 --- a/cogl/cogl-atlas.h +++ b/cogl/cogl-atlas.h @@ -40,37 +40,37 @@ struct _CoglAtlasRectangle }; CoglAtlas * -cogl_atlas_new (guint width, guint height, - GDestroyNotify value_destroy_func); +_cogl_atlas_new (guint width, guint height, + GDestroyNotify value_destroy_func); gboolean -cogl_atlas_add_rectangle (CoglAtlas *atlas, - guint width, guint height, - gpointer data, - CoglAtlasRectangle *rectangle); +_cogl_atlas_add_rectangle (CoglAtlas *atlas, + guint width, guint height, + gpointer data, + CoglAtlasRectangle *rectangle); void -cogl_atlas_remove_rectangle (CoglAtlas *atlas, - const CoglAtlasRectangle *rectangle); +_cogl_atlas_remove_rectangle (CoglAtlas *atlas, + const CoglAtlasRectangle *rectangle); guint -cogl_atlas_get_width (CoglAtlas *atlas); +_cogl_atlas_get_width (CoglAtlas *atlas); guint -cogl_atlas_get_height (CoglAtlas *atlas); +_cogl_atlas_get_height (CoglAtlas *atlas); guint -cogl_atlas_get_remaining_space (CoglAtlas *atlas); +_cogl_atlas_get_remaining_space (CoglAtlas *atlas); guint -cogl_atlas_get_n_rectangles (CoglAtlas *atlas); +_cogl_atlas_get_n_rectangles (CoglAtlas *atlas); void -cogl_atlas_foreach (CoglAtlas *atlas, - CoglAtlasCallback callback, - gpointer data); +_cogl_atlas_foreach (CoglAtlas *atlas, + CoglAtlasCallback callback, + gpointer data); void -cogl_atlas_free (CoglAtlas *atlas); +_cogl_atlas_free (CoglAtlas *atlas); #endif /* __COGL_ATLAS_H */ diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index 850789b80..3fc854288 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -192,7 +192,7 @@ _cogl_destroy_context () cogl_handle_unref (_context->default_material); if (_context->atlas) - cogl_atlas_free (_context->atlas); + _cogl_atlas_free (_context->atlas); if (_context->atlas_texture) cogl_handle_unref (_context->atlas_texture); From e83e0c3e5b78e6e220f27c3648d84a0f92eb2d23 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 3 Feb 2010 22:54:44 +0000 Subject: [PATCH 30/33] cogl-texture: Split out _cogl_texture_prepare_for_upload The Cogl atlas code was using _cogl_texture_prepare_for_upload with a NULL pointer for the dst_bmp to determine the internal format of the texture without converting the bitmap. It needs to do this to decide whether the texture will go in the atlas before wasting time on the conversion. This use of the function is a little confusing so that part of it has been split out into a new function called _cogl_texture_determine_internal_format. The code to decide whether a premult conversion is needed has also been split out. --- cogl/cogl-atlas-texture.c | 11 +---- cogl/cogl-texture-private.h | 7 ++++ cogl/cogl-texture.c | 81 ++++++++++++++++++++++--------------- 3 files changed, 57 insertions(+), 42 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 4cc76b4d6..f84594762 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -949,15 +949,8 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, COGL_NOTE (ATLAS, "Adding texture of size %ix%i", bmp->width, bmp->height); - if (!_cogl_texture_prepare_for_upload (bmp, - internal_format, - &internal_format, - NULL, - NULL, - &gl_intformat, - &gl_format, - &gl_type)) - return COGL_INVALID_HANDLE; + internal_format = _cogl_texture_determine_internal_format (bmp->format, + internal_format); /* If the texture is in a strange format then we can't use it */ if (internal_format != COGL_PIXEL_FORMAT_RGB_888 && diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index 857e3f6a8..920618529 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -150,6 +150,13 @@ _cogl_texture_ensure_mipmaps (CoglHandle handle); void _cogl_texture_ensure_non_quad_rendering (CoglHandle handle); +/* Utility function to determine which pixel format to use when + dst_format is COGL_PIXEL_FORMAT_ANY. If dst_format is not ANY then + it will just be returned directly */ +CoglPixelFormat +_cogl_texture_determine_internal_format (CoglPixelFormat src_format, + CoglPixelFormat dst_format); + /* Utility function to help uploading a bitmap. If the bitmap needs premult conversion then it will be copied and *copied_bitmap will be set to TRUE. Otherwise dst_bmp will be set to a shallow copy of diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 7166e5406..da55fd004 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -100,6 +100,36 @@ cogl_texture_unref (CoglHandle handle) cogl_handle_unref (handle); } +static gboolean +_cogl_texture_needs_premult_conversion (CoglPixelFormat src_format, + CoglPixelFormat dst_format) +{ + return ((src_format & COGL_A_BIT) && + src_format != COGL_PIXEL_FORMAT_A_8 && + (src_format & COGL_PREMULT_BIT) != + (dst_format & COGL_PREMULT_BIT)); +} + +CoglPixelFormat +_cogl_texture_determine_internal_format (CoglPixelFormat src_format, + CoglPixelFormat dst_format) +{ + /* If the application hasn't specified a specific format then we'll + * pick the most appropriate. By default Cogl will use a + * premultiplied internal format. Later we will add control over + * this. */ + if (dst_format == COGL_PIXEL_FORMAT_ANY) + { + if ((src_format & COGL_A_BIT) && + src_format != COGL_PIXEL_FORMAT_A_8) + return src_format | COGL_PREMULT_BIT; + else + return src_format; + } + else + return dst_format; +} + gboolean _cogl_texture_prepare_for_upload (CoglBitmap *src_bmp, CoglPixelFormat dst_format, @@ -110,42 +140,27 @@ _cogl_texture_prepare_for_upload (CoglBitmap *src_bmp, GLenum *out_glformat, GLenum *out_gltype) { - /* If the application hasn't specified a specific format then we'll - * pick the most appropriate. By default Cogl will use a - * premultiplied internal format. Later we will add control over - * this. */ - if (dst_format == COGL_PIXEL_FORMAT_ANY) - { - if ((src_bmp->format & COGL_A_BIT) && - src_bmp->format != COGL_PIXEL_FORMAT_A_8) - dst_format = src_bmp->format | COGL_PREMULT_BIT; - else - dst_format = src_bmp->format; - } + dst_format = _cogl_texture_determine_internal_format (src_bmp->format, + dst_format); - if (dst_bmp) - { - *copied_bitmap = FALSE; - *dst_bmp = *src_bmp; + *copied_bitmap = FALSE; + *dst_bmp = *src_bmp; - /* If the source format does not have the same premult flag as the - dst format then we need to copy and convert it */ - if ((src_bmp->format & COGL_A_BIT) && - src_bmp->format != COGL_PIXEL_FORMAT_A_8 && - (src_bmp->format & COGL_PREMULT_BIT) != - (dst_format & COGL_PREMULT_BIT)) + /* If the source format does not have the same premult flag as the + dst format then we need to copy and convert it */ + if (_cogl_texture_needs_premult_conversion (src_bmp->format, + dst_format)) + { + dst_bmp->data = g_memdup (dst_bmp->data, + dst_bmp->height * dst_bmp->rowstride); + *copied_bitmap = TRUE; + + if (!_cogl_bitmap_convert_premult_status (dst_bmp, + src_bmp->format ^ + COGL_PREMULT_BIT)) { - dst_bmp->data = g_memdup (dst_bmp->data, - dst_bmp->height * dst_bmp->rowstride); - *copied_bitmap = TRUE; - - if (!_cogl_bitmap_convert_premult_status (dst_bmp, - src_bmp->format ^ - COGL_PREMULT_BIT)) - { - g_free (dst_bmp->data); - return FALSE; - } + g_free (dst_bmp->data); + return FALSE; } } From 264f0f0f05746e8e92100022fdd09934882fad77 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 3 Feb 2010 23:08:30 +0000 Subject: [PATCH 31/33] cogl-texture: Avoid copying the bitmap when premultiplying from a file In cogl_texture_new_from_file we create and own a temporary bitmap. There's no need to copy this data if we need to do a premult conversion so instead it just does conversion before passing it on to cogl_texture_new_from_bitmap. --- cogl/cogl-texture.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index da55fd004..db9a0650d 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -396,16 +396,29 @@ cogl_texture_new_from_file (const gchar *filename, CoglPixelFormat internal_format, GError **error) { - CoglHandle bmp; - CoglHandle handle; + CoglHandle bmp_handle; + CoglBitmap *bmp; + CoglHandle handle = COGL_INVALID_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) + bmp_handle = cogl_bitmap_new_from_file (filename, error); + if (bmp_handle == COGL_INVALID_HANDLE) return COGL_INVALID_HANDLE; - handle = cogl_texture_new_from_bitmap (bmp, flags, internal_format); + bmp = (CoglBitmap *) bmp_handle; + + /* We know that the bitmap data is solely owned by this function so + we can do the premult conversion in place. This avoids having to + copy the bitmap which will otherwise happen in + _cogl_texture_prepare_for_upload */ + internal_format = _cogl_texture_determine_internal_format (bmp->format, + internal_format); + if (!_cogl_texture_needs_premult_conversion (bmp->format, internal_format) || + _cogl_bitmap_convert_premult_status (bmp, + bmp->format ^= COGL_PREMULT_BIT)) + handle = cogl_texture_new_from_bitmap (bmp, flags, internal_format); + cogl_handle_unref (bmp); return handle; From 45215cb42640dc094f002dd7d84bcc2c9af22cea Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 5 Feb 2010 17:03:04 +0000 Subject: [PATCH 32/33] cogl-atlas-texture: Fix a cut and paste error when getting the height There was a typo in getting the height of the full texture to check whether the sub region fits so that it was using the width instead. This was causing crashes when debugging is enabled for some apps. --- cogl/cogl-sub-texture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogl/cogl-sub-texture.c b/cogl/cogl-sub-texture.c index 164a23f47..53b4d58cc 100644 --- a/cogl/cogl-sub-texture.c +++ b/cogl/cogl-sub-texture.c @@ -250,7 +250,7 @@ _cogl_sub_texture_new (CoglHandle full_texture, guint full_width, full_height; full_width = cogl_texture_get_width (full_texture); - full_height = cogl_texture_get_width (full_texture); + full_height = cogl_texture_get_height (full_texture); /* The region must specify a non-zero subset of the full texture */ g_return_val_if_fail (sub_x >= 0 && sub_y >= 0, COGL_INVALID_HANDLE); From b9a9bf60b7b6b0d80f0b97a43527d7d6fd83b9f8 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Sat, 6 Feb 2010 00:12:10 +0000 Subject: [PATCH 33/33] cogl-bitmap: Update the format after (un)premultiplying The pixel format of the bitmap needs to have its premult flag cleared or set after the premult conversion otherwise it may get converted again. --- cogl/cogl-bitmap-fallback.c | 4 ++++ cogl/cogl-texture.c | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cogl/cogl-bitmap-fallback.c b/cogl/cogl-bitmap-fallback.c index 097cc5292..a02d85611 100644 --- a/cogl/cogl-bitmap-fallback.c +++ b/cogl/cogl-bitmap-fallback.c @@ -379,6 +379,8 @@ _cogl_bitmap_fallback_unpremult (CoglBitmap *bmp) } } + bmp->format &= ~COGL_PREMULT_BIT; + return TRUE; } @@ -414,6 +416,8 @@ _cogl_bitmap_fallback_premult (CoglBitmap *bmp) } } + bmp->format |= COGL_PREMULT_BIT; + return TRUE; } diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index db9a0650d..ec69aca6f 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -415,8 +415,7 @@ cogl_texture_new_from_file (const gchar *filename, internal_format = _cogl_texture_determine_internal_format (bmp->format, internal_format); if (!_cogl_texture_needs_premult_conversion (bmp->format, internal_format) || - _cogl_bitmap_convert_premult_status (bmp, - bmp->format ^= COGL_PREMULT_BIT)) + _cogl_bitmap_convert_premult_status (bmp, bmp->format ^ COGL_PREMULT_BIT)) handle = cogl_texture_new_from_bitmap (bmp, flags, internal_format); cogl_handle_unref (bmp);