static CoglBool _cogl_texture_driver_upload_subregion_to_gl (CoglContext *ctx, CoglTexture *texture, CoglBool is_foreign, int src_x, int src_y, int dst_x, int dst_y, int width, int height, int level, CoglBitmap *source_bmp, GLuint source_gl_format, GLuint source_gl_type, CoglError **error) { GLenum gl_target; GLuint gl_handle; uint8_t *data; CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); int bpp = _cogl_pixel_format_get_bytes_per_pixel (source_format); CoglBitmap *slice_bmp; int rowstride; CoglBool status = TRUE; CoglError *internal_error = NULL; int level_width; int level_height; cogl_texture_get_gl_texture (texture, &gl_handle, &gl_target); /* If we have the GL_EXT_unpack_subimage extension then we can upload from subregions directly. Otherwise we may need to copy the bitmap */ if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE) && (src_x != 0 || src_y != 0 || width != cogl_bitmap_get_width (source_bmp) || height != cogl_bitmap_get_height (source_bmp))) { slice_bmp = _cogl_bitmap_new_with_malloc_buffer (ctx, width, height, source_format, error); if (!slice_bmp) return FALSE; if (!_cogl_bitmap_copy_subregion (source_bmp, slice_bmp, src_x, src_y, 0, 0, /* dst_x/y */ width, height, error)) { cogl_object_unref (slice_bmp); return FALSE; } src_x = src_y = 0; } else { slice_bmp = prepare_bitmap_alignment_for_upload (ctx, source_bmp, error); if (!slice_bmp) return FALSE; } rowstride = cogl_bitmap_get_rowstride (slice_bmp); /* Setup gl alignment to match rowstride and top-left corner */ prep_gl_for_pixels_upload_full (ctx, rowstride, src_x, src_y, bpp); data = _cogl_bitmap_gl_bind (slice_bmp, COGL_BUFFER_ACCESS_READ, 0, &internal_error); /* NB: _cogl_bitmap_gl_bind() may return NULL when successfull so we * have to explicitly check the cogl error pointer to catch * problems... */ if (internal_error) { _cogl_propagate_error (error, internal_error); cogl_object_unref (slice_bmp); return FALSE; } _cogl_bind_gl_texture_transient (gl_target, gl_handle, is_foreign); /* Clear any GL errors */ _cogl_gl_util_clear_gl_errors (ctx); _cogl_texture_get_level_size (texture, level, &level_width, &level_height, NULL); if (level_width == width && level_height == height) { /* GL gets upset if you use glTexSubImage2D to define the * contents of a mipmap level so we make sure to use * glTexImage2D if we are uploading a full mipmap level. */ ctx->glTexImage2D (gl_target, level, _cogl_texture_gl_get_format (texture), width, height, 0, source_gl_format, source_gl_type, data); } else { /* GL gets upset if you use glTexSubImage2D to initialize the * contents of a mipmap level so if this is the first time * we've seen a request to upload to this level we call * glTexImage2D first to assert that the storage for this * level exists. */ if (texture->max_level < level) { ctx->glTexImage2D (gl_target, level, _cogl_texture_gl_get_format (texture), level_width, level_height, 0, source_gl_format, source_gl_type, NULL); } ctx->glTexSubImage2D (gl_target, level, dst_x, dst_y, width, height, source_gl_format, source_gl_type, data); } if (_cogl_gl_util_catch_out_of_memory (ctx, error)) status = FALSE; _cogl_bitmap_gl_unbind (slice_bmp); cogl_object_unref (slice_bmp); return status; }
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; }
/* 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; }
static void clutter_canvas_emit_draw (ClutterCanvas *self) { ClutterCanvasPrivate *priv = self->priv; int real_width, real_height; cairo_surface_t *surface; gboolean mapped_buffer; unsigned char *data; CoglBuffer *buffer; int window_scale = 1; gboolean res; cairo_t *cr; g_assert (priv->height > 0 && priv->width > 0); priv->dirty = TRUE; if (priv->scale_factor_set) window_scale = priv->scale_factor; else g_object_get (clutter_settings_get_default (), "window-scaling-factor", &window_scale, NULL); real_width = priv->width * window_scale; real_height = priv->height * window_scale; CLUTTER_NOTE (MISC, "Creating Cairo surface with size %d x %d (real: %d x %d, scale: %d)", priv->width, priv->height, real_width, real_height, window_scale); if (priv->buffer == NULL) { CoglContext *ctx; ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); priv->buffer = cogl_bitmap_new_with_size (ctx, real_width, real_height, CLUTTER_CAIRO_FORMAT_ARGB32); } buffer = COGL_BUFFER (cogl_bitmap_get_buffer (priv->buffer)); if (buffer == NULL) return; cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC); data = cogl_buffer_map (buffer, COGL_BUFFER_ACCESS_READ_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); if (data != NULL) { int bitmap_stride = cogl_bitmap_get_rowstride (priv->buffer); surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, real_width, real_height, bitmap_stride); mapped_buffer = TRUE; } else { surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, real_width, real_height); mapped_buffer = FALSE; } cairo_surface_set_device_scale (surface, window_scale, window_scale); self->priv->cr = cr = cairo_create (surface); g_signal_emit (self, canvas_signals[DRAW], 0, cr, priv->width, priv->height, &res); #ifdef CLUTTER_ENABLE_DEBUG if (_clutter_diagnostic_enabled () && cairo_status (cr)) { g_warning ("Drawing failed for <ClutterCanvas>[%p]: %s", self, cairo_status_to_string (cairo_status (cr))); } #endif self->priv->cr = NULL; cairo_destroy (cr); if (mapped_buffer) cogl_buffer_unmap (buffer); else { int size = cairo_image_surface_get_stride (surface) * priv->height; cogl_buffer_set_data (buffer, 0, cairo_image_surface_get_data (surface), size); } cairo_surface_destroy (surface); }