Skip to content

Commit cb60903

Browse files
committed
Support TinyVG format decoding and rendering
Implemented support for decoding TinyVG format by parsing commands step-by-step. Each decoded command is executed using twin's path functions to render graphics. This enhances twin's capability to handle compact vector graphics . Ref: https://tinyvg.tech/download/specification.pdf Close #71
1 parent 16f7f9e commit cb60903

File tree

15 files changed

+1650
-15
lines changed

15 files changed

+1650
-15
lines changed

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ ifeq ($(CONFIG_LOADER_GIF), y)
8686
libtwin.a_files-y += src/image-gif.c
8787
endif
8888

89+
ifeq ($(CONFIG_LOADER_TVG), y)
90+
libtwin.a_files-y += src/image-tvg.c
91+
endif
92+
8993
# Applications
9094

9195
libapps.a_files-y := apps/dummy.c
@@ -96,6 +100,7 @@ libapps.a_files-$(CONFIG_DEMO_CALCULATOR) += apps/calc.c
96100
libapps.a_files-$(CONFIG_DEMO_LINE) += apps/line.c
97101
libapps.a_files-$(CONFIG_DEMO_SPLINE) += apps/spline.c
98102
libapps.a_files-$(CONFIG_DEMO_ANIMATION) += apps/animation.c
103+
libapps.a_files-$(CONFIG_DEMO_IMAGE) += apps/image.c
99104

100105
libapps.a_includes-y := include
101106

apps/apps_image.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Twin - A Tiny Window System
3+
* Copyright (c) 2024 National Cheng Kung University
4+
* All rights reserved.
5+
*/
6+
7+
#ifndef _APPS_IMAGE_H_
8+
#define _APPS_IMAGE_H_
9+
10+
#include <twin.h>
11+
12+
void apps_image_start(twin_screen_t *screen, const char *name, int x, int y);
13+
14+
#endif /* _APPS_ANIMATION_H_ */

apps/image.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Twin - A Tiny Window System
3+
* Copyright (c) 2024 National Cheng Kung University
4+
* All rights reserved.
5+
*/
6+
7+
#include <stdlib.h>
8+
9+
#include "twin_private.h"
10+
11+
#include "apps_image.h"
12+
13+
#define _apps_image_pixmap(image) ((image)->widget.window->pixmap)
14+
#define D(x) twin_double_to_fixed(x)
15+
#define ASSET_PATH "assets/"
16+
#define APP_WIDTH 400
17+
#define APP_HEIGHT 400
18+
typedef struct {
19+
twin_widget_t widget;
20+
twin_pixmap_t **pixes;
21+
int image_idx;
22+
} apps_image_t;
23+
24+
static const char *tvg_files[] = {
25+
/* https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ */
26+
ASSET_PATH "tiger.tvg",
27+
/* https://tinyvg.tech/img/chart.svg */
28+
ASSET_PATH "chart.tvg",
29+
/* https://freesvg.org/betrayed */
30+
ASSET_PATH "comic.tvg",
31+
/* https://github.com/PapirusDevelopmentTeam/papirus-icon-theme */
32+
ASSET_PATH "folder.tvg",
33+
/* https://materialdesignicons.com/ */
34+
ASSET_PATH "shield.tvg",
35+
/* https://tinyvg.tech/img/flowchart.png */
36+
ASSET_PATH "flowchart.tvg",
37+
};
38+
39+
static void _apps_image_paint(apps_image_t *img)
40+
{
41+
twin_operand_t srcop = {
42+
.source_kind = TWIN_PIXMAP,
43+
.u.pixmap = img->pixes[img->image_idx],
44+
};
45+
46+
twin_composite(_apps_image_pixmap(img), 0, 0, &srcop, 0, 0, NULL, 0, 0,
47+
TWIN_SOURCE, APP_WIDTH, APP_HEIGHT);
48+
}
49+
50+
static twin_dispatch_result_t _apps_image_dispatch(twin_widget_t *widget,
51+
twin_event_t *event)
52+
{
53+
apps_image_t *img = (apps_image_t *) widget;
54+
if (_twin_widget_dispatch(widget, event) == TwinDispatchDone)
55+
return TwinDispatchDone;
56+
switch (event->kind) {
57+
case TwinEventPaint:
58+
_apps_image_paint(img);
59+
break;
60+
default:
61+
break;
62+
}
63+
return TwinDispatchContinue;
64+
}
65+
66+
static void _apps_image_button_signal(maybe_unused twin_button_t *button,
67+
twin_button_signal_t signal,
68+
void *closure)
69+
{
70+
if (signal != TwinButtonSignalDown)
71+
return;
72+
73+
apps_image_t *img = closure;
74+
const int n = sizeof(tvg_files) / sizeof(tvg_files[0]);
75+
img->image_idx = img->image_idx == n - 1 ? 0 : img->image_idx + 1;
76+
if (!img->pixes[img->image_idx]) {
77+
twin_pixmap_t *pix = twin_tvg_to_pixmap_scale(
78+
tvg_files[img->image_idx], TWIN_ARGB32, APP_WIDTH, APP_HEIGHT);
79+
if (!pix)
80+
return;
81+
img->pixes[img->image_idx] = pix;
82+
}
83+
_twin_widget_queue_paint(&img->widget);
84+
}
85+
86+
static void _apps_image_init(apps_image_t *img,
87+
twin_box_t *parent,
88+
twin_dispatch_proc_t dispatch)
89+
{
90+
static twin_widget_layout_t preferred = {0, 0, 1, 1};
91+
preferred.height = parent->widget.window->screen->height * 3.0 / 4.0;
92+
_twin_widget_init(&img->widget, parent, 0, preferred, dispatch);
93+
img->image_idx = 0;
94+
img->pixes = calloc(sizeof(tvg_files), sizeof(twin_pixmap_t *));
95+
img->pixes[0] = twin_tvg_to_pixmap_scale(tvg_files[0], TWIN_ARGB32,
96+
APP_WIDTH, APP_HEIGHT);
97+
twin_button_t *button =
98+
twin_button_create(parent, "Next Image", 0xFF482722, D(10),
99+
TwinStyleBold | TwinStyleOblique);
100+
twin_widget_set(&button->label.widget, 0xFFFEE4CE);
101+
button->signal = _apps_image_button_signal;
102+
button->closure = img;
103+
button->label.widget.shape = TwinShapeRectangle;
104+
}
105+
106+
static apps_image_t *apps_image_create(twin_box_t *parent)
107+
{
108+
apps_image_t *img = malloc(sizeof(apps_image_t));
109+
110+
_apps_image_init(img, parent, _apps_image_dispatch);
111+
return img;
112+
}
113+
114+
void apps_image_start(twin_screen_t *screen, const char *name, int x, int y)
115+
{
116+
twin_toplevel_t *toplevel =
117+
twin_toplevel_create(screen, TWIN_ARGB32, TwinWindowApplication, x, y,
118+
APP_WIDTH, APP_HEIGHT, name);
119+
apps_image_t *img = apps_image_create(&toplevel->box);
120+
(void) img;
121+
twin_toplevel_show(toplevel);
122+
}

apps/main.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "apps_calc.h"
1818
#include "apps_clock.h"
1919
#include "apps_hello.h"
20+
#include "apps_image.h"
2021
#include "apps_line.h"
2122
#include "apps_multi.h"
2223
#include "apps_spline.h"
@@ -127,6 +128,9 @@ int main(void)
127128
apps_animation_start(tx->screen, "Viewer", ASSET_PATH "nyancat.gif", 20,
128129
20);
129130
#endif
131+
#if defined(CONFIG_DEMO_IMAGE)
132+
apps_image_start(tx->screen, "Viewer", 20, 20);
133+
#endif
130134

131135
twin_dispatch(tx);
132136

assets/chart.tvg

6.39 KB
Binary file not shown.

assets/comic.tvg

16.5 KB
Binary file not shown.

assets/flowchart.tvg

6.31 KB
Binary file not shown.

assets/folder.tvg

927 Bytes
Binary file not shown.

assets/shield.tvg

119 Bytes
Binary file not shown.

assets/tiger.tvg

26.9 KB
Binary file not shown.

configs/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ config LOADER_GIF
7171
bool "Enable GIF loader"
7272
default y
7373

74+
config LOADER_TinyVG
75+
bool "Enable TinyVG loader"
76+
default y
77+
7478
endmenu
7579

7680
menu "Demo Applications"
@@ -113,4 +117,9 @@ config DEMO_ANIMATION
113117
bool "Build animation demo"
114118
default y
115119
depends on DEMO_APPLICATIONS
120+
121+
config DEMO_IMAGE
122+
bool "Build image demo"
123+
default y
124+
depends on DEMO_APPLICATIONS
116125
endmenu

include/twin.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,15 @@ twin_work_t *twin_set_work(twin_work_proc_t work_proc,
11861186

11871187
void twin_clear_work(twin_work_t *work);
11881188

1189+
/*
1190+
* image-tvg.c
1191+
*/
1192+
1193+
twin_pixmap_t *twin_tvg_to_pixmap_scale(const char *filepath,
1194+
twin_format_t fmt,
1195+
twin_coord_t w,
1196+
twin_coord_t h);
1197+
11891198
/*
11901199
* backend
11911200
*/

0 commit comments

Comments
 (0)