CoglBool _cogl_bitmap_copy_subregion (CoglBitmap *src, CoglBitmap *dst, int src_x, int src_y, int dst_x, int dst_y, int width, int height, CoglError **error) { uint8_t *srcdata; uint8_t *dstdata; int bpp; int line; CoglBool succeeded = FALSE; /* Intended only for fast copies when format is equal! */ _COGL_RETURN_VAL_IF_FAIL ((src->format & ~COGL_PREMULT_BIT) == (dst->format & ~COGL_PREMULT_BIT), FALSE); bpp = _cogl_pixel_format_get_bytes_per_pixel (src->format); if ((srcdata = _cogl_bitmap_map (src, COGL_BUFFER_ACCESS_READ, 0, error))) { if ((dstdata = _cogl_bitmap_map (dst, COGL_BUFFER_ACCESS_WRITE, 0, error))) { srcdata += src_y * src->rowstride + src_x * bpp; dstdata += dst_y * dst->rowstride + dst_x * bpp; for (line = 0; line < height; ++line) { memcpy (dstdata, srcdata, width * bpp); srcdata += src->rowstride; dstdata += dst->rowstride; } succeeded = TRUE; _cogl_bitmap_unmap (dst); } _cogl_bitmap_unmap (src); } return succeeded; }
void _cogl_bitmap_unmap (CoglBitmap *bitmap) { /* Divert to another bitmap if this data is shared */ if (bitmap->shared_bmp) { _cogl_bitmap_unmap (bitmap->shared_bmp); return; } g_assert (bitmap->mapped); bitmap->mapped = FALSE; if (bitmap->buffer) cogl_buffer_unmap (bitmap->buffer); }
void _cogl_bitmap_gl_unbind (CoglBitmap *bitmap) { /* Divert to another bitmap if this data is shared */ if (bitmap->shared_bmp) { _cogl_bitmap_gl_unbind (bitmap->shared_bmp); return; } g_assert (bitmap->bound); bitmap->bound = FALSE; /* If the bitmap wasn't created from a pixel array then the implementation of unbind is the same as unmap */ if (bitmap->buffer) _cogl_buffer_gl_unbind (bitmap->buffer); else _cogl_bitmap_unmap (bitmap); }
CoglTexture3D * cogl_texture_3d_new_from_data (CoglContext *context, int width, int height, int depth, CoglPixelFormat format, int rowstride, int image_stride, const uint8_t *data, CoglError **error) { CoglBitmap *bitmap; CoglTexture3D *ret; _COGL_RETURN_VAL_IF_FAIL (data, NULL); _COGL_RETURN_VAL_IF_FAIL (format != COGL_PIXEL_FORMAT_ANY, NULL); /* Rowstride from width if not given */ if (rowstride == 0) rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format); /* Image stride from height and rowstride if not given */ if (image_stride == 0) image_stride = height * rowstride; if (image_stride < rowstride * height) return NULL; /* GL doesn't support uploading when the image_stride isn't a multiple of the rowstride. If this happens we'll just pack the image into a new bitmap. The documentation for this function recommends avoiding this situation. */ if (image_stride % rowstride != 0) { uint8_t *bmp_data; int bmp_rowstride; int z, y; bitmap = _cogl_bitmap_new_with_malloc_buffer (context, width, depth * height, format, error); if (!bitmap) return NULL; bmp_data = _cogl_bitmap_map (bitmap, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD, error); if (bmp_data == NULL) { cogl_object_unref (bitmap); return NULL; } bmp_rowstride = cogl_bitmap_get_rowstride (bitmap); /* Copy all of the images in */ for (z = 0; z < depth; z++) for (y = 0; y < height; y++) memcpy (bmp_data + (z * bmp_rowstride * height + bmp_rowstride * y), data + z * image_stride + rowstride * y, bmp_rowstride); _cogl_bitmap_unmap (bitmap); } else bitmap = cogl_bitmap_new_for_data (context, width, image_stride / rowstride * depth, format, rowstride, (uint8_t *) data); ret = cogl_texture_3d_new_from_bitmap (bitmap, height, depth); cogl_object_unref (bitmap); if (ret && !cogl_texture_allocate (COGL_TEXTURE (ret), error)) { cogl_object_unref (ret); return NULL; } return ret; }
static CoglBool allocate_from_bitmap (CoglTexture3D *tex_3d, CoglTextureLoader *loader, CoglError **error) { CoglTexture *tex = COGL_TEXTURE (tex_3d); CoglContext *ctx = tex->context; CoglPixelFormat internal_format; CoglBitmap *bmp = loader->src.bitmap.bitmap; int bmp_width = cogl_bitmap_get_width (bmp); int height = loader->src.bitmap.height; int depth = loader->src.bitmap.depth; CoglPixelFormat bmp_format = cogl_bitmap_get_format (bmp); CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place; CoglBitmap *upload_bmp; CoglPixelFormat upload_format; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; internal_format = _cogl_texture_determine_internal_format (tex, bmp_format); if (!_cogl_texture_3d_can_create (ctx, bmp_width, height, depth, internal_format, error)) return FALSE; upload_bmp = _cogl_bitmap_convert_for_upload (bmp, internal_format, can_convert_in_place, error); if (upload_bmp == NULL) return FALSE; upload_format = cogl_bitmap_get_format (upload_bmp); ctx->driver_vtable->pixel_format_to_gl (ctx, upload_format, NULL, /* internal format */ &gl_format, &gl_type); ctx->driver_vtable->pixel_format_to_gl (ctx, internal_format, &gl_intformat, NULL, NULL); /* Keep a copy of the first pixel so that if glGenerateMipmap isn't supported we can fallback to using GL_GENERATE_MIPMAP */ if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) { CoglError *ignore = NULL; uint8_t *data = _cogl_bitmap_map (upload_bmp, COGL_BUFFER_ACCESS_READ, 0, &ignore); tex_3d->first_pixel.gl_format = gl_format; tex_3d->first_pixel.gl_type = gl_type; if (data) { memcpy (tex_3d->first_pixel.data, data, _cogl_pixel_format_get_bytes_per_pixel (upload_format)); _cogl_bitmap_unmap (upload_bmp); } else { g_warning ("Failed to read first pixel of bitmap for " "glGenerateMipmap fallback"); cogl_error_free (ignore); memset (tex_3d->first_pixel.data, 0, _cogl_pixel_format_get_bytes_per_pixel (upload_format)); } } tex_3d->gl_texture = ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, internal_format); if (!ctx->texture_driver->upload_to_gl_3d (ctx, GL_TEXTURE_3D, tex_3d->gl_texture, FALSE, /* is_foreign */ height, depth, upload_bmp, gl_intformat, gl_format, gl_type, error)) { cogl_object_unref (upload_bmp); return FALSE; } tex_3d->gl_format = gl_intformat; cogl_object_unref (upload_bmp); tex_3d->depth = loader->src.bitmap.depth; tex_3d->internal_format = internal_format; _cogl_texture_set_allocated (tex, internal_format, bmp_width, loader->src.bitmap.height); return TRUE; }
gboolean _cogl_texture_2d_gl_copy_from_bitmap (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglBitmap *bmp, int dst_x, int dst_y, int level, CoglError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2d); CoglContext *ctx = tex->context; CoglBitmap *upload_bmp; CoglPixelFormat upload_format; GLenum gl_format; GLenum gl_type; gboolean status = TRUE; upload_bmp = _cogl_bitmap_convert_for_upload (bmp, _cogl_texture_get_format (tex), FALSE, /* can't convert in place */ error); if (upload_bmp == NULL) return FALSE; upload_format = cogl_bitmap_get_format (upload_bmp); ctx->driver_vtable->pixel_format_to_gl (ctx, upload_format, NULL, /* internal gl format */ &gl_format, &gl_type); /* If this touches the first pixel then we'll update our copy */ if (dst_x == 0 && dst_y == 0 && !cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) { CoglError *ignore = NULL; uint8_t *data = _cogl_bitmap_map (upload_bmp, COGL_BUFFER_ACCESS_READ, 0, &ignore); CoglPixelFormat bpp = _cogl_pixel_format_get_bytes_per_pixel (upload_format); tex_2d->first_pixel.gl_format = gl_format; tex_2d->first_pixel.gl_type = gl_type; if (data) { memcpy (tex_2d->first_pixel.data, (data + cogl_bitmap_get_rowstride (upload_bmp) * src_y + bpp * src_x), bpp); _cogl_bitmap_unmap (bmp); } else { g_warning ("Failed to read first bitmap pixel for " "glGenerateMipmap fallback"); cogl_error_free (ignore); memset (tex_2d->first_pixel.data, 0, bpp); } } status = ctx->texture_driver->upload_subregion_to_gl (ctx, tex, FALSE, src_x, src_y, dst_x, dst_y, width, height, level, upload_bmp, gl_format, gl_type, error); cogl_object_unref (upload_bmp); _cogl_texture_gl_maybe_update_max_level (tex, level); return status; }
static gboolean allocate_from_bitmap (CoglTexture2D *tex_2d, CoglTextureLoader *loader, CoglError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2d); CoglBitmap *bmp = loader->src.bitmap.bitmap; CoglContext *ctx = _cogl_bitmap_get_context (bmp); CoglPixelFormat internal_format; int width = cogl_bitmap_get_width (bmp); int height = cogl_bitmap_get_height (bmp); gboolean can_convert_in_place = loader->src.bitmap.can_convert_in_place; CoglBitmap *upload_bmp; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; internal_format = _cogl_texture_determine_internal_format (tex, cogl_bitmap_get_format (bmp)); if (!_cogl_texture_2d_gl_can_create (ctx, width, height, internal_format)) { _cogl_set_error (error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_SIZE, "Failed to create texture 2d due to size/format" " constraints"); return FALSE; } upload_bmp = _cogl_bitmap_convert_for_upload (bmp, internal_format, can_convert_in_place, error); if (upload_bmp == NULL) return FALSE; ctx->driver_vtable->pixel_format_to_gl (ctx, cogl_bitmap_get_format (upload_bmp), NULL, /* internal format */ &gl_format, &gl_type); ctx->driver_vtable->pixel_format_to_gl (ctx, internal_format, &gl_intformat, NULL, NULL); /* Keep a copy of the first pixel so that if glGenerateMipmap isn't supported we can fallback to using GL_GENERATE_MIPMAP */ if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) { CoglError *ignore = NULL; uint8_t *data = _cogl_bitmap_map (upload_bmp, COGL_BUFFER_ACCESS_READ, 0, &ignore); CoglPixelFormat format = cogl_bitmap_get_format (upload_bmp); tex_2d->first_pixel.gl_format = gl_format; tex_2d->first_pixel.gl_type = gl_type; if (data) { memcpy (tex_2d->first_pixel.data, data, _cogl_pixel_format_get_bytes_per_pixel (format)); _cogl_bitmap_unmap (upload_bmp); } else { g_warning ("Failed to read first pixel of bitmap for " "glGenerateMipmap fallback"); cogl_error_free (ignore); memset (tex_2d->first_pixel.data, 0, _cogl_pixel_format_get_bytes_per_pixel (format)); } } tex_2d->gl_texture = ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format); if (!ctx->texture_driver->upload_to_gl (ctx, GL_TEXTURE_2D, tex_2d->gl_texture, FALSE, upload_bmp, gl_intformat, gl_format, gl_type, error)) { cogl_object_unref (upload_bmp); return FALSE; } tex_2d->gl_internal_format = gl_intformat; cogl_object_unref (upload_bmp); tex_2d->internal_format = internal_format; _cogl_texture_set_allocated (tex, internal_format, width, height); return TRUE; }
/* the error does not contain the filename as the caller already has it */ CoglBitmap * _cogl_bitmap_from_file (const char *filename, GError **error) { CFURLRef url; CGImageSourceRef image_source; CGImageRef image; int save_errno; CFStringRef type; size_t width, height, rowstride; uint8_t *out_data; CGColorSpaceRef color_space; CGContextRef bitmap_context; CoglBitmap *bmp; _COGL_GET_CONTEXT (ctx, NULL); g_assert (filename != NULL); g_assert (error == NULL || *error == NULL); url = CFURLCreateFromFileSystemRepresentation (NULL, (guchar *) filename, strlen (filename), false); image_source = CGImageSourceCreateWithURL (url, NULL); save_errno = errno; CFRelease (url); if (image_source == NULL) { /* doesn't exist, not readable, etc. */ g_set_error_literal (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_FAILED, g_strerror (save_errno)); return NULL; } /* Unknown images would be cleanly caught as zero width/height below, but try * to provide better error message */ type = CGImageSourceGetType (image_source); if (type == NULL) { CFRelease (image_source); g_set_error_literal (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_UNKNOWN_TYPE, "Unknown image type"); return NULL; } CFRelease (type); image = CGImageSourceCreateImageAtIndex (image_source, 0, NULL); CFRelease (image_source); width = CGImageGetWidth (image); height = CGImageGetHeight (image); if (width == 0 || height == 0) { /* incomplete or corrupt */ CFRelease (image); g_set_error_literal (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_CORRUPT_IMAGE, "Image has zero width or height"); return NULL; } /* allocate buffer big enough to hold pixel data */ bmp = _cogl_bitmap_new_with_malloc_buffer (ctx, width, height, COGL_PIXEL_FORMAT_ARGB_8888); rowstride = cogl_bitmap_get_rowstride (bmp); out_data = _cogl_bitmap_map (bmp, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); /* render to buffer */ color_space = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB); bitmap_context = CGBitmapContextCreate (out_data, width, height, 8, rowstride, color_space, kCGImageAlphaPremultipliedFirst); CGColorSpaceRelease (color_space); { const CGRect rect = {{0, 0}, {width, height}}; CGContextDrawImage (bitmap_context, rect, image); } CGImageRelease (image); CGContextRelease (bitmap_context); _cogl_bitmap_unmap (bmp); /* store bitmap info */ return bmp; }
int cogl_texture_get_data (CoglHandle handle, CoglPixelFormat format, unsigned int rowstride, guint8 *data) { CoglTexture *tex; int bpp; int byte_size; CoglPixelFormat closest_format; int closest_bpp; GLenum closest_gl_format; GLenum closest_gl_type; CoglBitmap *target_bmp; CoglBitmap *new_bmp; guint8 *src; guint8 *dst; int y; int tex_width; int tex_height; CoglTextureGetData tg_data; if (!cogl_is_texture (handle)) return 0; tex = COGL_TEXTURE (handle); /* Default to internal format if none specified */ if (format == COGL_PIXEL_FORMAT_ANY) format = cogl_texture_get_format (handle); tex_width = cogl_texture_get_width (handle); tex_height = cogl_texture_get_height (handle); /* Rowstride from texture width if none specified */ bpp = _cogl_get_format_bpp (format); if (rowstride == 0) rowstride = tex_width * bpp; /* Return byte size if only that requested */ byte_size = tex_height * rowstride; if (data == NULL) return byte_size; closest_format = _cogl_texture_driver_find_best_gl_get_data_format (format, &closest_gl_format, &closest_gl_type); closest_bpp = _cogl_get_format_bpp (closest_format); /* Is the requested format supported? */ if (closest_format == format) /* Target user data directly */ target_bmp = _cogl_bitmap_new_from_data (data, format, tex_width, tex_height, rowstride, NULL, NULL); else { int target_rowstride = tex_width * closest_bpp; guint8 *target_data = g_malloc (tex_height * target_rowstride); target_bmp = _cogl_bitmap_new_from_data (target_data, closest_format, tex_width, tex_height, target_rowstride, (CoglBitmapDestroyNotify) g_free, NULL); } tg_data.orig_width = tex_width; tg_data.orig_height = tex_height; tg_data.target_bmp = target_bmp; tg_data.target_bits = _cogl_bitmap_map (target_bmp, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); if (tg_data.target_bits == NULL) { cogl_object_unref (target_bmp); return 0; } tg_data.success = TRUE; /* Iterating through the subtextures allows piecing together * the data for a sliced texture, and allows us to do the * read-from-framebuffer logic here in a simple fashion rather than * passing offsets down through the code. */ _cogl_texture_foreach_sub_texture_in_region (handle, 0, 0, 1, 1, texture_get_cb, &tg_data); _cogl_bitmap_unmap (target_bmp); /* 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. */ if (!tg_data.success) _cogl_texture_draw_and_read (tex, target_bmp, closest_gl_format, closest_gl_type); /* Was intermediate used? */ if (closest_format != format) { guint8 *new_bmp_data; int new_bmp_rowstride; /* Convert to requested format */ new_bmp = _cogl_bitmap_convert_format_and_premult (target_bmp, format); /* Free intermediate data and return if failed */ cogl_object_unref (target_bmp); if (new_bmp == NULL) return 0; new_bmp_rowstride = _cogl_bitmap_get_rowstride (new_bmp); new_bmp_data = _cogl_bitmap_map (new_bmp, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); if (new_bmp_data == NULL) { cogl_object_unref (new_bmp); return 0; } /* Copy to user buffer */ for (y = 0; y < tex_height; ++y) { src = new_bmp_data + y * new_bmp_rowstride; dst = data + y * rowstride; memcpy (dst, src, tex_width * bpp); } _cogl_bitmap_unmap (new_bmp); /* Free converted data */ cogl_object_unref (new_bmp); } return byte_size; }
/* Reads back the contents of a texture by rendering it to the framebuffer * and reading back the resulting pixels. * * NB: Normally this approach isn't normally used since we can just use * glGetTexImage, but may be used as a fallback in some circumstances. */ gboolean _cogl_texture_draw_and_read (CoglHandle handle, CoglBitmap *target_bmp, GLuint target_gl_format, GLuint target_gl_type) { int bpp; CoglFramebuffer *framebuffer; int viewport[4]; CoglBitmap *alpha_bmp; CoglMatrixStack *projection_stack; CoglMatrixStack *modelview_stack; int target_width = _cogl_bitmap_get_width (target_bmp); int target_height = _cogl_bitmap_get_height (target_bmp); int target_rowstride = _cogl_bitmap_get_rowstride (target_bmp); _COGL_GET_CONTEXT (ctx, FALSE); bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); framebuffer = _cogl_get_draw_buffer (); /* Viewport needs to have some size and be inside the window for this */ _cogl_framebuffer_get_viewport4fv (framebuffer, viewport); if (viewport[0] < 0 || viewport[1] < 0 || viewport[2] <= 0 || viewport[3] <= 0) return FALSE; /* Setup orthographic projection into current viewport (0,0 in top-left * corner to draw the texture upside-down so we match the way cogl_read_pixels * works) */ projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); _cogl_matrix_stack_push (projection_stack); _cogl_matrix_stack_load_identity (projection_stack); _cogl_matrix_stack_ortho (projection_stack, 0, (float)(viewport[2]), (float)(viewport[3]), 0, (float)(0), (float)(100)); modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); _cogl_matrix_stack_push (modelview_stack); _cogl_matrix_stack_load_identity (modelview_stack); /* Direct copy operation */ if (ctx->texture_download_pipeline == COGL_INVALID_HANDLE) { ctx->texture_download_pipeline = cogl_pipeline_new (); cogl_pipeline_set_blend (ctx->texture_download_pipeline, "RGBA = ADD (SRC_COLOR, 0)", NULL); } cogl_push_source (ctx->texture_download_pipeline); cogl_pipeline_set_layer_texture (ctx->texture_download_pipeline, 0, handle); cogl_pipeline_set_layer_combine (ctx->texture_download_pipeline, 0, /* layer */ "RGBA = REPLACE (TEXTURE)", NULL); cogl_pipeline_set_layer_filters (ctx->texture_download_pipeline, 0, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); 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 still doesn't seem to have an alpha buffer. This might be just a PowerVR issue. GLint r_bits, g_bits, b_bits, a_bits; GE( glGetIntegerv (GL_ALPHA_BITS, &a_bits) ); GE( glGetIntegerv (GL_RED_BITS, &r_bits) ); GE( glGetIntegerv (GL_GREEN_BITS, &g_bits) ); GE( glGetIntegerv (GL_BLUE_BITS, &b_bits) ); printf ("R bits: %d\n", r_bits); printf ("G bits: %d\n", g_bits); printf ("B bits: %d\n", b_bits); printf ("A bits: %d\n", a_bits); */ if ((cogl_texture_get_format (handle) & COGL_A_BIT)/* && a_bits == 0*/) { guint8 *srcdata; guint8 *dstdata; guint8 *srcpixel; guint8 *dstpixel; int x,y; int alpha_rowstride = bpp * target_width; if ((dstdata = _cogl_bitmap_map (target_bmp, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD)) == NULL) return FALSE; srcdata = g_malloc (alpha_rowstride * target_height); /* Create temp bitmap for alpha values */ alpha_bmp = _cogl_bitmap_new_from_data (srcdata, COGL_PIXEL_FORMAT_RGBA_8888, target_width, target_height, alpha_rowstride, (CoglBitmapDestroyNotify) g_free, NULL); /* Draw alpha values into RGB channels */ cogl_pipeline_set_layer_combine (ctx->texture_download_pipeline, 0, /* layer */ "RGBA = REPLACE (TEXTURE[A])", NULL); do_texture_draw_and_read (handle, alpha_bmp, viewport); /* Copy temp R to target A */ for (y=0; y<target_height; ++y) { for (x=0; x<target_width; ++x) { srcpixel = srcdata + x*bpp; dstpixel = dstdata + x*bpp; dstpixel[3] = srcpixel[0]; } srcdata += alpha_rowstride; dstdata += target_rowstride; } _cogl_bitmap_unmap (target_bmp); cogl_object_unref (alpha_bmp); } /* Restore old state */ _cogl_matrix_stack_pop (modelview_stack); _cogl_matrix_stack_pop (projection_stack); /* restore the original pipeline */ cogl_pop_source (); return TRUE; }