GUAC-240: Approximate whether images will compress well with JPEG vs. PNG.

This commit is contained in:
Michael Jumper 2015-08-17 06:29:30 -07:00
parent f7cb3d56e9
commit b56afd8bb8
4 changed files with 68 additions and 207 deletions

View File

@ -35,8 +35,7 @@ noinst_HEADERS = \
guac_pointer_cursor.h \ guac_pointer_cursor.h \
guac_rect.h \ guac_rect.h \
guac_string.h \ guac_string.h \
guac_surface.h \ guac_surface.h
guac_surface_smoothness.h
libguac_common_la_SOURCES = \ libguac_common_la_SOURCES = \
guac_io.c \ guac_io.c \
@ -48,8 +47,7 @@ libguac_common_la_SOURCES = \
guac_pointer_cursor.c \ guac_pointer_cursor.c \
guac_rect.c \ guac_rect.c \
guac_string.c \ guac_string.c \
guac_surface.c \ guac_surface.c
guac_surface_smoothness.c
libguac_common_la_CFLAGS = \ libguac_common_la_CFLAGS = \
-Werror -Wall -pedantic \ -Werror -Wall -pedantic \

View File

@ -23,7 +23,6 @@
#include "config.h" #include "config.h"
#include "guac_rect.h" #include "guac_rect.h"
#include "guac_surface.h" #include "guac_surface.h"
#include "guac_surface_smoothness.h"
#include <cairo/cairo.h> #include <cairo/cairo.h>
#include <guacamole/client.h> #include <guacamole/client.h>
@ -313,6 +312,70 @@ static unsigned int __guac_common_surface_calculate_framerate(
return 0; 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); int framerate = __guac_common_surface_calculate_framerate(surface, rect);
/* JPEG is preferred if framerate is high enough */ /* 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;
} }

View File

@ -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 <corentin.chary@gmail.com>
*
* 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 <stdlib.h>
#include <stdint.h>
#include <string.h>
/**
* 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;
}

View File

@ -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