Skip to content

Commit

Permalink
perf(graph): improve image scaling quality (issue lc-soft#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
ReinForce-II committed Aug 20, 2018
1 parent d75b4ca commit b2346be
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 2 deletions.
21 changes: 20 additions & 1 deletion include/LCUI/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,28 @@ LCUI_BEGIN_HEADER
if( (G)->color_type == LCUI_COLOR_TYPE_ARGB ) { \
(G)->argb[(G)->width*(Y)+(X)] = (C); \
} else { \
(G)->bytes[(G)->bytes_per_row*(Y)+(X)*3] = (C).value>>8; \
(G)->bytes[(G)->bytes_per_row*(Y)+(X)*3] = (C).b; \
(G)->bytes[(G)->bytes_per_row*(Y)+(X)*3+1] = (C).g; \
(G)->bytes[(G)->bytes_per_row*(Y)+(X)*3+2] = (C).r; \
}

#define Graph_SetPixelAlpha(G, X, Y, A)\
(G)->argb[(G)->width*(Y)+(X)].alpha = (A)

#define Graph_GetPixel(G, X, Y, C) \
if( (G)->color_type == LCUI_COLOR_TYPE_ARGB ) { \
(C) = (G)->argb[(G)->width*((Y)%(G)->height)+((X)%(G)->width)]; \
} else { \
(C).value = \
(G)->bytes[(G)->bytes_per_row*((Y)%(G)->height) \
+((X)%(G)->width)*(G)->bytes_per_pixel]<<0 \
| (G)->bytes[(G)->bytes_per_row*((Y)%(G)->height) \
+((X)%(G)->width)*(G)->bytes_per_pixel+1]<<8 \
| (G)->bytes[(G)->bytes_per_row*((Y)%(G)->height) \
+((X)%(G)->width)*(G)->bytes_per_pixel+2]<<16 \
| 0xff<<24; \
}

LCUI_API void Graph_PrintInfo(LCUI_Graph *graph);

LCUI_API void Graph_Init(LCUI_Graph *graph);
Expand Down Expand Up @@ -165,6 +181,9 @@ LCUI_API int Graph_SetBlueBits(LCUI_Graph *graph, uchar_t *b, size_t size);
LCUI_API int Graph_Zoom(const LCUI_Graph *graph, LCUI_Graph *buff,
LCUI_BOOL keep_scale, int width, int height);

LCUI_API int Graph_ZoomBilinear(const LCUI_Graph *graph, LCUI_Graph *buff,
LCUI_BOOL keep_scale, int width, int height);

LCUI_API int Graph_Cut(const LCUI_Graph *graph, LCUI_Rect rect,
LCUI_Graph *buff);

Expand Down
91 changes: 91 additions & 0 deletions src/graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,13 @@ static int Graph_FillRectARGB(LCUI_Graph *graph, LCUI_Color color,
return 0;
}

static uchar_t Graph_BilinearResamplingCore(uchar_t a, uchar_t b, uchar_t c,
uchar_t d, float dx, float dy)
{
return a * (1 - dx) * (1 - dy) + b * (dx) * (1 - dy) +
c * (dy) * (1 - dx) + d * (dx * dy);
}

/*-------------------------------- End ARGB --------------------------------*/

int Graph_SetColorType(LCUI_Graph *graph, int color_type)
Expand Down Expand Up @@ -1023,6 +1030,90 @@ int Graph_Zoom(const LCUI_Graph *graph, LCUI_Graph *buff, LCUI_BOOL keep_scale,
return 0;
}

int Graph_ZoomBilinear(const LCUI_Graph *graph, LCUI_Graph *buff,
LCUI_BOOL keep_scale, int width, int height)
{
if (graph->color_type != LCUI_COLOR_TYPE_RGB &&
graph->color_type != LCUI_COLOR_TYPE_ARGB) {
/* fall back to nearest scaling */
LOG("[graph] unable to perform bilinear scaling, "
"fallback...\n");
return Graph_Zoom(graph, buff, keep_scale, width, height);
}
LCUI_Rect rect;
LCUI_ARGB a, b, c, d, t_color;
int x, y, i, j;
float x_diff, y_diff;
double scale_x = 0.0, scale_y = 0.0;
if (!Graph_IsValid(graph) || (width <= 0 && height <= 0)) {
return -1;
}
/* 获取引用的有效区域,以及指向引用的对象的指针 */
Graph_GetValidRect(graph, &rect);
graph = Graph_GetQuote(graph);
if (width > 0) {
scale_x = 1.0 * rect.width / width;
}
if (height > 0) {
scale_y = 1.0 * rect.height / height;
}
if (width <= 0) {
scale_x = scale_y;
width = (int)(0.5 + 1.0 * graph->width / scale_x);
}
if (height <= 0) {
scale_y = scale_x;
height = (int)(0.5 + 1.0 * graph->height / scale_y);
}
/* 如果保持宽高比 */
if (keep_scale) {
if (scale_x < scale_y) {
scale_y = scale_x;
} else {
scale_x = scale_y;
}
}
buff->color_type = graph->color_type;
if (Graph_Create(buff, width, height) < 0) {
return -2;
}
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
/*
* Qmn = (m, n).
* Qxy1 = (x2 - x) / (x2 - x1) * Q11 + (x - x1) / (x2
* - x1) * Q21
* Qxy2 = (x2 - x) / (x2 - x1) * Q12 + (x - x1) / (x2
* - x1) * Q22
* Qxy = (y2 - y) / (y2 - y1) * Qxy1 + (y - y1) / (y2 -
* y1) * Qxy2
*/
x = (int)(scale_x * j);
y = (int)(scale_y * i);
x_diff = (scale_x * j) - x;
y_diff = (scale_y * i) - y;
Graph_GetPixel(graph, x + rect.x + 0, y + rect.y + 0,
a);
Graph_GetPixel(graph, x + rect.x + 1, y + rect.y + 0,
b);
Graph_GetPixel(graph, x + rect.x + 0, y + rect.y + 1,
c);
Graph_GetPixel(graph, x + rect.x + 1, y + rect.y + 1,
d);
t_color.b = Graph_BilinearResamplingCore(
a.b, b.b, c.b, d.b, x_diff, y_diff);
t_color.g = Graph_BilinearResamplingCore(
a.g, b.g, c.g, d.g, x_diff, y_diff);
t_color.r = Graph_BilinearResamplingCore(
a.r, b.r, c.r, d.r, x_diff, y_diff);
t_color.a = Graph_BilinearResamplingCore(
a.a, b.a, c.a, d.a, x_diff, y_diff);
Graph_SetPixel(buff, j, i, t_color);
}
}
return 0;
}

int Graph_Cut(const LCUI_Graph *graph, LCUI_Rect rect, LCUI_Graph *buff)
{
if (!Graph_IsValid(graph)) {
Expand Down
2 changes: 1 addition & 1 deletion test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ AM_CFLAGS = -I$(top_builddir)/include $(CODE_COVERAGE_CFLAGS)
noinst_PROGRAMS = helloworld test test_charset test_touch test_char_render \
test_string_render test_widget_render test_widget_layout test_widget_rect \
test_widget_flex_layout test_widget_inline_block_layout test_scaling_support \
test_scrollbar test_textview_resize
test_scrollbar test_textview_resize test_image_scaling_bench

##指定测试程序的源码文件
helloworld_SOURCES = helloworld.c
Expand Down
64 changes: 64 additions & 0 deletions test/test_image_scaling_bench.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <LCUI_Build.h>
#include <LCUI/LCUI.h>
#include <LCUI/timer.h>
#include <LCUI/display.h>
#include <LCUI/gui/widget.h>
#include <LCUI/gui/builder.h>
#include <LCUI/graph.h>
#include <LCUI/image.h>

#ifdef LCUI_BUILD_IN_WIN32
static void LoggerHandler(const char *str)
{
OutputDebugStringA(str);
}

static void LoggerHandlerW(const wchar_t *str)
{
OutputDebugStringW(str);
}
#endif

int main(int argc, char **argv)
{
LCUI_Graph g_src, g_dst;
LCUI_Color t_color;
int i;
int64_t t0, t1, t2;
int resx[] = { 480, 960, 1280, 1366, 1920, 2560, 3840 }, resy;
char s_res[32], s_t0[32], s_t1[32];
#ifdef LCUI_BUILD_IN_WIN32
Logger_SetHandler(LoggerHandler);
Logger_SetHandlerW(LoggerHandlerW);
#endif
Graph_Init(&g_src);
g_src.color_type = LCUI_COLOR_TYPE_ARGB;
if (Graph_Create(&g_src, 960, 540) < 0) {
return -2;
}
t_color.value = 0xffaa5500;
Graph_FillRect(&g_src, t_color, NULL, false);
LOG("%-20s%-20s%s\n", "image size\\method", "Graph_Zoom()",
"Graph_ZoomBilinear()");
for (i = 0; i < sizeof(resx) / sizeof(int); i++) {
resy = resx[i] * 9 / 16;
t0 = LCUI_GetTime();
Graph_Init(&g_dst);
Graph_Zoom(&g_src, &g_dst, false, resx[i], resy);
Graph_Free(&g_dst);
t1 = LCUI_GetTime();
Graph_Init(&g_dst);
Graph_ZoomBilinear(&g_src, &g_dst, false, resx[i], resy);
Graph_Free(&g_dst);
t2 = LCUI_GetTime();
sprintf(s_res, "%dx%d", resx[i], resy);
sprintf(s_t0, "%ldms", t1 - t0);
sprintf(s_t1, "%ldms", t2 - t1);
LOG("%-20s%-20s%-20s\n", s_res, s_t0, s_t1);
}
Graph_Free(&g_src);
return 0;
}

0 comments on commit b2346be

Please sign in to comment.