1 /*
2 * Cogl
3 *
4 * A Low Level GPU Graphics and Utilities API
5 *
6 * Copyright (C) 2007,2008,2009 Intel Corporation.
7 * Copyright (C) 2010 Red Hat, Inc.
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use, copy,
13 * modify, merge, publish, distribute, sublicense, and/or sell copies
14 * of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 *
29 *
30 *
31 * Authors:
32 * Matthew Allum <mallum@openedhand.com>
33 * Neil Roberts <neil@linux.intel.com>
34 * Robert Bragg <robert@linux.intel.com>
35 */
36
37 #include "cogl-config.h"
38
39 #include "cogl-util.h"
40 #include "cogl-bitmap.h"
41 #include "cogl-bitmap-private.h"
42 #include "cogl-buffer-private.h"
43 #include "cogl-pixel-buffer-private.h"
44 #include "cogl-private.h"
45 #include "cogl-texture-private.h"
46 #include "cogl-texture-driver.h"
47 #include "cogl-texture-2d-sliced-private.h"
48 #include "cogl-texture-2d-private.h"
49 #include "cogl-sub-texture-private.h"
50 #include "cogl-atlas-texture-private.h"
51 #include "cogl-pipeline.h"
52 #include "cogl-context-private.h"
53 #include "cogl-object-private.h"
54 #include "cogl-object-private.h"
55 #include "cogl-offscreen-private.h"
56 #include "cogl-framebuffer-private.h"
57 #include "cogl1-context.h"
58 #include "cogl-sub-texture.h"
59 #include "cogl-primitive-texture.h"
60 #include "cogl-gtype-private.h"
61
62 #include <string.h>
63 #include <stdlib.h>
64 #include <math.h>
65
66 COGL_GTYPE_DEFINE_INTERFACE (Texture, texture);
67
68 uint32_t
cogl_texture_error_quark(void)69 cogl_texture_error_quark (void)
70 {
71 return g_quark_from_static_string ("cogl-texture-error-quark");
72 }
73
74 /* XXX:
75 * The CoglObject macros don't support any form of inheritance, so for
76 * now we implement the CoglObject support for the CoglTexture
77 * abstract class manually.
78 */
79
80 static GSList *_cogl_texture_types;
81
82 void
_cogl_texture_register_texture_type(const CoglObjectClass * klass)83 _cogl_texture_register_texture_type (const CoglObjectClass *klass)
84 {
85 _cogl_texture_types = g_slist_prepend (_cogl_texture_types, (void *) klass);
86 }
87
88 gboolean
cogl_is_texture(void * object)89 cogl_is_texture (void *object)
90 {
91 CoglObject *obj = (CoglObject *)object;
92 GSList *l;
93
94 if (object == NULL)
95 return FALSE;
96
97 for (l = _cogl_texture_types; l; l = l->next)
98 if (l->data == obj->klass)
99 return TRUE;
100
101 return FALSE;
102 }
103
104 void
_cogl_texture_init(CoglTexture * texture,CoglContext * context,int width,int height,CoglPixelFormat src_format,CoglTextureLoader * loader,const CoglTextureVtable * vtable)105 _cogl_texture_init (CoglTexture *texture,
106 CoglContext *context,
107 int width,
108 int height,
109 CoglPixelFormat src_format,
110 CoglTextureLoader *loader,
111 const CoglTextureVtable *vtable)
112 {
113 texture->context = context;
114 texture->max_level_set = 0;
115 texture->max_level_requested = 1000; /* OpenGL default GL_TEXTURE_MAX_LEVEL */
116 texture->width = width;
117 texture->height = height;
118 texture->allocated = FALSE;
119 texture->vtable = vtable;
120 texture->framebuffers = NULL;
121
122 texture->loader = loader;
123
124 _cogl_texture_set_internal_format (texture, src_format);
125
126 /* Although we want to initialize texture::components according
127 * to the source format, we always want the internal layout to
128 * be considered premultiplied by default.
129 *
130 * NB: this ->premultiplied state is user configurable so to avoid
131 * awkward documentation, setting this to 'true' does not depend on
132 * ->components having an alpha component (we will simply ignore the
133 * premultiplied status later if there is no alpha component).
134 * This way we don't have to worry about updating the
135 * ->premultiplied state in _set_components(). Similarly we don't
136 * have to worry about updating the ->components state in
137 * _set_premultiplied().
138 */
139 texture->premultiplied = TRUE;
140 }
141
142 static void
_cogl_texture_free_loader(CoglTexture * texture)143 _cogl_texture_free_loader (CoglTexture *texture)
144 {
145 if (texture->loader)
146 {
147 CoglTextureLoader *loader = texture->loader;
148 switch (loader->src_type)
149 {
150 case COGL_TEXTURE_SOURCE_TYPE_SIZED:
151 case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE:
152 case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE_EXTERNAL:
153 break;
154 case COGL_TEXTURE_SOURCE_TYPE_BITMAP:
155 cogl_object_unref (loader->src.bitmap.bitmap);
156 break;
157 }
158 g_free (loader);
159 texture->loader = NULL;
160 }
161 }
162
163 CoglTextureLoader *
_cogl_texture_create_loader(void)164 _cogl_texture_create_loader (void)
165 {
166 return g_new0 (CoglTextureLoader, 1);
167 }
168
169 void
_cogl_texture_free(CoglTexture * texture)170 _cogl_texture_free (CoglTexture *texture)
171 {
172 _cogl_texture_free_loader (texture);
173
174 g_free (texture);
175 }
176
177 gboolean
_cogl_texture_needs_premult_conversion(CoglPixelFormat src_format,CoglPixelFormat dst_format)178 _cogl_texture_needs_premult_conversion (CoglPixelFormat src_format,
179 CoglPixelFormat dst_format)
180 {
181 return ((src_format & dst_format & COGL_A_BIT) &&
182 src_format != COGL_PIXEL_FORMAT_A_8 &&
183 dst_format != COGL_PIXEL_FORMAT_A_8 &&
184 (src_format & COGL_PREMULT_BIT) !=
185 (dst_format & COGL_PREMULT_BIT));
186 }
187
188 gboolean
cogl_texture_is_get_data_supported(CoglTexture * texture)189 cogl_texture_is_get_data_supported (CoglTexture *texture)
190 {
191 if (texture->vtable->is_get_data_supported)
192 return texture->vtable->is_get_data_supported (texture);
193 else
194 return TRUE;
195 }
196
197 unsigned int
cogl_texture_get_width(CoglTexture * texture)198 cogl_texture_get_width (CoglTexture *texture)
199 {
200 return texture->width;
201 }
202
203 unsigned int
cogl_texture_get_height(CoglTexture * texture)204 cogl_texture_get_height (CoglTexture *texture)
205 {
206 return texture->height;
207 }
208
209 CoglPixelFormat
_cogl_texture_get_format(CoglTexture * texture)210 _cogl_texture_get_format (CoglTexture *texture)
211 {
212 if (!texture->allocated)
213 cogl_texture_allocate (texture, NULL);
214 return texture->vtable->get_format (texture);
215 }
216
217 int
cogl_texture_get_max_waste(CoglTexture * texture)218 cogl_texture_get_max_waste (CoglTexture *texture)
219 {
220 return texture->vtable->get_max_waste (texture);
221 }
222
223 int
_cogl_texture_get_n_levels(CoglTexture * texture)224 _cogl_texture_get_n_levels (CoglTexture *texture)
225 {
226 int width = cogl_texture_get_width (texture);
227 int height = cogl_texture_get_height (texture);
228 int max_dimension = MAX (width, height);
229 int n_levels = _cogl_util_fls (max_dimension);
230
231 return MIN (n_levels, texture->max_level_requested + 1);
232 }
233
234 void
cogl_texture_set_max_level(CoglTexture * texture,int max_level)235 cogl_texture_set_max_level (CoglTexture *texture,
236 int max_level)
237 {
238 texture->max_level_requested = max_level;
239 }
240
241 void
_cogl_texture_get_level_size(CoglTexture * texture,int level,int * width,int * height,int * depth)242 _cogl_texture_get_level_size (CoglTexture *texture,
243 int level,
244 int *width,
245 int *height,
246 int *depth)
247 {
248 int current_width = cogl_texture_get_width (texture);
249 int current_height = cogl_texture_get_height (texture);
250 int current_depth = 0;
251 int i;
252
253 /* NB: The OpenGL spec (like D3D) uses a floor() convention to
254 * round down the size of a mipmap level when dividing the size
255 * of the previous level results in a fraction...
256 */
257 for (i = 0; i < level; i++)
258 {
259 current_width = MAX (1, current_width >> 1);
260 current_height = MAX (1, current_height >> 1);
261 current_depth = MAX (1, current_depth >> 1);
262 }
263
264 if (width)
265 *width = current_width;
266 if (height)
267 *height = current_height;
268 if (depth)
269 *depth = current_depth;
270 }
271
272 gboolean
cogl_texture_is_sliced(CoglTexture * texture)273 cogl_texture_is_sliced (CoglTexture *texture)
274 {
275 if (!texture->allocated)
276 cogl_texture_allocate (texture, NULL);
277 return texture->vtable->is_sliced (texture);
278 }
279
280 /* If this returns FALSE, that implies _foreach_sub_texture_in_region
281 * will be needed to iterate over multiple sub textures for regions whose
282 * texture coordinates extend out of the range [0,1]
283 */
284 gboolean
_cogl_texture_can_hardware_repeat(CoglTexture * texture)285 _cogl_texture_can_hardware_repeat (CoglTexture *texture)
286 {
287 if (!texture->allocated)
288 cogl_texture_allocate (texture, NULL);
289 return texture->vtable->can_hardware_repeat (texture);
290 }
291
292 /* NB: You can't use this with textures comprised of multiple sub textures (use
293 * cogl_texture_is_sliced() to check) since coordinate transformation for such
294 * textures will be different for each slice. */
295 void
_cogl_texture_transform_coords_to_gl(CoglTexture * texture,float * s,float * t)296 _cogl_texture_transform_coords_to_gl (CoglTexture *texture,
297 float *s,
298 float *t)
299 {
300 texture->vtable->transform_coords_to_gl (texture, s, t);
301 }
302
303 CoglTransformResult
_cogl_texture_transform_quad_coords_to_gl(CoglTexture * texture,float * coords)304 _cogl_texture_transform_quad_coords_to_gl (CoglTexture *texture,
305 float *coords)
306 {
307 return texture->vtable->transform_quad_coords_to_gl (texture, coords);
308 }
309
310 gboolean
cogl_texture_get_gl_texture(CoglTexture * texture,GLuint * out_gl_handle,GLenum * out_gl_target)311 cogl_texture_get_gl_texture (CoglTexture *texture,
312 GLuint *out_gl_handle,
313 GLenum *out_gl_target)
314 {
315 if (!texture->allocated)
316 cogl_texture_allocate (texture, NULL);
317
318 return texture->vtable->get_gl_texture (texture,
319 out_gl_handle, out_gl_target);
320 }
321
322 void
_cogl_texture_pre_paint(CoglTexture * texture,CoglTexturePrePaintFlags flags)323 _cogl_texture_pre_paint (CoglTexture *texture, CoglTexturePrePaintFlags flags)
324 {
325 /* Assert that the storage for the texture exists already if we're
326 * about to reference it for painting.
327 *
328 * Note: we abort on error here since it's a bit late to do anything
329 * about it if we fail to allocate the texture and the app could
330 * have explicitly allocated the texture earlier to handle problems
331 * gracefully.
332 *
333 * XXX: Maybe it could even be considered a programmer error if the
334 * texture hasn't been allocated by this point since it implies we
335 * are about to paint with undefined texture contents?
336 */
337 cogl_texture_allocate (texture, NULL);
338
339 texture->vtable->pre_paint (texture, flags);
340 }
341
342 void
_cogl_texture_ensure_non_quad_rendering(CoglTexture * texture)343 _cogl_texture_ensure_non_quad_rendering (CoglTexture *texture)
344 {
345 texture->vtable->ensure_non_quad_rendering (texture);
346 }
347
348 gboolean
_cogl_texture_set_region_from_bitmap(CoglTexture * texture,int src_x,int src_y,int width,int height,CoglBitmap * bmp,int dst_x,int dst_y,int level,GError ** error)349 _cogl_texture_set_region_from_bitmap (CoglTexture *texture,
350 int src_x,
351 int src_y,
352 int width,
353 int height,
354 CoglBitmap *bmp,
355 int dst_x,
356 int dst_y,
357 int level,
358 GError **error)
359 {
360 g_return_val_if_fail (cogl_bitmap_get_width (bmp) - src_x >= width, FALSE);
361 g_return_val_if_fail (cogl_bitmap_get_height (bmp) - src_y >= height, FALSE);
362 g_return_val_if_fail (width > 0, FALSE);
363 g_return_val_if_fail (height > 0, FALSE);
364
365 /* Assert that the storage for this texture has been allocated */
366 if (!cogl_texture_allocate (texture, error))
367 return FALSE;
368
369 /* Note that we don't prepare the bitmap for upload here because
370 some backends may be internally using a different format for the
371 actual GL texture than that reported by
372 _cogl_texture_get_format. For example the atlas textures are
373 always stored in an RGBA texture even if the texture format is
374 advertised as RGB. */
375
376 return texture->vtable->set_region (texture,
377 src_x, src_y,
378 dst_x, dst_y,
379 width, height,
380 level,
381 bmp,
382 error);
383 }
384
385 gboolean
cogl_texture_set_region_from_bitmap(CoglTexture * texture,int src_x,int src_y,int dst_x,int dst_y,unsigned int dst_width,unsigned int dst_height,CoglBitmap * bitmap)386 cogl_texture_set_region_from_bitmap (CoglTexture *texture,
387 int src_x,
388 int src_y,
389 int dst_x,
390 int dst_y,
391 unsigned int dst_width,
392 unsigned int dst_height,
393 CoglBitmap *bitmap)
394 {
395 GError *ignore_error = NULL;
396 gboolean status =
397 _cogl_texture_set_region_from_bitmap (texture,
398 src_x, src_y,
399 dst_width, dst_height,
400 bitmap,
401 dst_x, dst_y,
402 0, /* level */
403 &ignore_error);
404
405 g_clear_error (&ignore_error);
406 return status;
407 }
408
409 gboolean
_cogl_texture_set_region(CoglTexture * texture,int width,int height,CoglPixelFormat format,int rowstride,const uint8_t * data,int dst_x,int dst_y,int level,GError ** error)410 _cogl_texture_set_region (CoglTexture *texture,
411 int width,
412 int height,
413 CoglPixelFormat format,
414 int rowstride,
415 const uint8_t *data,
416 int dst_x,
417 int dst_y,
418 int level,
419 GError **error)
420 {
421 CoglContext *ctx = texture->context;
422 CoglBitmap *source_bmp;
423 gboolean ret;
424
425 g_return_val_if_fail (format != COGL_PIXEL_FORMAT_ANY, FALSE);
426 g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, FALSE);
427
428 /* Rowstride from width if none specified */
429 if (rowstride == 0)
430 rowstride = cogl_pixel_format_get_bytes_per_pixel (format, 0) * width;
431
432 /* Init source bitmap */
433 source_bmp = cogl_bitmap_new_for_data (ctx,
434 width, height,
435 format,
436 rowstride,
437 (uint8_t *) data);
438
439 ret = _cogl_texture_set_region_from_bitmap (texture,
440 0, 0,
441 width, height,
442 source_bmp,
443 dst_x, dst_y,
444 level,
445 error);
446
447 cogl_object_unref (source_bmp);
448
449 return ret;
450 }
451
452 gboolean
cogl_texture_set_region(CoglTexture * texture,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 uint8_t * data)453 cogl_texture_set_region (CoglTexture *texture,
454 int src_x,
455 int src_y,
456 int dst_x,
457 int dst_y,
458 unsigned int dst_width,
459 unsigned int dst_height,
460 int width,
461 int height,
462 CoglPixelFormat format,
463 unsigned int rowstride,
464 const uint8_t *data)
465 {
466 GError *ignore_error = NULL;
467 const uint8_t *first_pixel;
468 int bytes_per_pixel;
469 gboolean status;
470
471 g_return_val_if_fail (format != COGL_PIXEL_FORMAT_ANY, FALSE);
472 g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, FALSE);
473
474 /* Rowstride from width if none specified */
475 bytes_per_pixel = cogl_pixel_format_get_bytes_per_pixel (format, 0);
476 if (rowstride == 0)
477 rowstride = bytes_per_pixel * width;
478
479 first_pixel = data + rowstride * src_y + bytes_per_pixel * src_x;
480
481 status = _cogl_texture_set_region (texture,
482 dst_width,
483 dst_height,
484 format,
485 rowstride,
486 first_pixel,
487 dst_x,
488 dst_y,
489 0,
490 &ignore_error);
491 g_clear_error (&ignore_error);
492 return status;
493 }
494
495 gboolean
cogl_texture_set_data(CoglTexture * texture,CoglPixelFormat format,int rowstride,const uint8_t * data,int level,GError ** error)496 cogl_texture_set_data (CoglTexture *texture,
497 CoglPixelFormat format,
498 int rowstride,
499 const uint8_t *data,
500 int level,
501 GError **error)
502 {
503 int level_width;
504 int level_height;
505
506 _cogl_texture_get_level_size (texture,
507 level,
508 &level_width,
509 &level_height,
510 NULL);
511
512 return _cogl_texture_set_region (texture,
513 level_width,
514 level_height,
515 format,
516 rowstride,
517 data,
518 0, 0, /* dest x, y */
519 level,
520 error);
521 }
522
523 static gboolean
get_texture_bits_via_offscreen(CoglTexture * meta_texture,CoglTexture * sub_texture,int x,int y,int width,int height,uint8_t * dst_bits,unsigned int dst_rowstride,CoglPixelFormat closest_format)524 get_texture_bits_via_offscreen (CoglTexture *meta_texture,
525 CoglTexture *sub_texture,
526 int x,
527 int y,
528 int width,
529 int height,
530 uint8_t *dst_bits,
531 unsigned int dst_rowstride,
532 CoglPixelFormat closest_format)
533 {
534 CoglContext *ctx = sub_texture->context;
535 CoglOffscreen *offscreen;
536 CoglFramebuffer *framebuffer;
537 CoglBitmap *bitmap;
538 gboolean ret;
539 GError *ignore_error = NULL;
540 CoglPixelFormat real_format;
541
542 offscreen = _cogl_offscreen_new_with_texture_full
543 (sub_texture,
544 COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL,
545 0);
546
547 framebuffer = COGL_FRAMEBUFFER (offscreen);
548 if (!cogl_framebuffer_allocate (framebuffer, &ignore_error))
549 {
550 g_error_free (ignore_error);
551 return FALSE;
552 }
553
554 /* Currently the framebuffer's internal format corresponds to the
555 * internal format of @sub_texture but in the case of atlas textures
556 * it's possible that this format doesn't reflect the correct
557 * premultiplied alpha status or what components are valid since
558 * atlas textures are always stored in a shared texture with a
559 * format of _RGBA_8888.
560 *
561 * Here we override the internal format to make sure the
562 * framebuffer's internal format matches the internal format of the
563 * parent meta_texture instead.
564 */
565 real_format = _cogl_texture_get_format (meta_texture);
566 _cogl_framebuffer_set_internal_format (framebuffer, real_format);
567
568 bitmap = cogl_bitmap_new_for_data (ctx,
569 width, height,
570 closest_format,
571 dst_rowstride,
572 dst_bits);
573 ret = _cogl_framebuffer_read_pixels_into_bitmap (framebuffer,
574 x, y,
575 COGL_READ_PIXELS_COLOR_BUFFER,
576 bitmap,
577 &ignore_error);
578
579 g_clear_error (&ignore_error);
580
581 cogl_object_unref (bitmap);
582
583 g_object_unref (framebuffer);
584
585 return ret;
586 }
587
588 static gboolean
get_texture_bits_via_copy(CoglTexture * texture,int x,int y,int width,int height,uint8_t * dst_bits,unsigned int dst_rowstride,CoglPixelFormat dst_format)589 get_texture_bits_via_copy (CoglTexture *texture,
590 int x,
591 int y,
592 int width,
593 int height,
594 uint8_t *dst_bits,
595 unsigned int dst_rowstride,
596 CoglPixelFormat dst_format)
597 {
598 unsigned int full_rowstride;
599 uint8_t *full_bits;
600 gboolean ret = TRUE;
601 int bpp;
602 int full_tex_width, full_tex_height;
603
604 g_return_val_if_fail (dst_format != COGL_PIXEL_FORMAT_ANY, FALSE);
605 g_return_val_if_fail (cogl_pixel_format_get_n_planes (dst_format) == 1, FALSE);
606
607 full_tex_width = cogl_texture_get_width (texture);
608 full_tex_height = cogl_texture_get_height (texture);
609
610 bpp = cogl_pixel_format_get_bytes_per_pixel (dst_format, 0);
611
612 full_rowstride = bpp * full_tex_width;
613 full_bits = g_malloc (full_rowstride * full_tex_height);
614
615 if (texture->vtable->get_data (texture,
616 dst_format,
617 full_rowstride,
618 full_bits))
619 {
620 uint8_t *dst = dst_bits;
621 uint8_t *src = full_bits + x * bpp + y * full_rowstride;
622 int i;
623
624 for (i = 0; i < height; i++)
625 {
626 memcpy (dst, src, bpp * width);
627 dst += dst_rowstride;
628 src += full_rowstride;
629 }
630 }
631 else
632 ret = FALSE;
633
634 g_free (full_bits);
635
636 return ret;
637 }
638
639 typedef struct
640 {
641 CoglTexture *meta_texture;
642 int orig_width;
643 int orig_height;
644 CoglBitmap *target_bmp;
645 uint8_t *target_bits;
646 gboolean success;
647 GError *error;
648 } CoglTextureGetData;
649
650 static void
texture_get_cb(CoglTexture * subtexture,const float * subtexture_coords,const float * virtual_coords,void * user_data)651 texture_get_cb (CoglTexture *subtexture,
652 const float *subtexture_coords,
653 const float *virtual_coords,
654 void *user_data)
655 {
656 CoglTextureGetData *tg_data = user_data;
657 CoglTexture *meta_texture = tg_data->meta_texture;
658 CoglPixelFormat closest_format = cogl_bitmap_get_format (tg_data->target_bmp);
659 /* We already asserted that we have a single plane format */
660 int bpp = cogl_pixel_format_get_bytes_per_pixel (closest_format, 0);
661 unsigned int rowstride = cogl_bitmap_get_rowstride (tg_data->target_bmp);
662 int subtexture_width = cogl_texture_get_width (subtexture);
663 int subtexture_height = cogl_texture_get_height (subtexture);
664
665 int x_in_subtexture = (int) (0.5 + subtexture_width * subtexture_coords[0]);
666 int y_in_subtexture = (int) (0.5 + subtexture_height * subtexture_coords[1]);
667 int width = ((int) (0.5 + subtexture_width * subtexture_coords[2])
668 - x_in_subtexture);
669 int height = ((int) (0.5 + subtexture_height * subtexture_coords[3])
670 - y_in_subtexture);
671 int x_in_bitmap = (int) (0.5 + tg_data->orig_width * virtual_coords[0]);
672 int y_in_bitmap = (int) (0.5 + tg_data->orig_height * virtual_coords[1]);
673
674 uint8_t *dst_bits;
675
676 if (!tg_data->success)
677 return;
678
679 dst_bits = tg_data->target_bits + x_in_bitmap * bpp + y_in_bitmap * rowstride;
680
681 /* If we can read everything as a single slice, then go ahead and do that
682 * to avoid allocating an FBO. We'll leave it up to the GL implementation to
683 * do glGetTexImage as efficiently as possible. (GLES doesn't have that,
684 * so we'll fall through)
685 */
686 if (x_in_subtexture == 0 && y_in_subtexture == 0 &&
687 width == subtexture_width && height == subtexture_height)
688 {
689 if (subtexture->vtable->get_data (subtexture,
690 closest_format,
691 rowstride,
692 dst_bits))
693 return;
694 }
695
696 /* Next best option is a FBO and glReadPixels */
697 if (get_texture_bits_via_offscreen (meta_texture,
698 subtexture,
699 x_in_subtexture, y_in_subtexture,
700 width, height,
701 dst_bits,
702 rowstride,
703 closest_format))
704 return;
705
706 /* Getting ugly: read the entire texture, copy out the part we want */
707 if (get_texture_bits_via_copy (subtexture,
708 x_in_subtexture, y_in_subtexture,
709 width, height,
710 dst_bits,
711 rowstride,
712 closest_format))
713 return;
714
715 /* No luck, the caller will fall back to the draw-to-backbuffer and
716 * read implementation */
717 tg_data->success = FALSE;
718 }
719
720 int
cogl_texture_get_data(CoglTexture * texture,CoglPixelFormat format,unsigned int rowstride,uint8_t * data)721 cogl_texture_get_data (CoglTexture *texture,
722 CoglPixelFormat format,
723 unsigned int rowstride,
724 uint8_t *data)
725 {
726 CoglContext *ctx = texture->context;
727 int bpp;
728 int byte_size;
729 CoglPixelFormat closest_format;
730 GLenum closest_gl_format;
731 GLenum closest_gl_type;
732 CoglBitmap *target_bmp;
733 int tex_width;
734 int tex_height;
735 CoglPixelFormat texture_format;
736 GError *ignore_error = NULL;
737
738 CoglTextureGetData tg_data;
739
740 texture_format = _cogl_texture_get_format (texture);
741
742 /* Default to internal format if none specified */
743 if (format == COGL_PIXEL_FORMAT_ANY)
744 format = texture_format;
745
746 /* We only support single plane formats */
747 g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, 0);
748
749 tex_width = cogl_texture_get_width (texture);
750 tex_height = cogl_texture_get_height (texture);
751
752 /* Rowstride from texture width if none specified */
753 bpp = cogl_pixel_format_get_bytes_per_pixel (format, 0);
754 if (rowstride == 0)
755 rowstride = tex_width * bpp;
756
757 /* Return byte size if only that requested */
758 byte_size = tex_height * rowstride;
759 if (data == NULL)
760 return byte_size;
761
762 closest_format =
763 ctx->texture_driver->find_best_gl_get_data_format (ctx,
764 format,
765 &closest_gl_format,
766 &closest_gl_type);
767
768 /* We can assume that whatever data GL gives us will have the
769 premult status of the original texture */
770 if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (closest_format))
771 closest_format = ((closest_format & ~COGL_PREMULT_BIT) |
772 (texture_format & COGL_PREMULT_BIT));
773
774 /* If the application is requesting a conversion from a
775 * component-alpha texture and the driver doesn't support them
776 * natively then we can only read into an alpha-format buffer. In
777 * this case the driver will be faking the alpha textures with a
778 * red-component texture and it won't swizzle to the correct format
779 * while reading */
780 if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES))
781 {
782 if (texture_format == COGL_PIXEL_FORMAT_A_8)
783 {
784 closest_format = COGL_PIXEL_FORMAT_A_8;
785 }
786 else if (format == COGL_PIXEL_FORMAT_A_8)
787 {
788 /* If we are converting to a component-alpha texture then we
789 * need to read all of the components to a temporary buffer
790 * because there is no way to get just the 4th component.
791 * Note: it doesn't matter whether the texture is
792 * pre-multiplied here because we're only going to look at
793 * the alpha component */
794 closest_format = COGL_PIXEL_FORMAT_RGBA_8888;
795 }
796 }
797
798 /* Is the requested format supported? */
799 if (closest_format == format)
800 /* Target user data directly */
801 target_bmp = cogl_bitmap_new_for_data (ctx,
802 tex_width,
803 tex_height,
804 format,
805 rowstride,
806 data);
807 else
808 {
809 target_bmp = _cogl_bitmap_new_with_malloc_buffer (ctx,
810 tex_width, tex_height,
811 closest_format,
812 &ignore_error);
813 if (!target_bmp)
814 {
815 g_error_free (ignore_error);
816 return 0;
817 }
818 }
819
820 tg_data.target_bits = _cogl_bitmap_map (target_bmp, COGL_BUFFER_ACCESS_WRITE,
821 COGL_BUFFER_MAP_HINT_DISCARD,
822 &ignore_error);
823 if (tg_data.target_bits)
824 {
825 tg_data.meta_texture = texture;
826 tg_data.orig_width = tex_width;
827 tg_data.orig_height = tex_height;
828 tg_data.target_bmp = target_bmp;
829 tg_data.error = NULL;
830 tg_data.success = TRUE;
831
832 /* If there are any dependent framebuffers on the texture then we
833 need to flush their journals so the texture contents will be
834 up-to-date */
835 _cogl_texture_flush_journal_rendering (texture);
836
837 /* Iterating through the subtextures allows piecing together
838 * the data for a sliced texture, and allows us to do the
839 * read-from-framebuffer logic here in a simple fashion rather than
840 * passing offsets down through the code. */
841 cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (texture),
842 0, 0, 1, 1,
843 COGL_PIPELINE_WRAP_MODE_REPEAT,
844 COGL_PIPELINE_WRAP_MODE_REPEAT,
845 texture_get_cb,
846 &tg_data);
847
848 _cogl_bitmap_unmap (target_bmp);
849 }
850 else
851 {
852 g_error_free (ignore_error);
853 tg_data.success = FALSE;
854 }
855
856 /* XXX: In some cases this api may fail to read back the texture
857 * data; such as for GLES which doesn't support glGetTexImage
858 */
859 if (!tg_data.success)
860 {
861 cogl_object_unref (target_bmp);
862 return 0;
863 }
864
865 /* Was intermediate used? */
866 if (closest_format != format)
867 {
868 CoglBitmap *new_bmp;
869 gboolean result;
870 GError *error = NULL;
871
872 /* Convert to requested format directly into the user's buffer */
873 new_bmp = cogl_bitmap_new_for_data (ctx,
874 tex_width, tex_height,
875 format,
876 rowstride,
877 data);
878 result = _cogl_bitmap_convert_into_bitmap (target_bmp, new_bmp, &error);
879
880 if (!result)
881 {
882 g_error_free (error);
883 /* Return failure after cleaning up */
884 byte_size = 0;
885 }
886
887 cogl_object_unref (new_bmp);
888 }
889
890 cogl_object_unref (target_bmp);
891
892 return byte_size;
893 }
894
895 static void
on_framebuffer_destroy(CoglFramebuffer * framebuffer,CoglTexture * texture)896 on_framebuffer_destroy (CoglFramebuffer *framebuffer,
897 CoglTexture *texture)
898 {
899 texture->framebuffers = g_list_remove (texture->framebuffers, framebuffer);
900 }
901
902 void
_cogl_texture_associate_framebuffer(CoglTexture * texture,CoglFramebuffer * framebuffer)903 _cogl_texture_associate_framebuffer (CoglTexture *texture,
904 CoglFramebuffer *framebuffer)
905 {
906 /* Note: we don't take a reference on the framebuffer here because
907 * that would introduce a circular reference. */
908 texture->framebuffers = g_list_prepend (texture->framebuffers, framebuffer);
909
910 g_signal_connect (framebuffer, "destroy",
911 G_CALLBACK (on_framebuffer_destroy),
912 texture);
913 }
914
915 const GList *
_cogl_texture_get_associated_framebuffers(CoglTexture * texture)916 _cogl_texture_get_associated_framebuffers (CoglTexture *texture)
917 {
918 return texture->framebuffers;
919 }
920
921 void
_cogl_texture_flush_journal_rendering(CoglTexture * texture)922 _cogl_texture_flush_journal_rendering (CoglTexture *texture)
923 {
924 GList *l;
925
926 /* It could be that a referenced texture is part of a framebuffer
927 * which has an associated journal that must be flushed before it
928 * can be sampled from by the current primitive... */
929 for (l = texture->framebuffers; l; l = l->next)
930 _cogl_framebuffer_flush_journal (l->data);
931 }
932
933 /* This function lets you define a meta texture as a grid of textures
934 * whereby the x and y grid-lines are defined by an array of
935 * CoglSpans. With that grid based description this function can then
936 * iterate all the cells of the grid that lye within a region
937 * specified as virtual, meta-texture, coordinates. This function can
938 * also cope with regions that extend beyond the original meta-texture
939 * grid by iterating cells repeatedly according to the wrap_x/y
940 * arguments.
941 *
942 * To differentiate between texture coordinates of a specific, real,
943 * slice texture and the texture coordinates of a composite, meta
944 * texture, the coordinates of the meta texture are called "virtual"
945 * coordinates and the coordinates of spans are called "slice"
946 * coordinates.
947 *
948 * Note: no guarantee is given about the order in which the slices
949 * will be visited.
950 *
951 * Note: The slice coordinates passed to @callback are always
952 * normalized coordinates even if the span coordinates aren't
953 * normalized.
954 */
955 void
_cogl_texture_spans_foreach_in_region(CoglSpan * x_spans,int n_x_spans,CoglSpan * y_spans,int n_y_spans,CoglTexture ** textures,float * virtual_coords,float x_normalize_factor,float y_normalize_factor,CoglPipelineWrapMode wrap_x,CoglPipelineWrapMode wrap_y,CoglMetaTextureCallback callback,void * user_data)956 _cogl_texture_spans_foreach_in_region (CoglSpan *x_spans,
957 int n_x_spans,
958 CoglSpan *y_spans,
959 int n_y_spans,
960 CoglTexture **textures,
961 float *virtual_coords,
962 float x_normalize_factor,
963 float y_normalize_factor,
964 CoglPipelineWrapMode wrap_x,
965 CoglPipelineWrapMode wrap_y,
966 CoglMetaTextureCallback callback,
967 void *user_data)
968 {
969 CoglSpanIter iter_x;
970 CoglSpanIter iter_y;
971 float slice_coords[4];
972 float span_virtual_coords[4];
973
974 /* Iterate the y axis of the virtual rectangle */
975 for (_cogl_span_iter_begin (&iter_y,
976 y_spans,
977 n_y_spans,
978 y_normalize_factor,
979 virtual_coords[1],
980 virtual_coords[3],
981 wrap_y);
982 !_cogl_span_iter_end (&iter_y);
983 _cogl_span_iter_next (&iter_y))
984 {
985 if (iter_y.flipped)
986 {
987 slice_coords[1] = iter_y.intersect_end;
988 slice_coords[3] = iter_y.intersect_start;
989 span_virtual_coords[1] = iter_y.intersect_end;
990 span_virtual_coords[3] = iter_y.intersect_start;
991 }
992 else
993 {
994 slice_coords[1] = iter_y.intersect_start;
995 slice_coords[3] = iter_y.intersect_end;
996 span_virtual_coords[1] = iter_y.intersect_start;
997 span_virtual_coords[3] = iter_y.intersect_end;
998 }
999
1000 /* Map the current intersection to normalized slice coordinates */
1001 slice_coords[1] = (slice_coords[1] - iter_y.pos) / iter_y.span->size;
1002 slice_coords[3] = (slice_coords[3] - iter_y.pos) / iter_y.span->size;
1003
1004 /* Iterate the x axis of the virtual rectangle */
1005 for (_cogl_span_iter_begin (&iter_x,
1006 x_spans,
1007 n_x_spans,
1008 x_normalize_factor,
1009 virtual_coords[0],
1010 virtual_coords[2],
1011 wrap_x);
1012 !_cogl_span_iter_end (&iter_x);
1013 _cogl_span_iter_next (&iter_x))
1014 {
1015 CoglTexture *span_tex;
1016
1017 if (iter_x.flipped)
1018 {
1019 slice_coords[0] = iter_x.intersect_end;
1020 slice_coords[2] = iter_x.intersect_start;
1021 span_virtual_coords[0] = iter_x.intersect_end;
1022 span_virtual_coords[2] = iter_x.intersect_start;
1023 }
1024 else
1025 {
1026 slice_coords[0] = iter_x.intersect_start;
1027 slice_coords[2] = iter_x.intersect_end;
1028 span_virtual_coords[0] = iter_x.intersect_start;
1029 span_virtual_coords[2] = iter_x.intersect_end;
1030 }
1031
1032 /* Map the current intersection to normalized slice coordinates */
1033 slice_coords[0] = (slice_coords[0] - iter_x.pos) / iter_x.span->size;
1034 slice_coords[2] = (slice_coords[2] - iter_x.pos) / iter_x.span->size;
1035
1036 /* Pluck out the cogl texture for this span */
1037 span_tex = textures[iter_y.index * n_x_spans + iter_x.index];
1038
1039 callback (COGL_TEXTURE (span_tex),
1040 slice_coords,
1041 span_virtual_coords,
1042 user_data);
1043 }
1044 }
1045 }
1046
1047 void
_cogl_texture_set_allocated(CoglTexture * texture,CoglPixelFormat internal_format,int width,int height)1048 _cogl_texture_set_allocated (CoglTexture *texture,
1049 CoglPixelFormat internal_format,
1050 int width,
1051 int height)
1052 {
1053 _cogl_texture_set_internal_format (texture, internal_format);
1054
1055 texture->width = width;
1056 texture->height = height;
1057 texture->allocated = TRUE;
1058
1059 _cogl_texture_free_loader (texture);
1060 }
1061
1062 gboolean
cogl_texture_allocate(CoglTexture * texture,GError ** error)1063 cogl_texture_allocate (CoglTexture *texture,
1064 GError **error)
1065 {
1066 if (texture->allocated)
1067 return TRUE;
1068
1069 if (texture->components == COGL_TEXTURE_COMPONENTS_RG &&
1070 !cogl_has_feature (texture->context, COGL_FEATURE_ID_TEXTURE_RG))
1071 g_set_error (error,
1072 COGL_TEXTURE_ERROR,
1073 COGL_TEXTURE_ERROR_FORMAT,
1074 "A red-green texture was requested but the driver "
1075 "does not support them");
1076
1077 texture->allocated = texture->vtable->allocate (texture, error);
1078
1079 return texture->allocated;
1080 }
1081
1082 void
_cogl_texture_set_internal_format(CoglTexture * texture,CoglPixelFormat internal_format)1083 _cogl_texture_set_internal_format (CoglTexture *texture,
1084 CoglPixelFormat internal_format)
1085 {
1086 texture->premultiplied = FALSE;
1087
1088 if (internal_format == COGL_PIXEL_FORMAT_ANY)
1089 internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
1090
1091 if (internal_format == COGL_PIXEL_FORMAT_A_8)
1092 {
1093 texture->components = COGL_TEXTURE_COMPONENTS_A;
1094 return;
1095 }
1096 else if (internal_format == COGL_PIXEL_FORMAT_RG_88)
1097 {
1098 texture->components = COGL_TEXTURE_COMPONENTS_RG;
1099 return;
1100 }
1101 else if (internal_format & COGL_DEPTH_BIT)
1102 {
1103 texture->components = COGL_TEXTURE_COMPONENTS_DEPTH;
1104 return;
1105 }
1106 else if (internal_format & COGL_A_BIT)
1107 {
1108 texture->components = COGL_TEXTURE_COMPONENTS_RGBA;
1109 if (internal_format & COGL_PREMULT_BIT)
1110 texture->premultiplied = TRUE;
1111 return;
1112 }
1113 else
1114 texture->components = COGL_TEXTURE_COMPONENTS_RGB;
1115 }
1116
1117 CoglPixelFormat
_cogl_texture_determine_internal_format(CoglTexture * texture,CoglPixelFormat src_format)1118 _cogl_texture_determine_internal_format (CoglTexture *texture,
1119 CoglPixelFormat src_format)
1120 {
1121 switch (texture->components)
1122 {
1123 case COGL_TEXTURE_COMPONENTS_DEPTH:
1124 if (src_format & COGL_DEPTH_BIT)
1125 return src_format;
1126 else
1127 {
1128 CoglContext *ctx = texture->context;
1129
1130 if (_cogl_has_private_feature (ctx,
1131 COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) ||
1132 _cogl_has_private_feature (ctx,
1133 COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL))
1134 {
1135 return COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8;
1136 }
1137 else
1138 return COGL_PIXEL_FORMAT_DEPTH_16;
1139 }
1140 case COGL_TEXTURE_COMPONENTS_A:
1141 return COGL_PIXEL_FORMAT_A_8;
1142 case COGL_TEXTURE_COMPONENTS_RG:
1143 return COGL_PIXEL_FORMAT_RG_88;
1144 case COGL_TEXTURE_COMPONENTS_RGB:
1145 if (src_format != COGL_PIXEL_FORMAT_ANY &&
1146 !(src_format & COGL_A_BIT) && !(src_format & COGL_DEPTH_BIT))
1147 return src_format;
1148 else
1149 return COGL_PIXEL_FORMAT_RGB_888;
1150 case COGL_TEXTURE_COMPONENTS_RGBA:
1151 {
1152 CoglPixelFormat format;
1153
1154 if (src_format != COGL_PIXEL_FORMAT_ANY &&
1155 (src_format & COGL_A_BIT) && src_format != COGL_PIXEL_FORMAT_A_8)
1156 format = src_format;
1157 else
1158 format = COGL_PIXEL_FORMAT_RGBA_8888;
1159
1160 if (texture->premultiplied)
1161 {
1162 if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (format))
1163 return format |= COGL_PREMULT_BIT;
1164 else
1165 return COGL_PIXEL_FORMAT_RGBA_8888_PRE;
1166 }
1167 else
1168 return format & ~COGL_PREMULT_BIT;
1169 }
1170 }
1171
1172 g_return_val_if_reached (COGL_PIXEL_FORMAT_RGBA_8888_PRE);
1173 }
1174
1175 void
cogl_texture_set_components(CoglTexture * texture,CoglTextureComponents components)1176 cogl_texture_set_components (CoglTexture *texture,
1177 CoglTextureComponents components)
1178 {
1179 g_return_if_fail (!texture->allocated);
1180
1181 if (texture->components == components)
1182 return;
1183
1184 texture->components = components;
1185 }
1186
1187 CoglTextureComponents
cogl_texture_get_components(CoglTexture * texture)1188 cogl_texture_get_components (CoglTexture *texture)
1189 {
1190 return texture->components;
1191 }
1192
1193 void
cogl_texture_set_premultiplied(CoglTexture * texture,gboolean premultiplied)1194 cogl_texture_set_premultiplied (CoglTexture *texture,
1195 gboolean premultiplied)
1196 {
1197 g_return_if_fail (!texture->allocated);
1198
1199 premultiplied = !!premultiplied;
1200
1201 if (texture->premultiplied == premultiplied)
1202 return;
1203
1204 texture->premultiplied = premultiplied;
1205 }
1206
1207 gboolean
cogl_texture_get_premultiplied(CoglTexture * texture)1208 cogl_texture_get_premultiplied (CoglTexture *texture)
1209 {
1210 return texture->premultiplied;
1211 }
1212
1213 void
_cogl_texture_copy_internal_format(CoglTexture * src,CoglTexture * dest)1214 _cogl_texture_copy_internal_format (CoglTexture *src,
1215 CoglTexture *dest)
1216 {
1217 cogl_texture_set_components (dest, src->components);
1218 cogl_texture_set_premultiplied (dest, src->premultiplied);
1219 }
1220