4
* An object oriented GL/GLES Abstraction/Utility Layer
6
* Copyright (C) 2007,2008,2009 Intel Corporation.
8
* This library is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Lesser General Public
10
* License as published by the Free Software Foundation; either
11
* version 2 of the License, or (at your option) any later version.
13
* This library is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* Lesser General Public License for more details.
18
* You should have received a copy of the GNU Lesser General Public
19
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
24
* Matthew Allum <mallum@openedhand.com>
25
* Neil Roberts <neil@linux.intel.com>
26
* Robert Bragg <robert@linux.intel.com>
34
#include "cogl-internal.h"
35
#include "cogl-util.h"
36
#include "cogl-bitmap.h"
37
#include "cogl-bitmap-private.h"
38
#include "cogl-texture-private.h"
39
#include "cogl-texture-2d-sliced-private.h"
40
#include "cogl-texture-driver.h"
41
#include "cogl-context.h"
42
#include "cogl-handle.h"
43
#include "cogl-spans.h"
44
#include "cogl-journal-private.h"
50
static void _cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds);
52
COGL_HANDLE_DEFINE (Texture2DSliced, texture_2d_sliced);
54
static const CoglTextureVtable cogl_texture_2d_sliced_vtable;
56
/* To differentiate between texture coordinates of a specific, real, slice
57
* texture and the texture coordinates of the composite, sliced texture, the
58
* coordinates of the sliced texture are called "virtual" coordinates and the
59
* coordinates of slices are called "slice" coordinates. */
60
/* This function lets you iterate all the slices that lie within the given
61
* virtual coordinates of the parent sliced texture. */
62
/* Note: no guarantee is given about the order in which the slices will be
65
_cogl_texture_2d_sliced_foreach_sub_texture_in_region (
71
CoglTextureSliceCallback callback,
74
CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
75
float width = tex_2ds->width;
76
float height = tex_2ds->height;
80
/* Slice spans are stored in denormalized coordinates, and this is what
81
* the _cogl_span_iter_* funcs expect to be given, so we scale the given
82
* virtual coordinates by the texture size to denormalize.
84
/* XXX: I wonder if it's worth changing how we store spans so we can avoid
85
* the need to denormalize here */
86
virtual_tx_1 *= width;
87
virtual_ty_1 *= height;
88
virtual_tx_2 *= width;
89
virtual_ty_2 *= height;
91
/* Iterate the y axis of the virtual rectangle */
92
for (_cogl_span_iter_begin (&iter_y,
93
tex_2ds->slice_y_spans,
97
!_cogl_span_iter_end (&iter_y);
98
_cogl_span_iter_next (&iter_y))
100
float y_intersect_start = iter_y.intersect_start;
101
float y_intersect_end = iter_y.intersect_end;
105
/* Discard slices out of rectangle early */
106
if (!iter_y.intersects)
111
y_intersect_start = iter_y.intersect_end;
112
y_intersect_end = iter_y.intersect_start;
115
/* Localize slice texture coordinates */
116
slice_ty1 = y_intersect_start - iter_y.pos;
117
slice_ty2 = y_intersect_end - iter_y.pos;
119
if (tex_2ds->gl_target == GL_TEXTURE_2D)
121
/* Normalize slice texture coordinates */
122
slice_ty1 /= iter_y.span->size;
123
slice_ty2 /= iter_y.span->size;
126
/* Iterate the x axis of the virtual rectangle */
127
for (_cogl_span_iter_begin (&iter_x,
128
tex_2ds->slice_x_spans,
132
!_cogl_span_iter_end (&iter_x);
133
_cogl_span_iter_next (&iter_x))
135
float slice_coords[4];
136
float virtual_coords[4];
137
float x_intersect_start = iter_x.intersect_start;
138
float x_intersect_end = iter_x.intersect_end;
143
/* Discard slices out of rectangle early */
144
if (!iter_x.intersects)
149
x_intersect_start = iter_x.intersect_end;
150
x_intersect_end = iter_x.intersect_start;
153
/* Localize slice texture coordinates */
154
slice_tx1 = x_intersect_start - iter_x.pos;
155
slice_tx2 = x_intersect_end - iter_x.pos;
157
/* Pluck out opengl texture object for this slice */
158
gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint,
159
iter_y.index * iter_x.array->len +
162
if (tex_2ds->gl_target == GL_TEXTURE_2D)
164
/* Normalize slice texture coordinates */
165
slice_tx1 /= iter_x.span->size;
166
slice_tx2 /= iter_x.span->size;
169
slice_coords[0] = slice_tx1;
170
slice_coords[1] = slice_ty1;
171
slice_coords[2] = slice_tx2;
172
slice_coords[3] = slice_ty2;
174
virtual_coords[0] = x_intersect_start / width;
175
virtual_coords[1] = y_intersect_start / height;
176
virtual_coords[2] = x_intersect_end / width;
177
virtual_coords[3] = y_intersect_end / height;
190
_cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds,
191
CoglPixelFormat format)
193
CoglSpan *last_x_span;
194
CoglSpan *last_y_span;
195
guint8 *waste_buf = NULL;
197
/* If the texture has any waste then allocate a buffer big enough to
199
last_x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan,
200
tex_2ds->slice_x_spans->len - 1);
201
last_y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan,
202
tex_2ds->slice_y_spans->len - 1);
203
if (last_x_span->waste > 0 || last_y_span->waste > 0)
205
int bpp = _cogl_get_format_bpp (format);
206
CoglSpan *first_x_span
207
= &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0);
208
CoglSpan *first_y_span
209
= &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0);
210
unsigned int right_size = first_y_span->size * last_x_span->waste;
211
unsigned int bottom_size = first_x_span->size * last_y_span->waste;
213
waste_buf = g_malloc (MAX (right_size, bottom_size) * bpp);
220
_cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds,
233
bpp = _cogl_get_format_bpp (bmp->format);
235
waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds,
238
/* Iterate vertical slices */
239
for (y = 0; y < tex_2ds->slice_y_spans->len; ++y)
241
y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, y);
243
/* Iterate horizontal slices */
244
for (x = 0; x < tex_2ds->slice_x_spans->len; ++x)
246
int slice_num = y * tex_2ds->slice_x_spans->len + x;
248
x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, x);
250
/* Pick the gl texture object handle */
251
gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, slice_num);
253
_cogl_texture_driver_upload_subregion_to_gl (
256
x_span->start, /* src x */
257
y_span->start, /* src y */
260
x_span->size - x_span->waste, /* width */
261
y_span->size - y_span->waste, /* height */
266
/* Keep a copy of the first pixel if needed */
267
if (tex_2ds->first_pixels)
269
memcpy (tex_2ds->first_pixels[slice_num].data,
270
bmp->data + x_span->start * bpp
271
+ y_span->start * bmp->rowstride,
273
tex_2ds->first_pixels[slice_num].gl_format = gl_format;
274
tex_2ds->first_pixels[slice_num].gl_type = gl_type;
277
/* Fill the waste with a copies of the rightmost pixels */
278
if (x_span->waste > 0)
280
const guint8 *src = bmp->data
281
+ y_span->start * bmp->rowstride
282
+ (x_span->start + x_span->size - x_span->waste - 1) * bpp;
283
guint8 *dst = waste_buf;
286
for (wy = 0; wy < y_span->size - y_span->waste; wy++)
288
for (wx = 0; wx < x_span->waste; wx++)
290
memcpy (dst, src, bpp);
293
src += bmp->rowstride;
296
_cogl_texture_driver_prep_gl_for_pixels_upload (
300
GE( glTexSubImage2D (tex_2ds->gl_target, 0,
301
x_span->size - x_span->waste,
304
y_span->size - y_span->waste,
309
if (y_span->waste > 0)
311
const guint8 *src = bmp->data
312
+ ((y_span->start + y_span->size - y_span->waste - 1)
314
+ x_span->start * bpp;
315
guint8 *dst = waste_buf;
318
for (wy = 0; wy < y_span->waste; wy++)
320
memcpy (dst, src, (x_span->size - x_span->waste) * bpp);
321
dst += (x_span->size - x_span->waste) * bpp;
323
for (wx = 0; wx < x_span->waste; wx++)
325
memcpy (dst, dst - bpp, bpp);
330
_cogl_texture_driver_prep_gl_for_pixels_upload (
334
GE( glTexSubImage2D (tex_2ds->gl_target, 0,
336
y_span->size - y_span->waste,
348
tex_2ds->mipmaps_dirty = TRUE;
354
_cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds,
361
CoglBitmap *source_bmp,
362
GLuint source_gl_format,
363
GLuint source_gl_type)
371
int source_x = 0, source_y = 0;
372
int inter_w = 0, inter_h = 0;
373
int local_x = 0, local_y = 0;
376
bpp = _cogl_get_format_bpp (source_bmp->format);
379
_cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds, source_bmp->format);
381
/* Iterate vertical spans */
382
for (source_y = src_y,
383
_cogl_span_iter_begin (&y_iter,
384
tex_2ds->slice_y_spans,
389
!_cogl_span_iter_end (&y_iter);
391
_cogl_span_iter_next (&y_iter),
392
source_y += inter_h )
394
/* Discard slices out of the subregion early */
395
if (!y_iter.intersects)
401
y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan,
404
/* Iterate horizontal spans */
405
for (source_x = src_x,
406
_cogl_span_iter_begin (&x_iter,
407
tex_2ds->slice_x_spans,
412
!_cogl_span_iter_end (&x_iter);
414
_cogl_span_iter_next (&x_iter),
415
source_x += inter_w )
419
/* Discard slices out of the subregion early */
420
if (!x_iter.intersects)
426
x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan,
429
/* Pick intersection width and height */
430
inter_w = (x_iter.intersect_end - x_iter.intersect_start);
431
inter_h = (y_iter.intersect_end - y_iter.intersect_start);
433
/* Localize intersection top-left corner to slice*/
434
local_x = (x_iter.intersect_start - x_iter.pos);
435
local_y = (y_iter.intersect_start - y_iter.pos);
437
slice_num = y_iter.index * tex_2ds->slice_x_spans->len + x_iter.index;
439
/* Pick slice GL handle */
440
gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, slice_num);
442
_cogl_texture_driver_upload_subregion_to_gl (tex_2ds->gl_target,
449
inter_h, /* height */
454
/* Keep a copy of the first pixel if needed */
455
if (tex_2ds->first_pixels && local_x == 0 && local_y == 0)
457
memcpy (tex_2ds->first_pixels[slice_num].data,
458
source_bmp->data + source_x * bpp
459
+ source_y * source_bmp->rowstride,
461
tex_2ds->first_pixels[slice_num].gl_format = source_gl_format;
462
tex_2ds->first_pixels[slice_num].gl_type = source_gl_type;
465
/* If the x_span is sliced and the upload touches the
466
rightmost pixels then fill the waste with copies of the
468
if (x_span->waste > 0
469
&& local_x < x_span->size - x_span->waste
470
&& local_x + inter_w >= x_span->size - x_span->waste)
476
src = source_bmp->data
477
+ (src_y + ((int)y_iter.intersect_start)
479
* source_bmp->rowstride
480
+ (src_x + x_span->start + x_span->size - x_span->waste
486
for (wy = 0; wy < inter_h; wy++)
488
for (wx = 0; wx < x_span->waste; wx++)
490
memcpy (dst, src, bpp);
493
src += source_bmp->rowstride;
496
_cogl_texture_driver_prep_gl_for_pixels_upload (
500
GE( glTexSubImage2D (tex_2ds->gl_target, 0,
501
x_span->size - x_span->waste,
510
/* same for the bottom-most pixels */
511
if (y_span->waste > 0
512
&& local_y < y_span->size - y_span->waste
513
&& local_y + inter_h >= y_span->size - y_span->waste)
518
unsigned int copy_width;
520
src = source_bmp->data
521
+ (src_x + ((int)x_iter.intersect_start)
524
+ (src_y + y_span->start + y_span->size - y_span->waste
526
* source_bmp->rowstride;
530
if (local_x + inter_w >= x_span->size - x_span->waste)
531
copy_width = x_span->size - local_x;
533
copy_width = inter_w;
535
for (wy = 0; wy < y_span->waste; wy++)
537
memcpy (dst, src, inter_w * bpp);
538
dst += inter_w * bpp;
540
for (wx = inter_w; wx < copy_width; wx++)
542
memcpy (dst, dst - bpp, bpp);
547
_cogl_texture_driver_prep_gl_for_pixels_upload (
551
GE( glTexSubImage2D (tex_2ds->gl_target, 0,
553
y_span->size - y_span->waste,
566
tex_2ds->mipmaps_dirty = TRUE;
572
_cogl_rect_slices_for_size (int size_to_fill,
580
/* Init first slice span */
582
span.size = max_span_size;
585
/* Repeat until whole area covered */
586
while (size_to_fill >= span.size)
588
/* Add another slice span of same size */
590
g_array_append_val (out_spans, span);
591
span.start += span.size;
592
size_to_fill -= span.size;
596
/* Add one last smaller slice span */
597
if (size_to_fill > 0)
599
span.size = size_to_fill;
601
g_array_append_val (out_spans, span);
609
_cogl_pot_slices_for_size (int size_to_fill,
617
/* Init first slice span */
619
span.size = max_span_size;
622
/* Fix invalid max_waste */
628
/* Is the whole area covered? */
629
if (size_to_fill > span.size)
631
/* Not yet - add a span of this size */
633
g_array_append_val (out_spans, span);
635
span.start += span.size;
636
size_to_fill -= span.size;
639
else if (span.size - size_to_fill <= max_waste)
641
/* Yes and waste is small enough */
642
span.waste = span.size - size_to_fill;
644
g_array_append_val (out_spans, span);
650
/* Yes but waste is too large */
651
while (span.size - size_to_fill > max_waste)
654
g_assert (span.size > 0);
664
_cogl_texture_2d_sliced_set_wrap_mode_parameter (CoglTexture *tex,
667
CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
669
/* Only set the wrap mode if it's different from the current
670
value to avoid too many GL calls */
671
if (tex_2ds->wrap_mode != wrap_mode)
675
/* Any queued texture rectangles may be depending on the previous
677
_cogl_journal_flush ();
679
for (i = 0; i < tex_2ds->slice_gl_handles->len; i++)
681
GLuint texnum = g_array_index (tex_2ds->slice_gl_handles, GLuint, i);
683
GE( glBindTexture (tex_2ds->gl_target, texnum) );
684
GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_WRAP_S, wrap_mode) );
685
GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_WRAP_T, wrap_mode) );
688
tex_2ds->wrap_mode = wrap_mode;
693
_cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds,
694
int width, int height,
708
const GLfloat transparent_color[4] = { 0x00, 0x00, 0x00, 0x00 };
710
int (*slices_for_size) (int, int, int, GArray*);
712
/* Initialize size of largest slice according to supported features */
713
if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT))
717
tex_2ds->gl_target = GL_TEXTURE_2D;
718
slices_for_size = _cogl_rect_slices_for_size;
722
max_width = cogl_util_next_p2 (width);
723
max_height = cogl_util_next_p2 (height);
724
tex_2ds->gl_target = GL_TEXTURE_2D;
725
slices_for_size = _cogl_pot_slices_for_size;
728
/* Negative number means no slicing forced by the user */
729
if (tex_2ds->max_waste <= -1)
733
/* Check if size supported else bail out */
734
if (!_cogl_texture_driver_size_supported (tex_2ds->gl_target,
746
/* Init span arrays */
747
tex_2ds->slice_x_spans = g_array_sized_new (FALSE, FALSE,
751
tex_2ds->slice_y_spans = g_array_sized_new (FALSE, FALSE,
755
/* Add a single span for width and height */
757
span.size = max_width;
758
span.waste = max_width - width;
759
g_array_append_val (tex_2ds->slice_x_spans, span);
761
span.size = max_height;
762
span.waste = max_height - height;
763
g_array_append_val (tex_2ds->slice_y_spans, span);
767
/* Decrease the size of largest slice until supported by GL */
768
while (!_cogl_texture_driver_size_supported (tex_2ds->gl_target,
774
/* Alternate between width and height */
775
if (max_width > max_height)
780
if (max_width == 0 || max_height == 0)
784
/* Determine the slices required to cover the bitmap area */
785
n_x_slices = slices_for_size (width,
786
max_width, tex_2ds->max_waste,
789
n_y_slices = slices_for_size (height,
790
max_height, tex_2ds->max_waste,
793
/* Init span arrays with reserved size */
794
tex_2ds->slice_x_spans = g_array_sized_new (FALSE, FALSE,
798
tex_2ds->slice_y_spans = g_array_sized_new (FALSE, FALSE,
802
/* Fill span arrays with info */
803
slices_for_size (width,
804
max_width, tex_2ds->max_waste,
805
tex_2ds->slice_x_spans);
807
slices_for_size (height,
808
max_height, tex_2ds->max_waste,
809
tex_2ds->slice_y_spans);
812
/* Init and resize GL handle array */
813
n_slices = n_x_slices * n_y_slices;
815
tex_2ds->slice_gl_handles = g_array_sized_new (FALSE, FALSE,
819
g_array_set_size (tex_2ds->slice_gl_handles, n_slices);
821
/* Allocate some space to store a copy of the first pixel of each
822
slice. This is only needed if glGenerateMipmap (which is part of
823
the FBO extension) is not available */
824
if (cogl_features_available (COGL_FEATURE_OFFSCREEN))
825
tex_2ds->first_pixels = NULL;
827
tex_2ds->first_pixels = g_new (CoglTexturePixel, n_slices);
829
/* Wrap mode not yet set */
830
tex_2ds->wrap_mode = GL_FALSE;
832
/* Generate a "working set" of GL texture objects
833
* (some implementations might supported faster
834
* re-binding between textures inside a set) */
835
gl_handles = (GLuint*) tex_2ds->slice_gl_handles->data;
837
_cogl_texture_driver_gen (GL_TEXTURE_2D, n_slices, gl_handles);
839
/* Init each GL texture object */
840
for (y = 0; y < n_y_slices; ++y)
842
y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, y);
844
for (x = 0; x < n_x_slices; ++x)
846
x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, x);
848
COGL_NOTE (SLICING, "CREATE SLICE (%d,%d)\tsize (%d,%d)",
850
x_span->size - x_span->waste,
851
y_span->size - y_span->waste);
853
/* Setup texture parameters */
854
GE( glBindTexture (tex_2ds->gl_target,
855
gl_handles[y * n_x_slices + x] ) );
857
_cogl_texture_driver_try_setting_gl_border_color (tex_2ds->gl_target,
860
/* Pass NULL data to init size and internal format */
861
GE( glTexImage2D (tex_2ds->gl_target, 0, gl_intformat,
862
x_span->size, y_span->size, 0,
863
gl_format, gl_type, 0) );
871
_cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *tex_2ds)
873
if (tex_2ds->slice_x_spans != NULL)
874
g_array_free (tex_2ds->slice_x_spans, TRUE);
876
if (tex_2ds->slice_y_spans != NULL)
877
g_array_free (tex_2ds->slice_y_spans, TRUE);
879
if (tex_2ds->slice_gl_handles != NULL)
881
if (tex_2ds->is_foreign == FALSE)
883
GE( glDeleteTextures (tex_2ds->slice_gl_handles->len,
884
(GLuint*) tex_2ds->slice_gl_handles->data) );
887
g_array_free (tex_2ds->slice_gl_handles, TRUE);
890
if (tex_2ds->first_pixels != NULL)
891
g_free (tex_2ds->first_pixels);
895
_cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds)
897
_cogl_texture_2d_sliced_slices_free (tex_2ds);
902
_cogl_texture_2d_sliced_upload_from_data
903
(CoglTexture2DSliced *tex_2ds,
905
CoglPixelFormat internal_format)
907
CoglTexture *tex = COGL_TEXTURE (tex_2ds);
912
tex->vtable = &cogl_texture_2d_sliced_vtable;
914
tex_2ds->is_foreign = FALSE;
915
tex_2ds->auto_mipmap = FALSE;
916
tex_2ds->mipmaps_dirty = TRUE;
917
tex_2ds->first_pixels = NULL;
919
tex_2ds->slice_x_spans = NULL;
920
tex_2ds->slice_y_spans = NULL;
921
tex_2ds->slice_gl_handles = NULL;
923
/* We default to GL_LINEAR for both filters */
924
tex_2ds->min_filter = GL_LINEAR;
925
tex_2ds->mag_filter = GL_LINEAR;
930
gboolean dst_bmp_owner;
932
if (!_cogl_texture_prepare_for_upload (bmp,
942
/* Create slices for the given format and size */
943
if (!_cogl_texture_2d_sliced_slices_create (tex_2ds,
951
g_free (dst_bmp.data);
956
if (!_cogl_texture_2d_sliced_upload_to_gl (tex_2ds,
963
g_free (dst_bmp.data);
969
g_free (dst_bmp.data);
973
/* Find closest GL format match */
974
_cogl_pixel_format_to_gl (internal_format,
979
/* Create slices for the given format and size */
980
if (!_cogl_texture_2d_sliced_slices_create (tex_2ds,
989
tex_2ds->gl_format = gl_intformat;
990
tex_2ds->width = bmp->width;
991
tex_2ds->height = bmp->height;
992
tex_2ds->format = bmp->format;
998
_cogl_texture_2d_sliced_new_with_size (unsigned int width,
1000
CoglTextureFlags flags,
1001
CoglPixelFormat internal_format)
1003
CoglTexture2DSliced *tex_2ds;
1006
/* Since no data, we need some internal format */
1007
if (internal_format == COGL_PIXEL_FORMAT_ANY)
1008
internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
1010
/* Init texture with empty bitmap */
1011
tex_2ds = g_new (CoglTexture2DSliced, 1);
1014
bmp.height = height;
1017
if ((flags & COGL_TEXTURE_NO_SLICING))
1018
tex_2ds->max_waste = -1;
1020
tex_2ds->max_waste = COGL_TEXTURE_MAX_WASTE;
1022
if (!_cogl_texture_2d_sliced_upload_from_data (tex_2ds, &bmp,
1025
_cogl_texture_2d_sliced_free (tex_2ds);
1026
return COGL_INVALID_HANDLE;
1029
tex_2ds->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0;
1031
return _cogl_texture_2d_sliced_handle_new (tex_2ds);
1035
_cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle,
1036
CoglTextureFlags flags,
1037
CoglPixelFormat internal_format)
1039
CoglTexture2DSliced *tex_2ds;
1040
CoglBitmap *bmp = (CoglBitmap *)bmp_handle;
1042
g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE);
1044
/* Create new texture and fill with loaded data */
1045
tex_2ds = g_new0 (CoglTexture2DSliced, 1);
1047
if (flags & COGL_TEXTURE_NO_SLICING)
1048
tex_2ds->max_waste = -1;
1050
tex_2ds->max_waste = COGL_TEXTURE_MAX_WASTE;
1052
/* FIXME: If upload fails we should set some kind of
1053
* error flag but still return texture handle if the
1054
* user decides to destroy another texture and upload
1055
* this one instead (reloading from file is not needed
1056
* in that case). As a rule then, everytime a valid
1057
* CoglHandle is returned, it should also be destroyed
1058
* with cogl_handle_unref at some point! */
1060
if (!_cogl_texture_2d_sliced_upload_from_data (tex_2ds, bmp,
1063
_cogl_texture_2d_sliced_free (tex_2ds);
1064
return COGL_INVALID_HANDLE;
1067
tex_2ds->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0;
1069
return _cogl_texture_2d_sliced_handle_new (tex_2ds);
1073
_cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle,
1079
CoglPixelFormat format)
1081
/* NOTE: width, height and internal format are not queriable
1082
* in GLES, hence such a function prototype.
1085
GLenum gl_error = 0;
1086
GLboolean gl_istexture;
1087
GLint gl_compressed = GL_FALSE;
1088
GLint gl_int_format = 0;
1090
GLint gl_height = 0;
1091
GLint gl_gen_mipmap;
1092
CoglTexture2DSliced *tex_2ds;
1097
if (!_cogl_texture_driver_allows_foreign_gl_target (gl_target))
1098
return COGL_INVALID_HANDLE;
1101
/* It shouldn't be necissary to have waste in this case since
1102
* the texture isn't limited to power of two sizes. */
1103
if (gl_target == GL_TEXTURE_RECTANGLE_ARB &&
1104
(x_pot_waste != 0 || y_pot_waste != 0))
1106
g_warning ("You can't create a foreign GL_TEXTURE_RECTANGLE cogl "
1107
"texture with waste\n");
1108
return COGL_INVALID_HANDLE;
1112
/* Make sure it is a valid GL texture object */
1113
gl_istexture = glIsTexture (gl_handle);
1114
if (gl_istexture == GL_FALSE)
1115
return COGL_INVALID_HANDLE;
1117
/* Make sure binding succeeds */
1118
while ((gl_error = glGetError ()) != GL_NO_ERROR)
1120
glBindTexture (gl_target, gl_handle);
1121
if (glGetError () != GL_NO_ERROR)
1122
return COGL_INVALID_HANDLE;
1124
/* Obtain texture parameters
1125
(only level 0 we are interested in) */
1128
GE( glGetTexLevelParameteriv (gl_target, 0,
1129
GL_TEXTURE_COMPRESSED,
1132
GE( glGetTexLevelParameteriv (gl_target, 0,
1133
GL_TEXTURE_INTERNAL_FORMAT,
1137
/* Note: We always trust the given width and height without querying
1138
* the texture object because the user may be creating a Cogl
1139
* texture for a texture_from_pixmap object where glTexImage2D may
1140
* not have been called and the texture_from_pixmap spec doesn't
1141
* clarify that it is reliable to query back the size from OpenGL.
1143
gl_width = width + x_pot_waste;
1144
gl_height = height + y_pot_waste;
1146
GE( glGetTexParameteriv (gl_target,
1150
/* Validate width and height */
1151
if (gl_width <= 0 || gl_height <= 0)
1152
return COGL_INVALID_HANDLE;
1154
/* Validate pot waste */
1155
if (x_pot_waste < 0 || x_pot_waste >= gl_width ||
1156
y_pot_waste < 0 || y_pot_waste >= gl_height)
1157
return COGL_INVALID_HANDLE;
1159
/* Compressed texture images not supported */
1160
if (gl_compressed == GL_TRUE)
1161
return COGL_INVALID_HANDLE;
1163
/* Try and match to a cogl format */
1164
if (!_cogl_pixel_format_from_gl_internal (gl_int_format, &format))
1165
return COGL_INVALID_HANDLE;
1167
/* Create new texture */
1168
tex_2ds = g_new0 (CoglTexture2DSliced, 1);
1170
tex = COGL_TEXTURE (tex_2ds);
1171
tex->vtable = &cogl_texture_2d_sliced_vtable;
1173
/* Setup bitmap info */
1174
tex_2ds->is_foreign = TRUE;
1175
tex_2ds->auto_mipmap = (gl_gen_mipmap == GL_TRUE) ? TRUE : FALSE;
1176
tex_2ds->mipmaps_dirty = TRUE;
1177
tex_2ds->first_pixels = NULL;
1179
tex_2ds->format = format;
1180
tex_2ds->width = gl_width - x_pot_waste;
1181
tex_2ds->height = gl_height - y_pot_waste;
1182
tex_2ds->gl_target = gl_target;
1183
tex_2ds->gl_format = gl_int_format;
1185
/* Unknown filter */
1186
tex_2ds->min_filter = GL_FALSE;
1187
tex_2ds->mag_filter = GL_FALSE;
1188
tex_2ds->max_waste = 0;
1190
/* Wrap mode not yet set */
1191
tex_2ds->wrap_mode = GL_FALSE;
1193
/* Create slice arrays */
1194
tex_2ds->slice_x_spans =
1195
g_array_sized_new (FALSE, FALSE,
1196
sizeof (CoglSpan), 1);
1198
tex_2ds->slice_y_spans =
1199
g_array_sized_new (FALSE, FALSE,
1200
sizeof (CoglSpan), 1);
1202
tex_2ds->slice_gl_handles =
1203
g_array_sized_new (FALSE, FALSE,
1204
sizeof (GLuint), 1);
1206
/* Store info for a single slice */
1208
x_span.size = gl_width;
1209
x_span.waste = x_pot_waste;
1210
g_array_append_val (tex_2ds->slice_x_spans, x_span);
1213
y_span.size = gl_height;
1214
y_span.waste = y_pot_waste;
1215
g_array_append_val (tex_2ds->slice_y_spans, y_span);
1217
g_array_append_val (tex_2ds->slice_gl_handles, gl_handle);
1219
tex_2ds->first_pixels = NULL;
1221
return _cogl_texture_2d_sliced_handle_new (tex_2ds);
1225
_cogl_texture_2d_sliced_get_max_waste (CoglTexture *tex)
1227
CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
1229
return tex_2ds->max_waste;
1233
_cogl_texture_2d_sliced_is_sliced (CoglTexture *tex)
1235
CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
1237
if (tex_2ds->slice_gl_handles == NULL)
1240
if (tex_2ds->slice_gl_handles->len <= 1)
1247
_cogl_texture_2d_sliced_can_hardware_repeat (CoglTexture *tex)
1249
CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
1253
x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0);
1254
y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0);
1257
/* TODO: COGL_TEXTURE_TYPE_2D_RECTANGLE */
1258
if (tex_2ds->gl_target == GL_TEXTURE_RECTANGLE_ARB)
1262
return (x_span->waste || y_span->waste) ? FALSE : TRUE;
1266
_cogl_texture_2d_sliced_transform_coords_to_gl (CoglTexture *tex,
1270
CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
1274
g_assert (!_cogl_texture_2d_sliced_is_sliced (tex));
1276
/* Don't include the waste in the texture coordinates */
1277
x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0);
1278
y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0);
1280
*s *= tex_2ds->width / (float)x_span->size;
1281
*t *= tex_2ds->height / (float)y_span->size;
1284
/* Denormalize texture coordinates for rectangle textures */
1285
if (tex_2ds->gl_target == GL_TEXTURE_RECTANGLE_ARB)
1293
static CoglTransformResult
1294
_cogl_texture_2d_sliced_transform_quad_coords_to_gl (CoglTexture *tex,
1297
gboolean need_repeat = FALSE;
1300
/* This is a bit lazy - in the case where the quad lies entirely
1301
* within a single slice we could avoid the fallback. But that
1302
* could likely lead to visual inconsistency if the fallback involves
1303
* dropping layers, so this might be the right thing to do anyways.
1305
if (_cogl_texture_2d_sliced_is_sliced (tex))
1306
return COGL_TRANSFORM_SOFTWARE_REPEAT;
1308
for (i = 0; i < 4; i++)
1309
if (coords[i] < 0.0f || coords[i] > 1.0f)
1312
if (need_repeat && !_cogl_texture_2d_sliced_can_hardware_repeat (tex))
1313
return COGL_TRANSFORM_SOFTWARE_REPEAT;
1315
_cogl_texture_2d_sliced_transform_coords_to_gl (tex, coords + 0, coords + 1);
1316
_cogl_texture_2d_sliced_transform_coords_to_gl (tex, coords + 2, coords + 3);
1319
? COGL_TRANSFORM_HARDWARE_REPEAT : COGL_TRANSFORM_NO_REPEAT);
1323
_cogl_texture_2d_sliced_get_gl_texture (CoglTexture *tex,
1324
GLuint *out_gl_handle,
1325
GLenum *out_gl_target)
1327
CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
1329
if (tex_2ds->slice_gl_handles == NULL)
1332
if (tex_2ds->slice_gl_handles->len < 1)
1335
if (out_gl_handle != NULL)
1336
*out_gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, 0);
1338
if (out_gl_target != NULL)
1339
*out_gl_target = tex_2ds->gl_target;
1345
_cogl_texture_2d_sliced_set_filters (CoglTexture *tex,
1349
CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
1353
/* Make sure slices were created */
1354
if (tex_2ds->slice_gl_handles == NULL)
1357
if (min_filter == tex_2ds->min_filter
1358
&& mag_filter == tex_2ds->mag_filter)
1361
/* Store new values */
1362
tex_2ds->min_filter = min_filter;
1363
tex_2ds->mag_filter = mag_filter;
1365
/* Apply new filters to every slice */
1366
for (i=0; i<tex_2ds->slice_gl_handles->len; ++i)
1368
gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, i);
1369
GE( glBindTexture (tex_2ds->gl_target, gl_handle) );
1370
GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_MAG_FILTER,
1371
tex_2ds->mag_filter) );
1372
GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_MIN_FILTER,
1373
tex_2ds->min_filter) );
1378
_cogl_texture_2d_sliced_ensure_mipmaps (CoglTexture *tex)
1380
CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
1383
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
1385
/* Only update if the mipmaps are dirty */
1386
if (!tex_2ds->auto_mipmap || !tex_2ds->mipmaps_dirty)
1389
/* Make sure slices were created */
1390
if (tex_2ds->slice_gl_handles == NULL)
1393
/* Regenerate the mipmaps on every slice */
1394
for (i = 0; i < tex_2ds->slice_gl_handles->len; i++)
1396
GLuint gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, i);
1397
GE( glBindTexture (tex_2ds->gl_target, gl_handle) );
1399
/* glGenerateMipmap is defined in the FBO extension */
1400
if (cogl_features_available (COGL_FEATURE_OFFSCREEN))
1401
_cogl_texture_driver_gl_generate_mipmaps (tex_2ds->gl_target);
1402
else if (tex_2ds->first_pixels)
1404
CoglTexturePixel *pixel = tex_2ds->first_pixels + i;
1405
/* Temporarily enable automatic mipmap generation and
1406
re-upload the first pixel to cause a regeneration */
1407
GE( glTexParameteri (tex_2ds->gl_target, GL_GENERATE_MIPMAP, GL_TRUE) );
1408
GE( glTexSubImage2D (tex_2ds->gl_target, 0, 0, 0, 1, 1,
1409
pixel->gl_format, pixel->gl_type,
1411
GE( glTexParameteri (tex_2ds->gl_target, GL_GENERATE_MIPMAP, GL_FALSE) );
1415
tex_2ds->mipmaps_dirty = FALSE;
1419
_cogl_texture_2d_sliced_ensure_non_quad_rendering (CoglTexture *tex)
1421
/* Nothing needs to be done */
1425
_cogl_texture_2d_sliced_set_region (CoglTexture *tex,
1430
unsigned int dst_width,
1431
unsigned int dst_height,
1434
CoglPixelFormat format,
1435
unsigned int rowstride,
1438
CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
1440
CoglBitmap source_bmp;
1442
gboolean tmp_bmp_owner = FALSE;
1443
GLenum closest_gl_format;
1444
GLenum closest_gl_type;
1446
/* Check for valid format */
1447
if (format == COGL_PIXEL_FORMAT_ANY)
1450
/* Shortcut out early if the image is empty */
1451
if (width == 0 || height == 0)
1454
/* Init source bitmap */
1455
source_bmp.width = width;
1456
source_bmp.height = height;
1457
source_bmp.format = format;
1458
source_bmp.data = (guint8 *)data;
1460
/* Rowstride from width if none specified */
1461
bpp = _cogl_get_format_bpp (format);
1462
source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride;
1464
/* Prepare the bitmap so that it will do the premultiplication
1466
_cogl_texture_prepare_for_upload (&source_bmp,
1476
/* Send data to GL */
1477
_cogl_texture_2d_sliced_upload_subregion_to_gl (tex_2ds,
1480
dst_width, dst_height,
1485
/* Free data if owner */
1487
g_free (tmp_bmp.data);
1493
_cogl_texture_2d_sliced_download_from_gl (
1494
CoglTexture2DSliced *tex_2ds,
1495
CoglBitmap *target_bmp,
1496
GLuint target_gl_format,
1497
GLuint target_gl_type)
1504
CoglBitmap slice_bmp;
1506
bpp = _cogl_get_format_bpp (target_bmp->format);
1508
/* Iterate vertical slices */
1509
for (y = 0; y < tex_2ds->slice_y_spans->len; ++y)
1511
y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, y);
1513
/* Iterate horizontal slices */
1514
for (x = 0; x < tex_2ds->slice_x_spans->len; ++x)
1516
/*if (x != 0 || y != 1) continue;*/
1517
x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, x);
1519
/* Pick the gl texture object handle */
1520
gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint,
1521
y * tex_2ds->slice_x_spans->len + x);
1523
/* If there's any waste we need to copy manually
1524
(no glGetTexSubImage) */
1526
if (y_span->waste != 0 || x_span->waste != 0)
1528
/* Setup temp bitmap for slice subregion */
1529
slice_bmp.format = target_bmp->format;
1530
slice_bmp.width = x_span->size;
1531
slice_bmp.height = y_span->size;
1532
slice_bmp.rowstride = bpp * slice_bmp.width;
1533
slice_bmp.data = g_malloc (slice_bmp.rowstride *
1536
/* Setup gl alignment to 0,0 top-left corner */
1537
_cogl_texture_driver_prep_gl_for_pixels_download (
1538
slice_bmp.rowstride,
1541
/* Download slice image data into temp bmp */
1542
GE( glBindTexture (tex_2ds->gl_target, gl_handle) );
1544
if (!_cogl_texture_driver_gl_get_tex_image (tex_2ds->gl_target,
1549
/* Free temp bitmap */
1550
g_free (slice_bmp.data);
1554
/* Copy portion of slice from temp to target bmp */
1555
_cogl_bitmap_copy_subregion (&slice_bmp,
1560
x_span->size - x_span->waste,
1561
y_span->size - y_span->waste);
1562
/* Free temp bitmap */
1563
g_free (slice_bmp.data);
1567
GLvoid *dst = target_bmp->data
1568
+ x_span->start * bpp
1569
+ y_span->start * target_bmp->rowstride;
1571
_cogl_texture_driver_prep_gl_for_pixels_download (
1572
target_bmp->rowstride,
1575
/* Download slice image data */
1576
GE( glBindTexture (tex_2ds->gl_target, gl_handle) );
1578
if (!_cogl_texture_driver_gl_get_tex_image (tex_2ds->gl_target,
1593
_cogl_texture_2d_sliced_get_data (CoglTexture *tex,
1594
CoglPixelFormat format,
1595
unsigned int rowstride,
1598
CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
1601
CoglPixelFormat closest_format;
1603
GLenum closest_gl_format;
1604
GLenum closest_gl_type;
1605
CoglBitmap target_bmp;
1612
/* Default to internal format if none specified */
1613
if (format == COGL_PIXEL_FORMAT_ANY)
1614
format = tex_2ds->format;
1616
/* Rowstride from texture width if none specified */
1617
bpp = _cogl_get_format_bpp (format);
1619
rowstride = tex_2ds->width * bpp;
1621
/* Return byte size if only that requested */
1622
byte_size = tex_2ds->height * rowstride;
1627
_cogl_texture_driver_find_best_gl_get_data_format (format,
1630
closest_bpp = _cogl_get_format_bpp (closest_format);
1632
target_bmp.width = tex_2ds->width;
1633
target_bmp.height = tex_2ds->height;
1635
/* Is the requested format supported? */
1636
if (closest_format == format)
1638
/* Target user data directly */
1639
target_bmp.format = format;
1640
target_bmp.rowstride = rowstride;
1641
target_bmp.data = data;
1645
/* Target intermediate buffer */
1646
target_bmp.format = closest_format;
1647
target_bmp.rowstride = target_bmp.width * closest_bpp;
1648
target_bmp.data = g_malloc (target_bmp.height * target_bmp.rowstride);
1651
/* Retrieve data from slices */
1652
if (!_cogl_texture_2d_sliced_download_from_gl (tex_2ds, &target_bmp,
1656
/* XXX: In some cases _cogl_texture_2d_sliced_download_from_gl may
1657
* fail to read back the texture data; such as for GLES which doesn't
1658
* support glGetTexImage, so here we fallback to drawing the texture
1659
* and reading the pixels from the framebuffer. */
1660
_cogl_texture_draw_and_read (tex, &target_bmp,
1665
/* Was intermediate used? */
1666
if (closest_format != format)
1668
/* Convert to requested format */
1669
success = _cogl_bitmap_convert_format_and_premult (&target_bmp,
1673
/* Free intermediate data and return if failed */
1674
g_free (target_bmp.data);
1675
if (!success) return 0;
1677
/* Copy to user buffer */
1678
for (y = 0; y < new_bmp.height; ++y)
1680
src = new_bmp.data + y * new_bmp.rowstride;
1681
dst = data + y * rowstride;
1682
memcpy (dst, src, new_bmp.width);
1685
/* Free converted data */
1686
g_free (new_bmp.data);
1692
static CoglPixelFormat
1693
_cogl_texture_2d_sliced_get_format (CoglTexture *tex)
1695
return COGL_TEXTURE_2D_SLICED (tex)->format;
1699
_cogl_texture_2d_sliced_get_gl_format (CoglTexture *tex)
1701
return COGL_TEXTURE_2D_SLICED (tex)->gl_format;
1705
_cogl_texture_2d_sliced_get_width (CoglTexture *tex)
1707
return COGL_TEXTURE_2D_SLICED (tex)->width;
1711
_cogl_texture_2d_sliced_get_height (CoglTexture *tex)
1713
return COGL_TEXTURE_2D_SLICED (tex)->height;
1716
static const CoglTextureVtable
1717
cogl_texture_2d_sliced_vtable =
1719
_cogl_texture_2d_sliced_set_region,
1720
_cogl_texture_2d_sliced_get_data,
1721
_cogl_texture_2d_sliced_foreach_sub_texture_in_region,
1722
_cogl_texture_2d_sliced_get_max_waste,
1723
_cogl_texture_2d_sliced_is_sliced,
1724
_cogl_texture_2d_sliced_can_hardware_repeat,
1725
_cogl_texture_2d_sliced_transform_coords_to_gl,
1726
_cogl_texture_2d_sliced_transform_quad_coords_to_gl,
1727
_cogl_texture_2d_sliced_get_gl_texture,
1728
_cogl_texture_2d_sliced_set_filters,
1729
_cogl_texture_2d_sliced_ensure_mipmaps,
1730
_cogl_texture_2d_sliced_ensure_non_quad_rendering,
1731
_cogl_texture_2d_sliced_set_wrap_mode_parameter,
1732
_cogl_texture_2d_sliced_get_format,
1733
_cogl_texture_2d_sliced_get_gl_format,
1734
_cogl_texture_2d_sliced_get_width,
1735
_cogl_texture_2d_sliced_get_height