summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2012-08-13 23:33:47 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2012-08-14 00:22:59 +0100
commite5bac9de50fe2f6bd241e78b2838c4893abecbff (patch)
treebb7816906be6c169779aabacb37fa6e153cda602
parent391abd1a8edc6103ee2007d8eff510102ea932ca (diff)
Demonstrate use of cairo_surface_create_similar_image for fast pixel upload
-rw-r--r--Makefile4
-rw-r--r--demo.c7
-rw-r--r--demo.h5
-rw-r--r--fish-demo.c7
-rw-r--r--png.c233
-rw-r--r--slideshow-demo.c2
-rw-r--r--spinner-demo.c2
7 files changed, 249 insertions, 11 deletions
diff --git a/Makefile b/Makefile
index bf8711f..7090311 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/demo.c b/demo.c
index c50bf1e..d2f65f7 100644
--- a/demo.c
+++ b/demo.c
@@ -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;
diff --git a/demo.h b/demo.h
index 17a2109..c3d3c17 100644
--- a/demo.h
+++ b/demo.h
@@ -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);
diff --git a/png.c b/png.c
new file mode 100644
index 0000000..702bd01
--- /dev/null
+++ b/png.c
@@ -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);