4
* An object oriented GL/GLES Abstraction/Utility Layer
6
* Copyright (C) 2011 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
20
* <http://www.gnu.org/licenses/>.
24
* Robert Bragg <robert@linux.intel.com>
31
#include "cogl-texture.h"
32
#include "cogl-matrix.h"
33
#include "cogl-spans.h"
34
#include "cogl-meta-texture.h"
35
#include "cogl-texture-rectangle-private.h"
40
typedef struct _ForeachData
42
float meta_region_coords[4];
43
CoglPipelineWrapMode wrap_s;
44
CoglPipelineWrapMode wrap_t;
45
CoglMetaTextureCallback callback;
51
CoglTexture *padded_textures[9];
52
const float *grid_slice_texture_coords;
60
padded_grid_repeat_cb (CoglTexture *slice_texture,
61
const float *slice_texture_coords,
62
const float *meta_coords,
66
float mapped_coords[4];
68
/* Ignore padding slices for the current grid */
74
/* NB: the slice_texture_coords[] we get here will always be
77
* We now need to map the normalized slice_texture_coords[] we have
78
* here back to the real slice coordinates we saved in the previous
82
slice_texture_coords[0] * data->slice_range_s + data->slice_offset_s;
84
slice_texture_coords[1] * data->slice_range_t + data->slice_offset_t;
86
slice_texture_coords[2] * data->slice_range_s + data->slice_offset_s;
88
slice_texture_coords[3] * data->slice_range_t + data->slice_offset_t;
90
data->callback (slice_texture,
91
mapped_coords, meta_coords, data->user_data);
95
setup_padded_spans (CoglSpan *spans,
106
spans[0].size = start;
109
spans[1].start = spans[0].size;
112
spans[span_index].start = 0;
114
spans[span_index].size = end - start;
115
spans[span_index].waste = 0;
116
*real_index = span_index;
121
spans[span_index].start =
122
spans[span_index - 1].start + spans[span_index - 1].size;
123
spans[span_index].size = range - end;
124
spans[span_index].waste = 0;
131
/* This handles each sub-texture within the range [0,1] of our
132
* original meta texture and repeats each one separately across the
133
* users requested virtual texture coordinates.
135
* A notable advantage of this approach is that we will batch
136
* together callbacks corresponding to the same underlying slice
140
create_grid_and_repeat_cb (CoglTexture *slice_texture,
141
const float *slice_texture_coords,
142
const float *meta_coords,
145
ForeachData *data = user_data;
153
/* NB: This callback is called for each slice of the meta-texture
154
* in the range [0,1].
156
* We define a "padded grid" for each slice of the meta-texture in
157
* the range [0,1]. The x axis and y axis grid lines are defined
160
* The padded grid maps over the meta-texture coordinates in the
161
* range [0,1] but only contains one valid cell that corresponds to
162
* current slice being iterated and all the surrounding cells just
165
* Once we've defined our padded grid we then repeat that across
166
* the user's original region, calling their callback whenever
167
* we see our current slice - ignoring padding.
169
* NB: we can assume meta_coords[] are normalized at this point
170
* since TextureRectangles aren't iterated with this code-path.
172
* NB: spans are always defined using non-normalized coordinates
174
n_x_spans = setup_padded_spans (x_spans,
175
meta_coords[0] * data->width,
176
meta_coords[2] * data->width,
179
n_y_spans = setup_padded_spans (y_spans,
180
meta_coords[1] * data->height,
181
meta_coords[3] * data->height,
185
data->padded_textures[n_y_spans * y_real_index + x_real_index] =
188
/* Our callback is going to be passed normalized slice texture
189
* coordinates, and we will need to map the range [0,1] to the real
190
* slice_texture_coords we have here... */
191
data->grid_slice_texture_coords = slice_texture_coords;
192
data->slice_range_s = fabs (data->grid_slice_texture_coords[2] -
193
data->grid_slice_texture_coords[0]);
194
data->slice_range_t = fabs (data->grid_slice_texture_coords[3] -
195
data->grid_slice_texture_coords[1]);
196
data->slice_offset_s = MIN (data->grid_slice_texture_coords[0],
197
data->grid_slice_texture_coords[2]);
198
data->slice_offset_t = MIN (data->grid_slice_texture_coords[1],
199
data->grid_slice_texture_coords[3]);
201
/* Now actually iterate the region the user originally requested
202
* using the current padded grid */
203
_cogl_texture_spans_foreach_in_region (x_spans,
207
data->padded_textures,
208
data->meta_region_coords,
213
padded_grid_repeat_cb,
216
/* Clear the padded_textures ready for the next iteration */
217
data->padded_textures[n_y_spans * y_real_index + x_real_index] = NULL;
220
#define SWAP(A,B) do { float tmp = B; B = A; A = tmp; } while (0)
222
typedef struct _ClampData
228
CoglMetaTextureCallback callback;
233
clamp_s_cb (CoglTexture *sub_texture,
234
const float *sub_texture_coords,
235
const float *meta_coords,
238
ClampData *clamp_data = user_data;
239
float mapped_meta_coords[4] = {
245
if (clamp_data->s_flipped)
246
SWAP (mapped_meta_coords[0], mapped_meta_coords[2]);
247
/* NB: we never need to flip the t coords when dealing with
248
* s-axis clamping so no need to check if ->t_flipped */
249
clamp_data->callback (sub_texture,
250
sub_texture_coords, mapped_meta_coords,
251
clamp_data->user_data);
255
clamp_t_cb (CoglTexture *sub_texture,
256
const float *sub_texture_coords,
257
const float *meta_coords,
260
ClampData *clamp_data = user_data;
261
float mapped_meta_coords[4] = {
267
if (clamp_data->s_flipped)
268
SWAP (mapped_meta_coords[0], mapped_meta_coords[2]);
269
if (clamp_data->t_flipped)
270
SWAP (mapped_meta_coords[1], mapped_meta_coords[3]);
271
clamp_data->callback (sub_texture,
272
sub_texture_coords, mapped_meta_coords,
273
clamp_data->user_data);
277
foreach_clamped_region (CoglMetaTexture *meta_texture,
282
CoglPipelineWrapMode wrap_s,
283
CoglPipelineWrapMode wrap_t,
284
CoglMetaTextureCallback callback,
287
float width = cogl_texture_get_width (COGL_TEXTURE (meta_texture));
288
ClampData clamp_data;
290
/* Consider that *tx_1 may be > *tx_2 and to simplify things
291
* we just flip them around if that's the case and keep a note
292
* of the fact that they are flipped. */
296
clamp_data.s_flipped = TRUE;
299
clamp_data.s_flipped = FALSE;
301
/* The same goes for ty_1 and ty_2... */
305
clamp_data.t_flipped = TRUE;
308
clamp_data.t_flipped = FALSE;
310
clamp_data.callback = callback;
311
clamp_data.user_data = user_data;
313
if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
316
float half_texel_width;
318
/* Consider that rectangle textures have non-normalized
320
if (cogl_is_texture_rectangle (meta_texture))
325
half_texel_width = max_s_coord / (width * 2);
327
/* Handle any left clamped region */
330
clamp_data.start = *tx_1;
331
clamp_data.end = MIN (0, *tx_2);;
332
cogl_meta_texture_foreach_in_region (meta_texture,
333
half_texel_width, *ty_1,
334
half_texel_width, *ty_2,
335
COGL_PIPELINE_WRAP_MODE_REPEAT,
339
/* Have we handled everything? */
343
/* clamp tx_1 since we've handled everything with x < 0 */
347
/* Handle any right clamped region - including the corners */
348
if (*tx_2 > max_s_coord)
350
clamp_data.start = MAX (max_s_coord, *tx_1);
351
clamp_data.end = *tx_2;
352
cogl_meta_texture_foreach_in_region (meta_texture,
353
max_s_coord - half_texel_width,
355
max_s_coord - half_texel_width,
357
COGL_PIPELINE_WRAP_MODE_REPEAT,
361
/* Have we handled everything? */
362
if (*tx_1 >= max_s_coord)
365
/* clamp tx_2 since we've handled everything with x >
371
if (wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
373
float height = cogl_texture_get_height (COGL_TEXTURE (meta_texture));
375
float half_texel_height;
377
/* Consider that rectangle textures have non-normalized
379
if (cogl_is_texture_rectangle (meta_texture))
380
max_t_coord = height;
384
half_texel_height = max_t_coord / (height * 2);
386
/* Handle any top clamped region */
389
clamp_data.start = *ty_1;
390
clamp_data.end = MIN (0, *ty_2);;
391
cogl_meta_texture_foreach_in_region (meta_texture,
392
*tx_1, half_texel_height,
393
*tx_2, half_texel_height,
395
COGL_PIPELINE_WRAP_MODE_REPEAT,
398
/* Have we handled everything? */
402
/* clamp ty_1 since we've handled everything with y < 0 */
406
/* Handle any bottom clamped region */
407
if (*ty_2 > max_t_coord)
409
clamp_data.start = MAX (max_t_coord, *ty_1);;
410
clamp_data.end = *ty_2;
411
cogl_meta_texture_foreach_in_region (meta_texture,
413
max_t_coord - half_texel_height,
415
max_t_coord - half_texel_height,
417
COGL_PIPELINE_WRAP_MODE_REPEAT,
420
/* Have we handled everything? */
421
if (*ty_1 >= max_t_coord)
424
/* clamp ty_2 since we've handled everything with y >
430
if (clamp_data.s_flipped)
432
if (clamp_data.t_flipped)
438
typedef struct _NormalizeData
440
CoglMetaTextureCallback callback;
442
float s_normalize_factor;
443
float t_normalize_factor;
447
normalize_meta_coords_cb (CoglTexture *slice_texture,
448
const float *slice_coords,
449
const float *meta_coords,
452
NormalizeData *data = user_data;
453
float normalized_meta_coords[4] = {
454
meta_coords[0] * data->s_normalize_factor,
455
meta_coords[1] * data->t_normalize_factor,
456
meta_coords[2] * data->s_normalize_factor,
457
meta_coords[3] * data->t_normalize_factor
460
data->callback (slice_texture,
461
slice_coords, normalized_meta_coords,
465
typedef struct _UnNormalizeData
467
CoglMetaTextureCallback callback;
474
un_normalize_slice_coords_cb (CoglTexture *slice_texture,
475
const float *slice_coords,
476
const float *meta_coords,
479
UnNormalizeData *data = user_data;
480
float un_normalized_slice_coords[4] = {
481
slice_coords[0] * data->width,
482
slice_coords[1] * data->height,
483
slice_coords[2] * data->width,
484
slice_coords[3] * data->height
487
data->callback (slice_texture,
488
un_normalized_slice_coords, meta_coords,
493
cogl_meta_texture_foreach_in_region (CoglMetaTexture *meta_texture,
498
CoglPipelineWrapMode wrap_s,
499
CoglPipelineWrapMode wrap_t,
500
CoglMetaTextureCallback callback,
503
CoglTexture *texture = COGL_TEXTURE (meta_texture);
504
float width = cogl_texture_get_width (texture);
505
float height = cogl_texture_get_height (texture);
506
NormalizeData normalize_data;
508
if (wrap_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
509
wrap_s = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
510
if (wrap_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
511
wrap_t = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
513
if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE ||
514
wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
516
gboolean finished = foreach_clamped_region (meta_texture,
517
&tx_1, &ty_1, &tx_2, &ty_2,
524
/* Since clamping has been handled we now want to normalize our
525
* wrap modes we se can assume from this point on we don't
526
* need to consider CLAMP_TO_EDGE. (NB: The spans code will
527
* assert that CLAMP_TO_EDGE isn't requested) */
528
if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
529
wrap_s = COGL_PIPELINE_WRAP_MODE_REPEAT;
530
if (wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
531
wrap_t = COGL_PIPELINE_WRAP_MODE_REPEAT;
534
/* It makes things simpler to deal with non-normalized region
535
* coordinates beyond this point and only re-normalize just before
536
* calling the user's callback... */
538
if (!cogl_is_texture_rectangle (COGL_TEXTURE (meta_texture)))
540
normalize_data.callback = callback;
541
normalize_data.user_data = user_data;
542
normalize_data.s_normalize_factor = 1.0f / width;
543
normalize_data.t_normalize_factor = 1.0f / height;
544
callback = normalize_meta_coords_cb;
545
user_data = &normalize_data;
552
/* XXX: at some point this wont be routed through the CoglTexture
553
* vtable, instead there will be a separate CoglMetaTexture
554
* interface vtable. */
556
if (texture->vtable->foreach_sub_texture_in_region)
560
data.meta_region_coords[0] = tx_1;
561
data.meta_region_coords[1] = ty_1;
562
data.meta_region_coords[2] = tx_2;
563
data.meta_region_coords[3] = ty_2;
564
data.wrap_s = wrap_s;
565
data.wrap_t = wrap_t;
566
data.callback = callback;
567
data.user_data = user_data;
570
data.height = height;
572
memset (data.padded_textures, 0, sizeof (data.padded_textures));
575
* 1) We iterate all the slices of the meta-texture only within
578
* 2) We define a "padded grid" for each slice of the
579
* meta-texture in the range [0,1].
581
* The padded grid maps over the meta-texture coordinates in
582
* the range [0,1] but only contains one valid cell that
583
* corresponds to current slice being iterated and all the
584
* surrounding cells just provide padding.
586
* 3) Once we've defined our padded grid we then repeat that
587
* across the user's original region, calling their callback
588
* whenever we see our current slice - ignoring padding.
590
* A notable benefit of this design is that repeating a texture
591
* made of multiple slices will result in us repeating each
592
* slice in-turn so the user gets repeat callbacks for the same
593
* texture batched together. For manual emulation of texture
594
* repeats done by drawing geometry this makes it more likely
595
* that we can batch geometry.
598
texture->vtable->foreach_sub_texture_in_region (texture,
600
create_grid_and_repeat_cb,
605
CoglSpan x_span = { 0, width, 0 };
606
CoglSpan y_span = { 0, height, 0 };
607
float meta_region_coords[4] = { tx_1, ty_1, tx_2, ty_2 };
608
UnNormalizeData un_normalize_data;
610
/* If we are dealing with a CoglTextureRectangle then we need a shim
611
* callback that un_normalizes the slice coordinates we get from
612
* _cogl_texture_spans_foreach_in_region before passing them to
613
* the user's callback. */
614
if (cogl_is_texture_rectangle (meta_texture))
616
un_normalize_data.callback = callback;
617
un_normalize_data.user_data = user_data;
618
un_normalize_data.width = width;
619
un_normalize_data.height = height;
620
callback = un_normalize_slice_coords_cb;
621
user_data = &un_normalize_data;
624
_cogl_texture_spans_foreach_in_region (&x_span, 1,