Skip to content

Commit bbfb9af

Browse files
committed
perf(graph): improve image scaling quality (issue #39)
1 parent d75b4ca commit bbfb9af

File tree

4 files changed

+181
-2
lines changed

4 files changed

+181
-2
lines changed

include/LCUI/graph.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,28 @@ LCUI_BEGIN_HEADER
9696
if( (G)->color_type == LCUI_COLOR_TYPE_ARGB ) { \
9797
(G)->argb[(G)->width*(Y)+(X)] = (C); \
9898
} else { \
99-
(G)->bytes[(G)->bytes_per_row*(Y)+(X)*3] = (C).value>>8; \
99+
(G)->bytes[(G)->bytes_per_row*(Y)+(X)*3] = (C).b; \
100+
(G)->bytes[(G)->bytes_per_row*(Y)+(X)*3+1] = (C).g; \
101+
(G)->bytes[(G)->bytes_per_row*(Y)+(X)*3+2] = (C).r; \
100102
}
101103

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

107+
#define Graph_GetPixel(G, X, Y, C) \
108+
if( (G)->color_type == LCUI_COLOR_TYPE_ARGB ) { \
109+
(C) = (G)->argb[(G)->width*((Y)%(G)->height)+((X)%(G)->width)]; \
110+
} else { \
111+
(C).value = \
112+
(G)->bytes[(G)->bytes_per_row*((Y)%(G)->height) \
113+
+((X)%(G)->width)*(G)->bytes_per_pixel]<<0 \
114+
| (G)->bytes[(G)->bytes_per_row*((Y)%(G)->height) \
115+
+((X)%(G)->width)*(G)->bytes_per_pixel+1]<<8 \
116+
| (G)->bytes[(G)->bytes_per_row*((Y)%(G)->height) \
117+
+((X)%(G)->width)*(G)->bytes_per_pixel+2]<<16 \
118+
| 0xff<<24; \
119+
}
120+
105121
LCUI_API void Graph_PrintInfo(LCUI_Graph *graph);
106122

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

184+
LCUI_API int Graph_ZoomBilinear(const LCUI_Graph *graph, LCUI_Graph *buff,
185+
LCUI_BOOL keep_scale, int width, int height);
186+
168187
LCUI_API int Graph_Cut(const LCUI_Graph *graph, LCUI_Rect rect,
169188
LCUI_Graph *buff);
170189

src/graph.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,13 @@ static int Graph_FillRectARGB(LCUI_Graph *graph, LCUI_Color color,
697697
return 0;
698698
}
699699

700+
static uchar_t Graph_BilinearResamplingCore(uchar_t a, uchar_t b, uchar_t c,
701+
uchar_t d, float dx, float dy)
702+
{
703+
return a * (1 - dx) * (1 - dy) + b * (dx) * (1 - dy) +
704+
c * (dy) * (1 - dx) + d * (dx * dy);
705+
}
706+
700707
/*-------------------------------- End ARGB --------------------------------*/
701708

702709
int Graph_SetColorType(LCUI_Graph *graph, int color_type)
@@ -1023,6 +1030,91 @@ int Graph_Zoom(const LCUI_Graph *graph, LCUI_Graph *buff, LCUI_BOOL keep_scale,
10231030
return 0;
10241031
}
10251032

1033+
int Graph_ZoomBilinear(const LCUI_Graph *graph, LCUI_Graph *buff,
1034+
LCUI_BOOL keep_scale, int width, int height)
1035+
{
1036+
LCUI_Rect rect;
1037+
LCUI_ARGB a, b, c, d, t_color;
1038+
int x, y, i, j;
1039+
float x_diff, y_diff;
1040+
double scale_x = 0.0, scale_y = 0.0;
1041+
1042+
if (graph->color_type != LCUI_COLOR_TYPE_RGB &&
1043+
graph->color_type != LCUI_COLOR_TYPE_ARGB) {
1044+
/* fall back to nearest scaling */
1045+
LOG("[graph] unable to perform bilinear scaling, "
1046+
"fallback...\n");
1047+
return Graph_Zoom(graph, buff, keep_scale, width, height);
1048+
}
1049+
if (!Graph_IsValid(graph) || (width <= 0 && height <= 0)) {
1050+
return -1;
1051+
}
1052+
/* 获取引用的有效区域,以及指向引用的对象的指针 */
1053+
Graph_GetValidRect(graph, &rect);
1054+
graph = Graph_GetQuote(graph);
1055+
if (width > 0) {
1056+
scale_x = 1.0 * rect.width / width;
1057+
}
1058+
if (height > 0) {
1059+
scale_y = 1.0 * rect.height / height;
1060+
}
1061+
if (width <= 0) {
1062+
scale_x = scale_y;
1063+
width = (int)(0.5 + 1.0 * graph->width / scale_x);
1064+
}
1065+
if (height <= 0) {
1066+
scale_y = scale_x;
1067+
height = (int)(0.5 + 1.0 * graph->height / scale_y);
1068+
}
1069+
/* 如果保持宽高比 */
1070+
if (keep_scale) {
1071+
if (scale_x < scale_y) {
1072+
scale_y = scale_x;
1073+
} else {
1074+
scale_x = scale_y;
1075+
}
1076+
}
1077+
buff->color_type = graph->color_type;
1078+
if (Graph_Create(buff, width, height) < 0) {
1079+
return -2;
1080+
}
1081+
for (i = 0; i < height; i++) {
1082+
for (j = 0; j < width; j++) {
1083+
/*
1084+
* Qmn = (m, n).
1085+
* Qxy1 = (x2 - x) / (x2 - x1) * Q11 + (x - x1) / (x2
1086+
* - x1) * Q21
1087+
* Qxy2 = (x2 - x) / (x2 - x1) * Q12 + (x - x1) / (x2
1088+
* - x1) * Q22
1089+
* Qxy = (y2 - y) / (y2 - y1) * Qxy1 + (y - y1) / (y2 -
1090+
* y1) * Qxy2
1091+
*/
1092+
x = (int)(scale_x * j);
1093+
y = (int)(scale_y * i);
1094+
x_diff = (scale_x * j) - x;
1095+
y_diff = (scale_y * i) - y;
1096+
Graph_GetPixel(graph, x + rect.x + 0, y + rect.y + 0,
1097+
a);
1098+
Graph_GetPixel(graph, x + rect.x + 1, y + rect.y + 0,
1099+
b);
1100+
Graph_GetPixel(graph, x + rect.x + 0, y + rect.y + 1,
1101+
c);
1102+
Graph_GetPixel(graph, x + rect.x + 1, y + rect.y + 1,
1103+
d);
1104+
t_color.b = Graph_BilinearResamplingCore(
1105+
a.b, b.b, c.b, d.b, x_diff, y_diff);
1106+
t_color.g = Graph_BilinearResamplingCore(
1107+
a.g, b.g, c.g, d.g, x_diff, y_diff);
1108+
t_color.r = Graph_BilinearResamplingCore(
1109+
a.r, b.r, c.r, d.r, x_diff, y_diff);
1110+
t_color.a = Graph_BilinearResamplingCore(
1111+
a.a, b.a, c.a, d.a, x_diff, y_diff);
1112+
Graph_SetPixel(buff, j, i, t_color);
1113+
}
1114+
}
1115+
return 0;
1116+
}
1117+
10261118
int Graph_Cut(const LCUI_Graph *graph, LCUI_Rect rect, LCUI_Graph *buff)
10271119
{
10281120
if (!Graph_IsValid(graph)) {

test/Makefile.am

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ AM_CFLAGS = -I$(top_builddir)/include $(CODE_COVERAGE_CFLAGS)
55
noinst_PROGRAMS = helloworld test test_charset test_touch test_char_render \
66
test_string_render test_widget_render test_widget_layout test_widget_rect \
77
test_widget_flex_layout test_widget_inline_block_layout test_scaling_support \
8-
test_scrollbar test_textview_resize
8+
test_scrollbar test_textview_resize test_image_scaling_bench
99

1010
##指定测试程序的源码文件
1111
helloworld_SOURCES = helloworld.c
@@ -61,4 +61,6 @@ test_scaling_support_LDADD = $(top_builddir)/src/libLCUI.la
6161
test_scrollbar_SOURCES = test_scrollbar.c
6262
test_scrollbar_LDADD = $(top_builddir)/src/libLCUI.la
6363

64+
test_image_scaling_bench_LDADD = $(top_builddir)/src/libLCUI.la
65+
6466
@CODE_COVERAGE_RULES@

test/test_image_scaling_bench.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include <stdlib.h>
2+
#include <stdbool.h>
3+
#include <stdio.h>
4+
#include <LCUI_Build.h>
5+
#include <LCUI/LCUI.h>
6+
#include <LCUI/timer.h>
7+
#include <LCUI/display.h>
8+
#include <LCUI/gui/widget.h>
9+
#include <LCUI/gui/builder.h>
10+
#include <LCUI/graph.h>
11+
#include <LCUI/image.h>
12+
13+
#ifdef LCUI_BUILD_IN_WIN32
14+
static void LoggerHandler(const char *str)
15+
{
16+
OutputDebugStringA(str);
17+
}
18+
19+
static void LoggerHandlerW(const wchar_t *str)
20+
{
21+
OutputDebugStringW(str);
22+
}
23+
#endif
24+
25+
int main(int argc, char **argv)
26+
{
27+
int i;
28+
int64_t t0, t1, t2;
29+
int resx[] = { 480, 960, 1280, 1366, 1920, 2560, 3840 }, resy;
30+
char s_res[32], s_t0[32], s_t1[32];
31+
32+
LCUI_Graph g_src, g_dst;
33+
LCUI_Color t_color;
34+
35+
#ifdef LCUI_BUILD_IN_WIN32
36+
Logger_SetHandler(LoggerHandler);
37+
Logger_SetHandlerW(LoggerHandlerW);
38+
#endif
39+
Graph_Init(&g_src);
40+
g_src.color_type = LCUI_COLOR_TYPE_ARGB;
41+
if (Graph_Create(&g_src, 960, 540) < 0) {
42+
return -2;
43+
}
44+
t_color.value = 0xffaa5500;
45+
Graph_FillRect(&g_src, t_color, NULL, false);
46+
LOG("%-20s%-20s%s\n", "image size\\method", "Graph_Zoom()",
47+
"Graph_ZoomBilinear()");
48+
for (i = 0; i < sizeof(resx) / sizeof(int); i++) {
49+
resy = resx[i] * 9 / 16;
50+
t0 = LCUI_GetTime();
51+
Graph_Init(&g_dst);
52+
Graph_Zoom(&g_src, &g_dst, false, resx[i], resy);
53+
Graph_Free(&g_dst);
54+
t1 = LCUI_GetTime();
55+
Graph_Init(&g_dst);
56+
Graph_ZoomBilinear(&g_src, &g_dst, false, resx[i], resy);
57+
Graph_Free(&g_dst);
58+
t2 = LCUI_GetTime();
59+
sprintf(s_res, "%dx%d", resx[i], resy);
60+
sprintf(s_t0, "%ldms", t1 - t0);
61+
sprintf(s_t1, "%ldms", t2 - t1);
62+
LOG("%-20s%-20s%-20s\n", s_res, s_t0, s_t1);
63+
}
64+
Graph_Free(&g_src);
65+
return 0;
66+
}

0 commit comments

Comments
 (0)