From f02a649a3ce5735f5a20103fe6f1002b3a421501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 12 Mar 2012 01:05:25 -0400 Subject: [PATCH] Consolidate image loading code and move to shared/ --- clients/cairo-util.c | 358 ++----------------------------------- clients/cairo-util.h | 2 +- clients/desktop-shell.c | 2 +- clients/image.c | 139 +++------------ clients/screenshot.c | 22 +-- clients/tablet-shell.c | 2 +- clients/view.c | 2 + clients/window.c | 97 ++-------- configure.ac | 43 +++-- shared/Makefile.am | 4 + shared/config-parser.h | 5 + shared/image-loader.c | 379 ++++++++++++++++++++++++++++++++++++++++ src/Makefile.am | 4 +- src/compositor-x11.c | 16 +- src/image-loader.c | 177 ------------------- src/screenshooter.c | 24 ++- 16 files changed, 519 insertions(+), 757 deletions(-) create mode 100644 shared/image-loader.c delete mode 100644 src/image-loader.c diff --git a/clients/cairo-util.c b/clients/cairo-util.c index 4257590a..1f8d3078 100644 --- a/clients/cairo-util.c +++ b/clients/cairo-util.c @@ -31,12 +31,7 @@ #include #include "cairo-util.h" -#include -#include - -#ifdef HAVE_WEBP -#include -#endif +#include "../shared/config-parser.h" #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) @@ -297,344 +292,19 @@ rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius) cairo_close_path(cr); } -static void -swizzle_row(JSAMPLE *row, JDIMENSION width) -{ - JSAMPLE *s; - uint32_t *d; - - s = row + (width - 1) * 3; - d = (uint32_t *) (row + (width - 1) * 4); - while (s >= row) { - *d = 0xff000000 | (s[0] << 16) | (s[1] << 8) | (s[2] << 0); - s -= 3; - d--; - } -} - -static void -error_exit(j_common_ptr cinfo) -{ - longjmp(cinfo->client_data, 1); -} - -static cairo_surface_t * -load_jpeg(FILE *fp) -{ - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - int stride, i, first; - JSAMPLE *data, *rows[4]; - jmp_buf env; - - cinfo.err = jpeg_std_error(&jerr); - jerr.error_exit = error_exit; - cinfo.client_data = env; - if (setjmp(env)) - return NULL; - - jpeg_create_decompress(&cinfo); - - jpeg_stdio_src(&cinfo, fp); - - jpeg_read_header(&cinfo, TRUE); - - cinfo.out_color_space = JCS_RGB; - jpeg_start_decompress(&cinfo); - - stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, - cinfo.output_width); - data = malloc(stride * cinfo.output_height); - if (data == NULL) { - fprintf(stderr, "couldn't allocate image data\n"); - return NULL; - } - - while (cinfo.output_scanline < cinfo.output_height) { - first = cinfo.output_scanline; - for (i = 0; i < ARRAY_LENGTH(rows); i++) - rows[i] = data + (first + i) * stride; - - jpeg_read_scanlines(&cinfo, rows, ARRAY_LENGTH(rows)); - for (i = 0; first + i < cinfo.output_scanline; i++) - swizzle_row(rows[i], cinfo.output_width); - } - - jpeg_finish_decompress(&cinfo); - - jpeg_destroy_decompress(&cinfo); - - return cairo_image_surface_create_for_data (data, - CAIRO_FORMAT_RGB24, - cinfo.output_width, - cinfo.output_height, - stride); -} - -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; - png_bytep p; - - for (i = 0, p = data; i < row_info->rowbytes; i += 4, p += 4) { - png_byte alpha = p[3]; - uint32_t w; - - if (alpha == 0) { - w = 0; - } else { - png_byte red = p[0]; - png_byte green = p[1]; - png_byte blue = p[2]; - - if (alpha != 0xff) { - red = multiply_alpha(alpha, red); - green = multiply_alpha(alpha, green); - blue = multiply_alpha(alpha, blue); - } - w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); - } - - * (uint32_t *) p = w; - } -} - -static void -read_func(png_structp png, png_bytep data, png_size_t size) -{ - FILE *fp = png_get_io_ptr(png); - - if (fread(data, 1, size, fp) < 0) - png_error(png, NULL); -} - -static void -png_error_callback(png_structp png, png_const_charp error_msg) -{ - longjmp (png_jmpbuf (png), 1); -} - -static cairo_surface_t * -load_png(FILE *fp) -{ - png_struct *png; - png_info *info; - png_byte *data = NULL; - png_byte **row_pointers = NULL; - png_uint_32 width, height; - int depth, color_type, interlace, stride; - unsigned int i; - - png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, - png_error_callback, NULL); - if (!png) - return NULL; - - info = png_create_info_struct(png); - if (!info) { - png_destroy_read_struct(&png, &info, NULL); - return NULL; - } - - if (setjmp(png_jmpbuf(png))) { - if (data) - free(data); - if (row_pointers) - free(row_pointers); - png_destroy_read_struct(&png, &info, NULL); - return NULL; - } - - png_set_read_fn(png, fp, read_func); - png_read_info(png, info); - png_get_IHDR(png, info, - &width, &height, &depth, - &color_type, &interlace, NULL, NULL); - - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png); - - if (color_type == PNG_COLOR_TYPE_GRAY) - png_set_expand_gray_1_2_4_to_8(png); - - 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); - - 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); - png_set_read_user_transform_fn(png, premultiply_data); - png_read_update_info(png, info); - png_get_IHDR(png, info, - &width, &height, &depth, - &color_type, &interlace, NULL, NULL); - - - stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, width); - data = malloc(stride * height); - if (!data) { - png_destroy_read_struct(&png, &info, NULL); - return NULL; - } - - row_pointers = malloc(height * sizeof row_pointers[0]); - if (row_pointers == NULL) { - free(data); - png_destroy_read_struct(&png, &info, NULL); - return NULL; - } - - for (i = 0; i < height; i++) - row_pointers[i] = &data[i * stride]; - - png_read_image(png, row_pointers); - png_read_end(png, info); - - free(row_pointers); - png_destroy_read_struct(&png, &info, NULL); - - return cairo_image_surface_create_for_data (data, - CAIRO_FORMAT_RGB24, - width, height, stride); -} - -#ifdef HAVE_WEBP - -static cairo_surface_t * -load_webp(FILE *fp) -{ - WebPDecoderConfig config; - uint8_t buffer[16 * 1024]; - int len; - VP8StatusCode status; - WebPIDecoder *idec; - - if (!WebPInitDecoderConfig(&config)) { - fprintf(stderr, "Library version mismatch!\n"); - return NULL; - } - - /* webp decoding api doesn't seem to specify a min size that's - usable for GetFeatures, but 256 works... */ - len = fread(buffer, 1, 256, fp); - status = WebPGetFeatures(buffer, len, &config.input); - if (status != VP8_STATUS_OK) { - fprintf(stderr, "failed to parse webp header\n"); - WebPFreeDecBuffer(&config.output); - return NULL; - } - - config.output.colorspace = MODE_BGRA; - config.output.u.RGBA.stride = - cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, - config.input.width); - config.output.u.RGBA.size = - config.output.u.RGBA.stride * config.input.height; - config.output.u.RGBA.rgba = - malloc(config.output.u.RGBA.stride * config.input.height); - config.output.is_external_memory = 1; - if (!config.output.u.RGBA.rgba) { - WebPFreeDecBuffer(&config.output); - return NULL; - } - - rewind(fp); - idec = WebPINewDecoder(&config.output); - if (!idec) { - WebPFreeDecBuffer(&config.output); - return NULL; - } - - while (!feof(fp)) { - len = fread(buffer, 1, sizeof buffer, fp); - status = WebPIAppend(idec, buffer, len); - if (status != VP8_STATUS_OK) { - fprintf(stderr, "webp decode status %d\n", status); - WebPIDelete(idec); - WebPFreeDecBuffer(&config.output); - return NULL; - } - } - - WebPIDelete(idec); - WebPFreeDecBuffer(&config.output); - - return cairo_image_surface_create_for_data (config.output.u.RGBA.rgba, - CAIRO_FORMAT_RGB24, - config.input.width, - config.input.height, - config.output.u.RGBA.stride); -} - -#endif - - -struct image_loader { - char header[4]; - int header_size; - cairo_surface_t *(*load)(FILE *fp); -}; - -static const struct image_loader loaders[] = { - { { 0x89, 'P', 'N', 'G' }, 4, load_png }, - { { 0xff, 0xd8 }, 2, load_jpeg }, -#ifdef HAVE_WEBP - { { 'R', 'I', 'F', 'F' }, 4, load_webp } -#endif -}; - cairo_surface_t * -load_image(const char *filename) +load_cairo_surface(const char *filename) { - cairo_surface_t *surface; - unsigned char header[4]; - FILE *fp; - int i; - - fp = fopen(filename, "rb"); - if (fp == NULL) - return NULL; - - fread(header, sizeof header, 1, fp); - rewind(fp); - for (i = 0; i < ARRAY_LENGTH(loaders); i++) { - if (memcmp(header, loaders[i].header, - loaders[i].header_size) == 0) { - surface = loaders[i].load(fp); - break; - } - } - - fclose(fp); - - if (i == ARRAY_LENGTH(loaders)) { - fprintf(stderr, "unrecognized file header for %s: " - "0x%02x 0x%02x 0x%02x 0x%02x\n", - filename, header[0], header[1], header[2], header[3]); - surface = NULL; - } - - return surface; + pixman_image_t *image; + int width, height, stride; + void *data; + + image = load_image(filename); + data = pixman_image_get_data(image); + width = pixman_image_get_width(image); + height = pixman_image_get_height(image); + stride = pixman_image_get_stride(image); + + return cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, + width, height, stride); } diff --git a/clients/cairo-util.h b/clients/cairo-util.h index 3a9f3bc5..a973e3c8 100644 --- a/clients/cairo-util.h +++ b/clients/cairo-util.h @@ -41,6 +41,6 @@ void rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius); cairo_surface_t * -load_image(const char *filename); +load_cairo_surface(const char *filename); #endif diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c index 881bf7c4..43d23eff 100644 --- a/clients/desktop-shell.c +++ b/clients/desktop-shell.c @@ -384,7 +384,7 @@ background_draw(struct widget *widget, void *data) widget_get_allocation(widget, &allocation); image = NULL; if (key_background_image) - image = load_image(key_background_image); + image = load_cairo_surface(key_background_image); if (strcmp(key_background_type, "scale") == 0) type = BACKGROUND_SCALE; diff --git a/clients/image.c b/clients/image.c index 10588478..36ab485c 100644 --- a/clients/image.c +++ b/clients/image.c @@ -26,131 +26,36 @@ #include #include #include +#include #include #include #include #include -#include -#include #include #include "window.h" +#include "cairo-util.h" struct image { struct window *window; struct widget *widget; struct display *display; - gchar *filename; + char *filename; + cairo_surface_t *image; }; -static void -set_source_pixbuf(cairo_t *cr, - const GdkPixbuf *pixbuf, - double src_x, - double src_y, - double src_width, - double src_height) -{ - gint width = gdk_pixbuf_get_width (pixbuf); - gint height = gdk_pixbuf_get_height (pixbuf); - guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf); - int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf); - int n_channels = gdk_pixbuf_get_n_channels (pixbuf); - int cairo_stride; - guchar *cairo_pixels; - cairo_format_t format; - cairo_surface_t *surface; - int j; - - if (n_channels == 3) - format = CAIRO_FORMAT_RGB24; - else - format = CAIRO_FORMAT_ARGB32; - - surface = cairo_image_surface_create(format, width, height); - if (cairo_surface_status(surface)) { - cairo_set_source_surface(cr, surface, 0, 0); - return; - } - - cairo_stride = cairo_image_surface_get_stride(surface); - cairo_pixels = cairo_image_surface_get_data(surface); - - for (j = height; j; j--) { - guchar *p = gdk_pixels; - guchar *q = cairo_pixels; - - if (n_channels == 3) { - guchar *end = p + 3 * width; - - while (p < end) { -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - q[0] = p[2]; - q[1] = p[1]; - q[2] = p[0]; -#else - q[1] = p[0]; - q[2] = p[1]; - q[3] = p[2]; -#endif - p += 3; - q += 4; - } - } else { - guchar *end = p + 4 * width; - guint t1,t2,t3; - -#define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END - - while (p < end) { -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - MULT(q[0], p[2], p[3], t1); - MULT(q[1], p[1], p[3], t2); - MULT(q[2], p[0], p[3], t3); - q[3] = p[3]; -#else - q[0] = p[3]; - MULT(q[1], p[0], p[3], t1); - MULT(q[2], p[1], p[3], t2); - MULT(q[3], p[2], p[3], t3); -#endif - - p += 4; - q += 4; - } -#undef MULT - } - - gdk_pixels += gdk_rowstride; - cairo_pixels += cairo_stride; - } - cairo_surface_mark_dirty(surface); - - cairo_set_source_surface(cr, surface, - src_x + .5 * (src_width - width), - src_y + .5 * (src_height - height)); - cairo_surface_destroy(surface); -} - static void redraw_handler(struct widget *widget, void *data) { struct image *image = data; struct rectangle allocation; - GdkPixbuf *pb; cairo_t *cr; cairo_surface_t *surface; + double width, height, doc_aspect, window_aspect, scale; widget_get_allocation(image->widget, &allocation); - pb = gdk_pixbuf_new_from_file_at_size(image->filename, - allocation.width, - allocation.height, - NULL); - if (pb == NULL) - return; - surface = window_get_surface(image->window); cr = cairo_create(surface); widget_get_allocation(image->widget, &allocation); @@ -163,14 +68,24 @@ redraw_handler(struct widget *widget, void *data) cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(cr, 0, 0, 0, 1); cairo_paint(cr); - set_source_pixbuf(cr, pb, - 0, 0, - allocation.width, allocation.height); + + width = cairo_image_surface_get_width(image->image); + height = cairo_image_surface_get_height(image->image); + doc_aspect = width / height; + window_aspect = (double) allocation.width / allocation.height; + if (doc_aspect < window_aspect) + scale = allocation.height / height; + else + scale = allocation.width / width; + cairo_scale(cr, scale, scale); + cairo_translate(cr, + (allocation.width - width * scale) / 2 / scale, + (allocation.height - height * scale) / 2 / scale); + + cairo_set_source_surface(cr, image->image, 0, 0); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_paint(cr); - g_object_unref(pb); - cairo_pop_group_to_source(cr); cairo_paint(cr); cairo_destroy(cr); @@ -191,20 +106,20 @@ static struct image * image_create(struct display *display, const char *filename) { struct image *image; - gchar *basename; - gchar *title; + char *b, *copy, title[512];; image = malloc(sizeof *image); if (image == NULL) return image; memset(image, 0, sizeof *image); - basename = g_path_get_basename(filename); - title = g_strdup_printf("Wayland Image - %s", basename); - g_free(basename); - - image->filename = g_strdup(filename); + copy = strdup(filename); + b = basename(copy); + snprintf(title, sizeof title, "Wayland Image - %s", b); + free(copy); + image->filename = strdup(filename); + image->image = load_cairo_surface(filename); image->window = window_create(display); image->widget = frame_create(image->window, image); window_set_title(image->window, title); diff --git a/clients/screenshot.c b/clients/screenshot.c index 51f32805..8b1edf6e 100644 --- a/clients/screenshot.c +++ b/clients/screenshot.c @@ -27,8 +27,7 @@ #include #include #include -#include -#include +#include #include #include "screenshooter-client-protocol.h" @@ -129,18 +128,13 @@ create_shm_buffer(int width, int height, void **data_out) static void write_png(int width, int height, void *data) { - GdkPixbuf *pixbuf, *normal; - GError *error = NULL; - - g_type_init(); - pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE, - 8, width, height, width * 4, NULL, - NULL); - - normal = gdk_pixbuf_flip(pixbuf, FALSE); - gdk_pixbuf_save(normal, "wayland-screenshot.png", "png", &error, NULL); - g_object_unref(normal); - g_object_unref(pixbuf); + cairo_surface_t *surface; + + surface = cairo_image_surface_create_for_data(data, + CAIRO_FORMAT_ARGB32, + width, height, width * 4); + cairo_surface_write_to_png(surface, "wayland-screenshot.png"); + cairo_surface_destroy(surface); } int main(int argc, char *argv[]) diff --git a/clients/tablet-shell.c b/clients/tablet-shell.c index 89dfc3fe..cfafd235 100644 --- a/clients/tablet-shell.c +++ b/clients/tablet-shell.c @@ -88,7 +88,7 @@ paint_background(cairo_t *cr, const char *path, struct rectangle *allocation) cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); if (path) - image = load_image(path); + image = load_cairo_surface(path); if (image) { pattern = cairo_pattern_create_for_surface(image); sx = (double) cairo_image_surface_get_width(image) / diff --git a/clients/view.c b/clients/view.c index 61a3d5cb..1264207f 100644 --- a/clients/view.c +++ b/clients/view.c @@ -262,6 +262,8 @@ main(int argc, char *argv[]) struct display *d; int i; + g_type_init(); + argc = parse_options(view_options, ARRAY_LENGTH(view_options), argc, argv); diff --git a/clients/window.c b/clients/window.c index 09d5ab14..9058e4e4 100644 --- a/clients/window.c +++ b/clients/window.c @@ -35,10 +35,11 @@ #include #include #include -#include #include #include +#include + #include #ifdef USE_CAIRO_GLESV2 @@ -405,45 +406,14 @@ display_create_egl_image_surface_from_file(struct display *display, struct rectangle *rect) { cairo_surface_t *surface; - GdkPixbuf *pixbuf; - GError *error = NULL; - int stride, i; - unsigned char *pixels, *p, *end; + pixman_image_t *image; struct egl_image_surface_data *data; - pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, - rect->width, rect->height, - FALSE, &error); - if (error != NULL) - return NULL; - - if (!gdk_pixbuf_get_has_alpha(pixbuf) || - gdk_pixbuf_get_n_channels(pixbuf) != 4) { - g_object_unref(pixbuf); - return NULL; - } - - - stride = gdk_pixbuf_get_rowstride(pixbuf); - pixels = gdk_pixbuf_get_pixels(pixbuf); - - for (i = 0; i < rect->height; i++) { - p = pixels + i * stride; - end = p + rect->width * 4; - while (p < end) { - unsigned int t; - - MULT(p[0], p[0], p[3], t); - MULT(p[1], p[1], p[3], t); - MULT(p[2], p[2], p[3], t); - p += 4; - - } - } + image = load_image(filename); surface = display_create_egl_image_surface(display, 0, rect); if (surface == NULL) { - g_object_unref(pixbuf); + pixman_image_unref(image); return NULL; } @@ -452,10 +422,11 @@ display_create_egl_image_surface_from_file(struct display *display, cairo_device_acquire(display->argb_device); glBindTexture(GL_TEXTURE_2D, data->texture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rect->width, rect->height, - GL_RGBA, GL_UNSIGNED_BYTE, pixels); + GL_RGBA, GL_UNSIGNED_BYTE, + pixman_image_get_data(image)); cairo_device_release(display->argb_device); - g_object_unref(pixbuf); + pixman_image_unref(image); return surface; } @@ -557,55 +528,23 @@ display_create_shm_surface_from_file(struct display *display, struct rectangle *rect) { cairo_surface_t *surface; - GdkPixbuf *pixbuf; - GError *error = NULL; - int stride, i; - unsigned char *pixels, *p, *end, *dest_data; - int dest_stride; - uint32_t *d; - - pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, - rect->width, rect->height, - FALSE, &error); - if (error != NULL) - return NULL; + pixman_image_t *image; + void *dest; + int size; - if (!gdk_pixbuf_get_has_alpha(pixbuf) || - gdk_pixbuf_get_n_channels(pixbuf) != 4) { - g_object_unref(pixbuf); - return NULL; - } - - stride = gdk_pixbuf_get_rowstride(pixbuf); - pixels = gdk_pixbuf_get_pixels(pixbuf); + image = load_image(filename); surface = display_create_shm_surface(display, rect, 0); if (surface == NULL) { - g_object_unref(pixbuf); + pixman_image_unref(image); return NULL; } - dest_data = cairo_image_surface_get_data (surface); - dest_stride = cairo_image_surface_get_stride (surface); - - for (i = 0; i < rect->height; i++) { - d = (uint32_t *) (dest_data + i * dest_stride); - p = pixels + i * stride; - end = p + rect->width * 4; - while (p < end) { - unsigned int t; - unsigned char a, r, g, b; - - a = p[3]; - MULT(r, p[0], a, t); - MULT(g, p[1], a, t); - MULT(b, p[2], a, t); - p += 4; - *d++ = (a << 24) | (r << 16) | (g << 8) | b; - } - } + size = pixman_image_get_stride(image) * pixman_image_get_height(image); + dest = cairo_image_surface_get_data(surface); + memcpy(dest, pixman_image_get_data(image), size); - g_object_unref(pixbuf); + pixman_image_unref(image); return surface; } @@ -2921,8 +2860,6 @@ display_create(int argc, char *argv[]) { struct display *d; - g_type_init(); - argc = parse_options(xkb_options, ARRAY_LENGTH(xkb_options), argc, argv); diff --git a/configure.ac b/configure.ac index ba42a044..c6e1e969 100644 --- a/configure.ac +++ b/configure.ac @@ -25,7 +25,7 @@ AC_CHECK_FUNC([dlopen], [], AC_SUBST(DLOPEN_LIBS) PKG_CHECK_MODULES(COMPOSITOR, - [wayland-server egl >= 7.10 glesv2 pixman-1 libpng]) + [wayland-server egl >= 7.10 glesv2]) AC_CHECK_PROG(RSVG_CONVERT, rsvg-convert, rsvg-convert) AM_CONDITIONAL(HAVE_RSVG_CONVERT, test -n "$RSVG_CONVERT") @@ -104,6 +104,26 @@ if test x$cairo_modules = xcairo-glesv2; then AC_DEFINE([USE_CAIRO_GLESV2], [1], [Use the GLESv2 GL cairo backend]) fi +PKG_CHECK_MODULES(PIXMAN, [pixman-1]) +PKG_CHECK_MODULES(PNG, [libpng]) +PKG_CHECK_MODULES(WEBP, [libwebp], [have_webp=yes], [have_webp=no]) +AS_IF([test "x$have_webp" = "xyes"], + [AC_DEFINE([HAVE_WEBP], [1], [Have webp])]) +IMAGE_LIBS="$PIXMAN_LIBS $PNG_LIBS $WEBP_LIBS" +IMAGE_CFLAGS="$PIXMAN_CFLAGS $PNG_CFLAGS $WEBP_CFLAGS" +AC_SUBST(IMAGE_LIBS) +AC_SUBST(IMAGE_CFLAGS) + +AC_CHECK_LIB([jpeg], [jpeg_CreateDecompress], have_jpeglib=yes) +if test x$have_jpeglib = xyes; then + IMAGE_LIBS="$IMAGE_LIBS -ljpeg" +else + AC_ERROR([libjpeg not found]) +fi + +COMPOSITOR_LIBS="$COMPOSITOR_LIBS $IMAGE_LIBS" +COMPOSITOR_CFLAGS="$COMPOSITOR_CFLAGS $IMAGE_CFLAGS" + AC_ARG_ENABLE(simple-clients, [ --enable-simple-clients],, enable_simple_clients=yes) AM_CONDITIONAL(BUILD_SIMPLE_CLIENTS, test x$enable_simple_clients = xyes) if test x$enable_simple_clients = xyes; then @@ -117,29 +137,18 @@ AM_CONDITIONAL(BUILD_CLIENTS, test x$enable_clients = xyes) if test x$enable_clients = xyes; then AC_DEFINE([BUILD_CLIENTS], [1], [Build the Wayland clients]) - PKG_CHECK_MODULES(CLIENT, [wayland-client wayland-egl egl >= 7.10 cairo >= 1.10.0 gdk-pixbuf-2.0 glib-2.0 gobject-2.0 gio-2.0 xkbcommon libpng]) + PKG_CHECK_MODULES(CLIENT, [wayland-client wayland-egl egl >= 7.10 cairo >= 1.10.0 xkbcommon]) + + CLIENT_CFLAGS="$CLIENT_CFLAGS $IMAGE_CFLAGS" + CLIENT_LIBS="$CLIENT_LIBS $IMAGE_LIBS" - PKG_CHECK_MODULES(POPPLER, [poppler-glib], + PKG_CHECK_MODULES(POPPLER, [poppler-glib glib-2.0 gobject-2.0 gio-2.0 ], [have_poppler=yes], [have_poppler=no]) PKG_CHECK_MODULES(CAIRO_EGL, [cairo-egl >= 1.11.3 $cairo_modules], [have_cairo_egl=yes], [have_cairo_egl=no]) AS_IF([test "x$have_cairo_egl" = "xyes"], [AC_DEFINE([HAVE_CAIRO_EGL], [1], [Have cairo-egl])], [AC_MSG_WARN([Cairo-EGL not found - clients will use cairo image])]) - - PKG_CHECK_MODULES(WEBP, [libwebp], [have_webp=yes], [have_webp=no]) - AS_IF([test "x$have_webp" = "xyes"], - [AC_DEFINE([HAVE_WEBP], [1], [Have webp])]) - CLIENT_LIBS="$CLIENT_LIBS $WEBP_LIBS" - CLIENT_CFLAGS="$CLIENT_CFLAGS $WEBP_CFLAGS" - - AC_CHECK_LIB([jpeg], [jpeg_CreateDecompress], have_jpeglib=yes) - if test x$have_jpeglib = xyes; then - CLIENT_LIBS="$CLIENT_LIBS -ljpeg" - else - AC_ERROR([libjpeg not found]) - fi - fi AM_CONDITIONAL(HAVE_POPPLER, test "x$have_poppler" = "xyes") diff --git a/shared/Makefile.am b/shared/Makefile.am index 0b8ec1bd..d77060cc 100644 --- a/shared/Makefile.am +++ b/shared/Makefile.am @@ -1,5 +1,9 @@ +libconfig_parser_la_LIBADD = $(IMAGE_LIBS) +AM_CPPFLAGS = $(IMAGE_CFLAGS) + noinst_LTLIBRARIES = libconfig-parser.la libconfig_parser_la_SOURCES = \ config-parser.c \ option-parser.c \ + image-loader.c \ config-parser.h diff --git a/shared/config-parser.h b/shared/config-parser.h index 0619bb47..64039479 100644 --- a/shared/config-parser.h +++ b/shared/config-parser.h @@ -23,6 +23,8 @@ #ifndef CONFIGPARSER_H #define CONFIGPARSER_H +#include + enum config_key_type { CONFIG_KEY_INTEGER, /* typeof data = int */ CONFIG_KEY_UNSIGNED_INTEGER, /* typeof data = unsigned int */ @@ -69,5 +71,8 @@ int parse_options(const struct weston_option *options, int count, int argc, char *argv[]); +pixman_image_t * +load_image(const char *filename); + #endif /* CONFIGPARSER_H */ diff --git a/shared/image-loader.c b/shared/image-loader.c new file mode 100644 index 00000000..98cc431b --- /dev/null +++ b/shared/image-loader.c @@ -0,0 +1,379 @@ +/* + * Copyright © 2008-2012 Kristian Høgsberg + * Copyright © 2012 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "../config.h" + +#include +#include +#include +#include +#include + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) + +#ifdef HAVE_WEBP +#include +#endif + +static int +stride_for_width(int width) +{ + return width * 4; +} + +static void +swizzle_row(JSAMPLE *row, JDIMENSION width) +{ + JSAMPLE *s; + uint32_t *d; + + s = row + (width - 1) * 3; + d = (uint32_t *) (row + (width - 1) * 4); + while (s >= row) { + *d = 0xff000000 | (s[0] << 16) | (s[1] << 8) | (s[2] << 0); + s -= 3; + d--; + } +} + +static void +error_exit(j_common_ptr cinfo) +{ + longjmp(cinfo->client_data, 1); +} + +static pixman_image_t * +load_jpeg(FILE *fp) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + int stride, i, first; + JSAMPLE *data, *rows[4]; + jmp_buf env; + + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = error_exit; + cinfo.client_data = env; + if (setjmp(env)) + return NULL; + + jpeg_create_decompress(&cinfo); + + jpeg_stdio_src(&cinfo, fp); + + jpeg_read_header(&cinfo, TRUE); + + cinfo.out_color_space = JCS_RGB; + jpeg_start_decompress(&cinfo); + + stride = cinfo.output_width * 4; + data = malloc(stride * cinfo.output_height); + if (data == NULL) { + fprintf(stderr, "couldn't allocate image data\n"); + return NULL; + } + + while (cinfo.output_scanline < cinfo.output_height) { + first = cinfo.output_scanline; + for (i = 0; i < ARRAY_LENGTH(rows); i++) + rows[i] = data + (first + i) * stride; + + jpeg_read_scanlines(&cinfo, rows, ARRAY_LENGTH(rows)); + for (i = 0; first + i < cinfo.output_scanline; i++) + swizzle_row(rows[i], cinfo.output_width); + } + + jpeg_finish_decompress(&cinfo); + + jpeg_destroy_decompress(&cinfo); + + return pixman_image_create_bits(PIXMAN_a8r8g8b8, + cinfo.output_width, + cinfo.output_height, + (uint32_t *) data, stride); +} + +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; + png_bytep p; + + for (i = 0, p = data; i < row_info->rowbytes; i += 4, p += 4) { + png_byte alpha = p[3]; + uint32_t w; + + if (alpha == 0) { + w = 0; + } else { + png_byte red = p[0]; + png_byte green = p[1]; + png_byte blue = p[2]; + + if (alpha != 0xff) { + red = multiply_alpha(alpha, red); + green = multiply_alpha(alpha, green); + blue = multiply_alpha(alpha, blue); + } + w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); + } + + * (uint32_t *) p = w; + } +} + +static void +read_func(png_structp png, png_bytep data, png_size_t size) +{ + FILE *fp = png_get_io_ptr(png); + + if (fread(data, 1, size, fp) < 0) + png_error(png, NULL); +} + +static void +png_error_callback(png_structp png, png_const_charp error_msg) +{ + longjmp (png_jmpbuf (png), 1); +} + +static pixman_image_t * +load_png(FILE *fp) +{ + png_struct *png; + png_info *info; + png_byte *data = NULL; + png_byte **row_pointers = NULL; + png_uint_32 width, height; + int depth, color_type, interlace, stride; + unsigned int i; + + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + png_error_callback, NULL); + if (!png) + return NULL; + + info = png_create_info_struct(png); + if (!info) { + png_destroy_read_struct(&png, &info, NULL); + return NULL; + } + + if (setjmp(png_jmpbuf(png))) { + if (data) + free(data); + if (row_pointers) + free(row_pointers); + png_destroy_read_struct(&png, &info, NULL); + return NULL; + } + + png_set_read_fn(png, fp, read_func); + png_read_info(png, info); + png_get_IHDR(png, info, + &width, &height, &depth, + &color_type, &interlace, NULL, NULL); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png); + + if (color_type == PNG_COLOR_TYPE_GRAY) + png_set_expand_gray_1_2_4_to_8(png); + + 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); + + 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); + png_set_read_user_transform_fn(png, premultiply_data); + png_read_update_info(png, info); + png_get_IHDR(png, info, + &width, &height, &depth, + &color_type, &interlace, NULL, NULL); + + + stride = stride_for_width(width); + data = malloc(stride * height); + if (!data) { + png_destroy_read_struct(&png, &info, NULL); + return NULL; + } + + row_pointers = malloc(height * sizeof row_pointers[0]); + if (row_pointers == NULL) { + free(data); + png_destroy_read_struct(&png, &info, NULL); + return NULL; + } + + for (i = 0; i < height; i++) + row_pointers[i] = &data[i * stride]; + + png_read_image(png, row_pointers); + png_read_end(png, info); + + free(row_pointers); + png_destroy_read_struct(&png, &info, NULL); + + return pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, + (uint32_t *) data, stride); +} + +#ifdef HAVE_WEBP + +static pixman_image_t * +load_webp(FILE *fp) +{ + WebPDecoderConfig config; + uint8_t buffer[16 * 1024]; + int len; + VP8StatusCode status; + WebPIDecoder *idec; + + if (!WebPInitDecoderConfig(&config)) { + fprintf(stderr, "Library version mismatch!\n"); + return NULL; + } + + /* webp decoding api doesn't seem to specify a min size that's + usable for GetFeatures, but 256 works... */ + len = fread(buffer, 1, 256, fp); + status = WebPGetFeatures(buffer, len, &config.input); + if (status != VP8_STATUS_OK) { + fprintf(stderr, "failed to parse webp header\n"); + WebPFreeDecBuffer(&config.output); + return NULL; + } + + config.output.colorspace = MODE_BGRA; + config.output.u.RGBA.stride = stride_for_width(config.input.width); + config.output.u.RGBA.size = + config.output.u.RGBA.stride * config.input.height; + config.output.u.RGBA.rgba = + malloc(config.output.u.RGBA.stride * config.input.height); + config.output.is_external_memory = 1; + if (!config.output.u.RGBA.rgba) { + WebPFreeDecBuffer(&config.output); + return NULL; + } + + rewind(fp); + idec = WebPINewDecoder(&config.output); + if (!idec) { + WebPFreeDecBuffer(&config.output); + return NULL; + } + + while (!feof(fp)) { + len = fread(buffer, 1, sizeof buffer, fp); + status = WebPIAppend(idec, buffer, len); + if (status != VP8_STATUS_OK) { + fprintf(stderr, "webp decode status %d\n", status); + WebPIDelete(idec); + WebPFreeDecBuffer(&config.output); + return NULL; + } + } + + WebPIDelete(idec); + WebPFreeDecBuffer(&config.output); + + return pixman_image_create_bits(PIXMAN_a8r8g8b8, + config.input.width, + config.input.height, + (uint32_t *) config.output.u.RGBA.rgba, + config.output.u.RGBA.stride); +} + +#endif + + +struct image_loader { + char header[4]; + int header_size; + pixman_image_t *(*load)(FILE *fp); +}; + +static const struct image_loader loaders[] = { + { { 0x89, 'P', 'N', 'G' }, 4, load_png }, + { { 0xff, 0xd8 }, 2, load_jpeg }, +#ifdef HAVE_WEBP + { { 'R', 'I', 'F', 'F' }, 4, load_webp } +#endif +}; + +pixman_image_t * +load_image(const char *filename) +{ + pixman_image_t *image; + unsigned char header[4]; + FILE *fp; + int i; + + fp = fopen(filename, "rb"); + if (fp == NULL) + return NULL; + + fread(header, sizeof header, 1, fp); + rewind(fp); + for (i = 0; i < ARRAY_LENGTH(loaders); i++) { + if (memcmp(header, loaders[i].header, + loaders[i].header_size) == 0) { + image = loaders[i].load(fp); + break; + } + } + + fclose(fp); + + if (i == ARRAY_LENGTH(loaders)) { + fprintf(stderr, "unrecognized file header for %s: " + "0x%02x 0x%02x 0x%02x 0x%02x\n", + filename, header[0], header[1], header[2], header[3]); + image = NULL; + } + + return image; +} diff --git a/src/Makefile.am b/src/Makefile.am index 4a93d51a..5ec9fea1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,7 +16,6 @@ weston_LDADD = \ weston_SOURCES = \ compositor.c \ compositor.h \ - image-loader.c \ screenshooter.c \ screenshooter-protocol.c \ screenshooter-server-protocol.h \ @@ -52,7 +51,8 @@ module_LTLIBRARIES = \ if ENABLE_X11_COMPOSITOR x11_backend = x11-backend.la x11_backend_la_LDFLAGS = -module -avoid-version -x11_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(X11_COMPOSITOR_LIBS) +x11_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(X11_COMPOSITOR_LIBS) \ + ../shared/libconfig-parser.la x11_backend_la_CFLAGS = $(X11_COMPOSITOR_CFLAGS) $(GCC_CFLAGS) x11_backend_la_SOURCES = compositor-x11.c endif diff --git a/src/compositor-x11.c b/src/compositor-x11.c index 9d813de6..c3eaab70 100644 --- a/src/compositor-x11.c +++ b/src/compositor-x11.c @@ -43,6 +43,7 @@ #include #include "compositor.h" +#include "../shared/config-parser.h" struct x11_compositor { struct weston_compositor base; @@ -310,26 +311,29 @@ static void x11_output_set_icon(struct x11_compositor *c, struct x11_output *output, const char *filename) { - uint32_t *icon, *pixels, stride; + uint32_t *icon; int32_t width, height; + pixman_image_t *image; - pixels = weston_load_image(filename, &width, &height, &stride); - if (!pixels) + image = load_image(filename); + if (!image) return; + width = pixman_image_get_width(image); + height = pixman_image_get_height(image); icon = malloc(width * height * 4 + 8); if (!icon) { - free(pixels); + pixman_image_unref(image); return; } icon[0] = width; icon[1] = height; - memcpy(icon + 2, pixels, width * height * 4); + memcpy(icon + 2, pixman_image_get_data(image), width * height * 4); xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window, c->atom.net_wm_icon, c->atom.cardinal, 32, width * height + 2, icon); free(icon); - free(pixels); + pixman_image_unref(image); } static int diff --git a/src/image-loader.c b/src/image-loader.c deleted file mode 100644 index 88899697..00000000 --- a/src/image-loader.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright © 2011 Intel Corporation - * - * Permission to use, copy, modify, distribute, and sell this software and - * its documentation for any purpose is hereby granted without fee, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of the copyright holders not be used in - * advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. The copyright holders make - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#include "compositor.h" - -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; - png_bytep p; - - for (i = 0, p = data; i < row_info->rowbytes; i += 4, p += 4) { - png_byte alpha = p[3]; - uint32_t w; - - if (alpha == 0) { - w = 0; - } else { - png_byte red = p[0]; - png_byte green = p[1]; - png_byte blue = p[2]; - - if (alpha != 0xff) { - red = multiply_alpha(alpha, red); - green = multiply_alpha(alpha, green); - blue = multiply_alpha(alpha, blue); - } - w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); - } - - * (uint32_t *) p = w; - } -} - -static void -read_func(png_structp png, png_bytep data, png_size_t size) -{ - FILE *fp = png_get_io_ptr(png); - - if (fread(data, 1, size, fp) < 0) - png_error(png, NULL); -} - -WL_EXPORT uint32_t * -weston_load_image(const char *filename, - int32_t *width_arg, int32_t *height_arg, - uint32_t *stride_arg) -{ - png_struct *png; - png_info *info; - png_byte *data; - png_byte **row_pointers = NULL; - png_uint_32 width, height; - int depth, color_type, interlace, stride; - unsigned int i; - FILE *fp; - - fp = fopen(filename, "rb"); - if (fp == NULL) - return NULL; - - png = png_create_read_struct(PNG_LIBPNG_VER_STRING, - NULL, NULL, NULL); - if (!png) { - fclose(fp); - return NULL; - } - - info = png_create_info_struct(png); - if (!info) { - png_destroy_read_struct(&png, &info, NULL); - fclose(fp); - return NULL; - } - - png_set_read_fn(png, fp, read_func); - png_read_info(png, info); - png_get_IHDR(png, info, - &width, &height, &depth, - &color_type, &interlace, NULL, NULL); - - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png); - - if (color_type == PNG_COLOR_TYPE_GRAY) - png_set_expand_gray_1_2_4_to_8(png); - - 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); - - 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); - png_set_read_user_transform_fn(png, premultiply_data); - png_read_update_info(png, info); - png_get_IHDR(png, info, - &width, &height, &depth, - &color_type, &interlace, NULL, NULL); - - stride = width * 4; - data = malloc(stride * height); - if (!data) { - png_destroy_read_struct(&png, &info, NULL); - fclose(fp); - return NULL; - } - - row_pointers = malloc(height * sizeof row_pointers[0]); - if (row_pointers == NULL) { - free(data); - png_destroy_read_struct(&png, &info, NULL); - fclose(fp); - return NULL; - } - - for (i = 0; i < height; i++) - row_pointers[i] = &data[i * stride]; - - png_read_image(png, row_pointers); - png_read_end(png, info); - - free(row_pointers); - png_destroy_read_struct(&png, &info, NULL); - fclose(fp); - - *width_arg = width; - *height_arg = height; - *stride_arg = stride; - - return (uint32_t *) data; -} diff --git a/src/screenshooter.c b/src/screenshooter.c index d0c83083..080427b7 100644 --- a/src/screenshooter.c +++ b/src/screenshooter.c @@ -21,6 +21,7 @@ */ #include +#include #include "compositor.h" #include "screenshooter-server-protocol.h" @@ -39,6 +40,8 @@ screenshooter_shoot(struct wl_client *client, { struct weston_output *output = output_resource->data; struct wl_buffer *buffer = buffer_resource->data; + uint8_t *tmp, *d, *s; + int32_t stride, i; if (!wl_buffer_is_shm(buffer)) return; @@ -47,10 +50,27 @@ screenshooter_shoot(struct wl_client *client, buffer->height < output->current->height) return; + stride = wl_shm_buffer_get_stride(buffer); + tmp = malloc(stride * buffer->height); + if (tmp == NULL) { + wl_resource_post_no_memory(resource); + return; + } + glPixelStorei(GL_PACK_ALIGNMENT, 1); glReadPixels(0, 0, output->current->width, output->current->height, - GL_RGBA, GL_UNSIGNED_BYTE, - wl_shm_buffer_get_data(buffer)); + GL_RGBA, GL_UNSIGNED_BYTE, tmp); + + d = wl_shm_buffer_get_data(buffer); + s = tmp + stride * (buffer->height - 1); + + for (i = 0; i < buffer->height; i++) { + memcpy(d, s, stride); + d += stride; + s -= stride; + } + + free(tmp); } struct screenshooter_interface screenshooter_implementation = {