-
Notifications
You must be signed in to change notification settings - Fork 1
/
image.c
202 lines (174 loc) · 4.99 KB
/
image.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#include "image.h"
#include "utils.h"
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
static void cairo_load(const char *path, int width, int height, double anchor_x,
double anchor_y, int mode, cairo_t *cr,
cairo_surface_t **frame) {
cairo_surface_t *img_surface = cairo_image_surface_create_from_png(path);
cairo_pattern_t *pattern = cairo_pattern_create_for_surface(img_surface);
int s_width = cairo_image_surface_get_width(img_surface);
int s_height = cairo_image_surface_get_height(img_surface);
cairo_matrix_t matrix;
cairo_matrix_init_identity(&matrix);
// scale to fill output
double scale_x = (double)width / (double)s_width;
double scale_y = (double)height / (double)s_height;
double scale;
switch (mode) {
case NONE:
scale = 1.0;
scale_x = scale;
scale_y = scale;
break;
case FIT:
scale = scale_x < scale_y ? scale_x : scale_y;
scale_x = scale;
scale_y = scale;
break;
case FILL:
scale = scale_x > scale_y ? scale_x : scale_y;
scale_x = scale;
scale_y = scale;
break;
case STRETCH:
break;
case TILE:
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
scale_x = 1;
scale_y = 1;
break;
default:
break;
}
double after_x = s_width * scale_x;
double after_y = s_height * scale_y;
// translate to center
int offset_x = ((double)width - after_x) * anchor_x;
int offset_y = ((double)height - after_y) * anchor_y;
cairo_matrix_scale(&matrix, 1 / scale_x, 1 / scale_y);
cairo_matrix_translate(&matrix, -offset_x, -offset_y);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_push_group(cr);
cairo_set_source(cr, pattern);
cairo_paint(cr);
cairo_surface_t *cache_surface =
cairo_surface_reference(cairo_get_group_target(cr));
*frame = cache_surface;
cairo_pop_group_to_source(cr);
cairo_pattern_destroy(pattern);
cairo_surface_destroy(img_surface);
}
int wayab_image_bootstrap(struct wayab_image *ptr, cairo_t *cr, int width,
int height, int bootstrap) {
char **paths = malloc(ptr->count * sizeof(char *));
DIR *dir = NULL;
dir = opendir(ptr->rule->dir);
if (dir == NULL) {
LOG("opendir failed\n");
goto error;
}
/* store all paths */
int n = 0;
struct dirent *de;
while ((de = readdir(dir)) != NULL) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) {
continue;
}
int len = strlen(ptr->rule->dir) + 1 + strlen(de->d_name) + 1;
paths[n] = malloc(len * sizeof(char));
strcpy(paths[n], ptr->rule->dir);
strcat(paths[n], "/");
strcat(paths[n], de->d_name);
++n;
}
closedir(dir);
/* sort in aplhabetical order */
for (int i = 0; i < ptr->count; i++) {
for (int j = i + 1; j < ptr->count; j++) {
if (strcmp(paths[i], paths[j]) > 0) {
char temp[256];
strcpy(temp, paths[i]);
strcpy(paths[i], paths[j]);
strcpy(paths[j], temp);
}
}
}
if (ptr->count == 0)
goto error;
// after the first frame is loaded, we spawn a thread to do the rest to reduce
// blank screen time
if (bootstrap) {
int start_frame = 0;
LOG("Bootstrap using %s\n", paths[start_frame]);
cairo_load(paths[start_frame], width, height, ptr->rule->anchor_x,
ptr->rule->anchor_y, ptr->rule->resize, cr,
&ptr->surfaces[start_frame]);
} else {
for (int i = 0; i < ptr->count; ++i) {
LOG("Loading %s\n", paths[i]);
cairo_load(paths[i], width, height, ptr->rule->anchor_x,
ptr->rule->anchor_y, ptr->rule->resize, cr, &ptr->surfaces[i]);
}
}
return 0;
error:
if (dir) {
closedir(dir);
}
if (paths) {
for (int i = 0; i < ptr->count; ++i) {
if (paths[i])
free(paths[i]);
}
free(paths);
}
return -1;
}
struct wayab_image *wayab_image_new(const struct wayab_rule *rule, cairo_t *cr,
int width, int height) {
struct wayab_image *ptr = calloc(1, sizeof(struct wayab_image));
ptr->rule = rule;
ptr->count = 0;
struct dirent *de;
DIR *dir;
dir = opendir(rule->dir);
if (dir == NULL) {
LOG("opendir failed\n");
goto error;
}
/* count frames */
while ((de = readdir(dir)) != NULL) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) {
continue;
}
++ptr->count;
}
closedir(dir);
/* init surface */
ptr->surfaces = malloc(ptr->count * sizeof(cairo_surface_t *));
for (int i = 0; i < ptr->count; ++i)
ptr->surfaces[i] = NULL;
if (wayab_image_bootstrap(ptr, cr, width, height, 1))
goto error;
return ptr;
error:
if (dir)
closedir(dir);
if (ptr)
wayab_image_destroy(ptr);
return NULL;
}
int wayab_image_destroy(struct wayab_image *ptr) {
if (ptr->surfaces) {
for (int i = 0; i < ptr->count; ++i) {
cairo_surface_destroy(ptr->surfaces[i]);
}
free(ptr->surfaces);
}
if (ptr)
free(ptr);
return 0;
}
void wayab_image_next_frame(struct wayab_image *ptr, uint64_t counter) {}