GUAC-240: Approximate whether images will compress well with JPEG vs. PNG.
This commit is contained in:
parent
f7cb3d56e9
commit
b56afd8bb8
@ -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 \
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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
|
|
Loading…
Reference in New Issue
Block a user