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
|
// 27 june 2016
#include "uipriv_unix.h"
struct uiImage {
double width;
double height;
GPtrArray *images;
};
static void freeImageRep(gpointer item)
{
cairo_surface_t *cs = (cairo_surface_t *) item;
unsigned char *buf;
buf = cairo_image_surface_get_data(cs);
cairo_surface_destroy(cs);
uiFree(buf);
}
uiImage *uiNewImage(double width, double height)
{
uiImage *i;
i = uiNew(uiImage);
i->width = width;
i->height = height;
i->images = g_ptr_array_new_with_free_func(freeImageRep);
return i;
}
void uiFreeImage(uiImage *i)
{
g_ptr_array_free(i->images, TRUE);
uiFree(i);
}
void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride)
{
cairo_surface_t *cs;
unsigned char *buf, *p;
uint8_t *src = (uint8_t *) pixels;
int cstride;
int y;
cstride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixelWidth);
buf = (unsigned char *) uiAlloc((cstride * pixelHeight * 4) * sizeof (unsigned char), "unsigned char[]");
p = buf;
for (y = 0; y < pixelStride * pixelHeight; y += pixelStride) {
memmove(p, src + y, cstride);
p += cstride;
}
cs = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32,
pixelWidth, pixelHeight,
cstride);
if (cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS)
/* TODO */;
cairo_surface_flush(cs);
g_ptr_array_add(i->images, cs);
}
struct matcher {
cairo_surface_t *best;
int distX;
int distY;
int targetX;
int targetY;
gboolean foundLarger;
};
// TODO is this the right algorithm?
static void match(gpointer surface, gpointer data)
{
cairo_surface_t *cs = (cairo_surface_t *) surface;
struct matcher *m = (struct matcher *) data;
int x, y;
int x2, y2;
x = cairo_image_surface_get_width(cs);
y = cairo_image_surface_get_height(cs);
if (m->best == NULL)
goto writeMatch;
if (x < m->targetX && y < m->targetY)
if (m->foundLarger)
// always prefer larger ones
return;
if (x >= m->targetX && y >= m->targetY && !m->foundLarger)
// we set foundLarger below
goto writeMatch;
x2 = abs(m->targetX - x);
y2 = abs(m->targetY - y);
if (x2 < m->distX && y2 < m->distY)
goto writeMatch;
// TODO weight one dimension? threshhold?
return;
writeMatch:
// must set this here too; otherwise the first image will never have ths set
if (x >= m->targetX && y >= m->targetY && !m->foundLarger)
m->foundLarger = TRUE;
m->best = cs;
m->distX = abs(m->targetX - x);
m->distY = abs(m->targetY - y);
}
cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w)
{
struct matcher m;
m.best = NULL;
m.distX = G_MAXINT;
m.distY = G_MAXINT;
m.targetX = i->width * gtk_widget_get_scale_factor(w);
m.targetY = i->height * gtk_widget_get_scale_factor(w);
m.foundLarger = FALSE;
g_ptr_array_foreach(i->images, match, &m);
return m.best;
}
|