diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2012-08-13 23:33:47 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2012-08-14 00:22:59 +0100 |
commit | e5bac9de50fe2f6bd241e78b2838c4893abecbff (patch) | |
tree | bb7816906be6c169779aabacb37fa6e153cda602 | |
parent | 391abd1a8edc6103ee2007d8eff510102ea932ca (diff) |
Demonstrate use of cairo_surface_create_similar_image for fast pixel upload
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | demo.c | 7 | ||||
-rw-r--r-- | demo.h | 5 | ||||
-rw-r--r-- | fish-demo.c | 7 | ||||
-rw-r--r-- | png.c | 233 | ||||
-rw-r--r-- | slideshow-demo.c | 2 | ||||
-rw-r--r-- | spinner-demo.c | 2 |
7 files changed, 249 insertions, 11 deletions
@@ -1,4 +1,4 @@ -SOURCES:=demo.c +SOURCES:=demo.c png.c REQUIRES:=cairo DRM:=0 @@ -69,7 +69,7 @@ DEFINES+=-DHAVE_GDK_PIXBUF endif CFLAGS:=$(shell pkg-config --cflags $(REQUIRES)) -Wall -g3 -LIBS:=$(shell pkg-config --libs $(REQUIRES)) -lm +LIBS:=$(shell pkg-config --libs $(REQUIRES)) -lpng -lm spinner-demo: spinner-demo.c $(SOURCES) demo.h Makefile $(CC) $(DEFINES) $(CFLAGS) -o $@ spinner-demo.c $(SOURCES) $(LIBS) @@ -224,7 +224,7 @@ struct device *device_open(int argc, char **argv) #if HAVE_GDK_PIXBUF cairo_surface_t * -_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *pixbuf) +surface_create_from_pixbuf(cairo_surface_t *other, const GdkPixbuf *pixbuf) { gint width = gdk_pixbuf_get_width (pixbuf); gint height = gdk_pixbuf_get_height (pixbuf); @@ -242,7 +242,10 @@ _cairo_image_surface_create_from_pixbuf(const GdkPixbuf *pixbuf) else format = CAIRO_FORMAT_ARGB32; - surface = cairo_image_surface_create(format, width, height); + if (other == NULL || cairo_version () < CAIRO_VERSION_ENCODE (1, 12, 0)) + surface = cairo_image_surface_create(format, width, height); + else + surface = cairo_surface_create_similar_image(other, format, width, height); if (cairo_surface_status(surface)) return surface; @@ -105,9 +105,12 @@ static inline struct device *skia_open (int argc, char **argv) { return 0; } #include <gdk-pixbuf/gdk-pixbuf.h> cairo_surface_t * -_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *pixbuf); +surface_create_from_pixbuf(cairo_surface_t *other, const GdkPixbuf *pixbuf); #endif +cairo_surface_t * +surface_create_from_png (cairo_surface_t *other, const char *filename); + void fps_draw (cairo_t *cr, const char *name, const char *version, const struct timeval *last, diff --git a/fish-demo.c b/fish-demo.c index 8555166..d6d32bb 100644 --- a/fish-demo.c +++ b/fish-demo.c @@ -30,8 +30,7 @@ static cairo_pattern_t *create_background(struct device *device, int *x1, int *x double sf; cairo_t *cr; - image = cairo_image_surface_create_from_png("fishbg.png"); - + image = surface_create_from_png(device->scanout, "fishbg.png"); height = cairo_image_surface_get_height(image); width = cairo_image_surface_get_width(image); sf = height / (double)device->height; @@ -65,11 +64,11 @@ static cairo_surface_t **create_strip(struct device *device) int width, height, x, y; cairo_t *cr; - image = cairo_image_surface_create_from_png("fishstrip.png"); + image = surface_create_from_png(device->scanout, "fishstrip.png"); width = cairo_image_surface_get_width(image); height = cairo_image_surface_get_height(image); surface = cairo_surface_create_similar(device->scanout, - CAIRO_CONTENT_COLOR_ALPHA, + cairo_surface_get_content (image), width, height); cr = cairo_create(surface); cairo_set_source_surface(cr, image, 0, 0); @@ -0,0 +1,233 @@ +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <png.h> + +#include "demo.h" + +static void +stdio_read_func (png_structp png, png_bytep data, png_size_t size) +{ + FILE *file = png_get_io_ptr (png); + while (size) { + size_t ret; + + ret = fread (data, 1, size, file); + size -= ret; + data += ret; + + if (size && (feof (file) || ferror (file))) + png_error (png, NULL); + } +} + +static void +png_simple_error_callback (png_structp png, + png_const_charp error_msg) +{ +#ifdef PNG_SETJMP_SUPPORTED + longjmp (png_jmpbuf (png), 1); +#endif + + /* if we get here, then we have to choice but to abort ... */ +} + +static void +png_simple_warning_callback (png_structp png, + png_const_charp error_msg) +{ +} + +static inline int +multiply_alpha (int alpha, int color) +{ + int temp = (alpha * color) + 0x80; + return ((temp + (temp >> 8)) >> 8); +} + +static void +premultiply_data (png_structp png, + png_row_infop row_info, + png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *base = &data[i]; + uint8_t alpha = base[3]; + uint32_t *p = (uint32_t *)base; + + if (alpha == 0) { + *p = 0; + } else { + uint8_t red = base[0]; + uint8_t green = base[1]; + uint8_t blue = base[2]; + + if (alpha != 0xff) { + red = multiply_alpha (alpha, red); + green = multiply_alpha (alpha, green); + blue = multiply_alpha (alpha, blue); + } + *p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); + } + } +} + +static void +convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *base = &data[i]; + uint8_t red = base[0]; + uint8_t green = base[1]; + uint8_t blue = base[2]; + uint32_t *p = (uint32_t *)base; + + *p = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0); + } +} + +static cairo_surface_t * +read_png (FILE *file, cairo_surface_t *other) +{ + cairo_surface_t *surface = NULL; + png_byte **rows = NULL; + png_struct *png; + png_info *info; + png_uint_32 width, height; + uint8_t *data; + int depth, color_type, interlace, stride; + unsigned int i; + cairo_format_t format; + + png = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, + png_simple_error_callback, + png_simple_warning_callback); + if (png == NULL) + return NULL; + + info = png_create_info_struct (png); + if (info == NULL) + goto BAIL; + + png_set_read_fn (png, file, stdio_read_func); + +#ifdef PNG_SETJMP_SUPPORTED + if (setjmp (png_jmpbuf (png))) + goto BAIL; +#endif + + png_read_info (png, info); + + png_get_IHDR (png, info, + &width, &height, &depth, + &color_type, &interlace, NULL, NULL); + + /* convert palette/gray image to rgb */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb (png); + + /* expand gray bit depth if needed */ + if (color_type == PNG_COLOR_TYPE_GRAY) { +#if PNG_LIBPNG_VER >= 10209 + png_set_expand_gray_1_2_4_to_8 (png); +#else + png_set_gray_1_2_4_to_8 (png); +#endif + } + + /* transform transparency to alpha */ + if (png_get_valid (png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png); + + if (depth == 16) + png_set_strip_16 (png); + + if (depth < 8) + png_set_packing (png); + + /* convert grayscale to RGB */ + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (png); + + if (interlace != PNG_INTERLACE_NONE) + png_set_interlace_handling (png); + + png_set_filler (png, 0xff, PNG_FILLER_AFTER); + + /* recheck header after setting EXPAND options */ + png_read_update_info (png, info); + png_get_IHDR (png, info, + &width, &height, &depth, + &color_type, &interlace, NULL, NULL); + if (depth != 8 || + ! (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA)) + goto BAIL; + + switch (color_type) { + default: + /* fall-through just in case ;-) */ + case PNG_COLOR_TYPE_RGB_ALPHA: + format = CAIRO_FORMAT_ARGB32; + png_set_read_user_transform_fn (png, premultiply_data); + break; + + case PNG_COLOR_TYPE_RGB: + format = CAIRO_FORMAT_RGB24; + png_set_read_user_transform_fn (png, convert_bytes_to_data); + break; + } + + surface = cairo_surface_create_similar_image (other, + format, width, height); + if (cairo_surface_status (surface)) + goto BAIL; + + rows = malloc (height * sizeof (char *)); + if (rows == NULL) + goto BAIL; + + data = cairo_image_surface_get_data (surface); + stride = cairo_image_surface_get_stride (surface); + for (i = 0; i < height; i++) + rows[i] = data + i*stride; + + png_read_image (png, rows); + png_read_end (png, info); + free (rows); + + cairo_surface_mark_dirty (surface); + return surface; + +BAIL: + free (rows); + if (png != NULL) + png_destroy_read_struct (&png, &info, NULL); + cairo_surface_destroy (surface); + return NULL; +} + +cairo_surface_t * +surface_create_from_png (cairo_surface_t *other, const char *filename) +{ + FILE *file; + cairo_surface_t *surface; + + if (cairo_version () < CAIRO_VERSION_ENCODE (1, 12, 0)) + return cairo_image_surface_create_from_png (filename); + + file = fopen (filename, "rb"); + if (file == NULL) + return NULL; + + surface = read_png (file, other); + fclose (file); + + return surface; +} diff --git a/slideshow-demo.c b/slideshow-demo.c index d4c85b2..284a1a9 100644 --- a/slideshow-demo.c +++ b/slideshow-demo.c @@ -35,7 +35,7 @@ static int load_sources_file(const char *filename, if (pb == NULL) return 0; - image = _cairo_image_surface_create_from_pixbuf(pb); + image = surface_create_from_pixbuf(preload ? target : NULL, pb); g_object_unref(pb); width = cairo_image_surface_get_width(image); diff --git a/spinner-demo.c b/spinner-demo.c index ea2921e..95bb6c1 100644 --- a/spinner-demo.c +++ b/spinner-demo.c @@ -32,7 +32,7 @@ static int load_source(const char *filename, struct device *device) if (pb == NULL) return 0; - image = _cairo_image_surface_create_from_pixbuf(pb); + image = surface_create_from_pixbuf(device->scanout, pb); g_object_unref(pb); width = cairo_image_surface_get_width(image); |