From b56afd8bb8504c6d9c2edee67b0368517bb540cc Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 17 Aug 2015 06:29:30 -0700 Subject: [PATCH] GUAC-240: Approximate whether images will compress well with JPEG vs. PNG. --- src/common/Makefile.am | 6 +- src/common/guac_surface.c | 68 +++++++++++- src/common/guac_surface_smoothness.c | 158 --------------------------- src/common/guac_surface_smoothness.h | 43 -------- 4 files changed, 68 insertions(+), 207 deletions(-) delete mode 100644 src/common/guac_surface_smoothness.c delete mode 100644 src/common/guac_surface_smoothness.h diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 816e1a74..370df685 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -35,8 +35,7 @@ noinst_HEADERS = \ guac_pointer_cursor.h \ guac_rect.h \ guac_string.h \ - guac_surface.h \ - guac_surface_smoothness.h + guac_surface.h libguac_common_la_SOURCES = \ guac_io.c \ @@ -48,8 +47,7 @@ libguac_common_la_SOURCES = \ guac_pointer_cursor.c \ guac_rect.c \ guac_string.c \ - guac_surface.c \ - guac_surface_smoothness.c + guac_surface.c libguac_common_la_CFLAGS = \ -Werror -Wall -pedantic \ diff --git a/src/common/guac_surface.c b/src/common/guac_surface.c index f19a1648..51c06b55 100644 --- a/src/common/guac_surface.c +++ b/src/common/guac_surface.c @@ -23,7 +23,6 @@ #include "config.h" #include "guac_rect.h" #include "guac_surface.h" -#include "guac_surface_smoothness.h" #include #include @@ -313,6 +312,70 @@ static unsigned int __guac_common_surface_calculate_framerate( return 0; +} + + /** + * Guesses whether a rectangle within a particular surface would be better + * compressed as PNG or as JPEG. Positive values indicate PNG is likely to + * be superior, while negative values indicate JPEG. + * + * @param surface + * The surface containing the image data to check. + * + * @param rect + * The rect to check within the given surface. + * + * @return + * Positive values if PNG compression is likely to perform better than + * JPEG, or negative values if JPEG is likely to perform better than PNG. + */ +static int guac_common_surface_png_optimality(guac_common_surface* surface, + const guac_common_rect* rect) { + + int x, y; + + int similarity = 0; + + /* Get image/buffer metrics */ + int width = rect->width; + int height = rect->height; + int stride = surface->stride; + + /* Get buffer from surface */ + unsigned char* buffer = surface->buffer + rect->y * stride + rect->x * 4; + + /* For each row */ + for (y = 0; y < height; y++) { + + uint32_t last_pixel = -1; + uint32_t* row = (uint32_t*) buffer; + + /* For each pixel in current row */ + for (x = 0; x < width; x++) { + + /* Get next pixel */ + uint32_t current_pixel = *(row++); + + /* Update similarity according to whether pixel is identical */ + if (last_pixel != -1) { + if (current_pixel == last_pixel) + similarity++; + else + similarity--; + } + + last_pixel = current_pixel; + + } + + /* Advance to next row */ + buffer += stride; + + } + + /* Return rough approximation of optimality for PNG compression */ + return 0xFF * similarity / width / height; + } /** @@ -336,7 +399,8 @@ static int __guac_common_surface_should_use_jpeg(guac_common_surface* surface, int framerate = __guac_common_surface_calculate_framerate(surface, rect); /* JPEG is preferred if framerate is high enough */ - return framerate >= GUAC_COMMON_SURFACE_JPEG_FRAMERATE; + return framerate >= GUAC_COMMON_SURFACE_JPEG_FRAMERATE + && guac_common_surface_png_optimality(surface, rect) < 0; } diff --git a/src/common/guac_surface_smoothness.c b/src/common/guac_surface_smoothness.c deleted file mode 100644 index 9300573b..00000000 --- a/src/common/guac_surface_smoothness.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2015 Glyptodon LLC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * Smoothness detection from: - * QEMU VNC display driver: tight encoding - * - * From libvncserver/libvncserver/tight.c - * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. - * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. - * - * Copyright (C) 2010 Corentin Chary - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "guac_surface_smoothness.h" - -#include -#include -#include - -/** - * The threshold to determine an image to be smooth. - */ -#define GUAC_SURFACE_SMOOTHNESS_THRESHOLD 0 - -/** - * Width of sub-row when detecting image smoothness. - */ -#define GUAC_SURFACE_SMOOTHNESS_DETECT_SUBROW_WIDTH 7 - -int guac_common_surface_rect_is_smooth(guac_common_surface* surface, - guac_common_rect* rect) -{ - - /* - * Code to guess if the image in a given rectangle is smooth - * (by applying "gradient" filter or JPEG coder). - */ - int x, y, d, dx; - unsigned int c; - unsigned int stats[256]; - int pixels = 0; - int pix, left[3]; - unsigned char* buffer = surface->buffer; - int stride = surface->stride; - int w = rect->x + rect->width; - int h = rect->y + rect->height; - - /* If rect is out of bounds, bail out */ - if (rect->x < 0 || rect->y < 0 || - w > surface->width || h > surface->height) { - return 0; - } - - /* If rect is too small to process, bail out */ - if (rect->width < GUAC_SURFACE_SMOOTHNESS_DETECT_SUBROW_WIDTH + 1 || - rect->height < GUAC_SURFACE_SMOOTHNESS_DETECT_SUBROW_WIDTH + 1) { - return 0; - } - - /* Init stats array */ - memset(stats, 0, sizeof (stats)); - - for (y = rect->y, x = rect->x; y < h && x < w;) { - - /* Scan sub-sections of the surface to determine how close the colors are - * to the previous. */ - for (d = 0; - d < h - y && d < w - x - GUAC_SURFACE_SMOOTHNESS_DETECT_SUBROW_WIDTH; - d++) { - - for (c = 0; c < 3; c++) { - unsigned int index = (y+d)*stride + (x+d)*4 + c; - left[c] = buffer[index] & 0xFF; - } - - for (dx = 1; dx <= GUAC_SURFACE_SMOOTHNESS_DETECT_SUBROW_WIDTH; dx++) { - - for (c = 0; c < 3; c++) { - unsigned int index = (y+d)*stride + (x+d+dx)*4 + c; - pix = buffer[index] & 0xFF; - stats[abs(pix - left[c])]++; - left[c] = pix; - } - ++pixels; - } - } - - /* Advance to next section */ - if (w > h) { - x += h; - y = rect->y; - } else { - x = rect->x; - y += w; - } - } - - if (pixels == 0) { - return 1; - } - - /* 95% smooth or more */ - if (stats[0] * 33 / pixels >= 95) { - return 1; - } - - unsigned int smoothness = 0; - for (c = 1; c < 8; c++) { - smoothness += stats[c] * (c * c); - if (stats[c] == 0 || stats[c] > stats[c-1] * 2) { - return 1; - } - } - for (; c < 256; c++) { - smoothness += stats[c] * (c * c); - } - smoothness /= (pixels * 3 - stats[0]); - - return smoothness <= GUAC_SURFACE_SMOOTHNESS_THRESHOLD; -} diff --git a/src/common/guac_surface_smoothness.h b/src/common/guac_surface_smoothness.h deleted file mode 100644 index 7bd15145..00000000 --- a/src/common/guac_surface_smoothness.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2015 Glyptodon LLC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef __GUAC_COMMON_SURFACE_SMOOTHNESS_H -#define __GUAC_COMMON_SURFACE_SMOOTHNESS_H - -#include "guac_surface.h" - -/** - * Returns the smoothness of an area on a surface. - * - * @param surface - * The surface on which the rectangle exists. - * - * @param rect - * The rectangle to check for smoothness. - * - * @return - * 1 if rectangle is smooth, zero if not. - */ -int guac_common_surface_rect_is_smooth(guac_common_surface* surface, - guac_common_rect* rect); - -#endif