xref: /inferno-os/libtk/image.c (revision d0e1d143ef6f03c75c008c7ec648859dd260cbab)
1 #include "lib9.h"
2 #include <kernel.h>
3 #include "draw.h"
4 #include "tk.h"
5 
6 #define	O(t, e)		((long)(&((t*)0)->e))
7 
8 char*	tkimgbmcreate(TkTop*, char*, int, char**);
9 char*	tkimgbmdel(TkImg*);
10 void	tkimgbmfree(TkImg*);
11 
12 static Rectangle huger = { -1000000, -1000000, 1000000, 1000000 };
13 
14 typedef struct TkImgtype TkImgtype;
15 struct TkImgtype
16 {
17 	char*	type;
18 	char*	(*create)(TkTop*, char*, int, char**);
19 	char*	(*delete)(TkImg*);
20 	void	(*destroy)(TkImg*);
21 } tkimgopts[] =
22 {
23 	"bitmap",	tkimgbmcreate,		tkimgbmdel, 	tkimgbmfree,
24 	nil,
25 };
26 
27 typedef struct Imgargs Imgargs;
28 struct Imgargs {
29 	Image*	fgimg;
30 	Image*	maskimg;
31 };
32 
33 TkImg*
34 tkname2img(TkTop *t, char *name)
35 {
36 	TkImg *tki;
37 
38 	for(tki = t->imgs; tki; tki = tki->link)
39 		if((tki->name != nil) && strcmp(tki->name->name, name) == 0)
40 			return tki;
41 
42 	return nil;
43 }
44 
45 TkOption
46 bitopt[] =
47 {
48 	"file",		OPTbmap,	O(Imgargs, fgimg),	nil,
49 	"maskfile",	OPTbmap,	O(Imgargs, maskimg),	nil,
50 	nil
51 };
52 
53 void
54 tksizeimage(Tk *tk, TkImg *tki)
55 {
56 	int dx, dy, repack;
57 
58 	dx = 0;
59 	dy = 0;
60 	if(tki->img != nil) {
61 		dx = Dx(tki->img->r);
62 		dy = Dy(tki->img->r);
63 	}
64 	repack = 0;
65 	if(tki->ref > 1 && (tki->w != dx || tki->h != dy))
66 		repack = 1;
67 	tki->w = dx;
68 	tki->h = dy;
69 
70 	if(repack) {
71 		tkpackqit(tk);
72 		tkrunpack(tk->env->top);
73 	}
74 }
75 
76 char*
77 tkimgbmcreate(TkTop *t, char *arg, int type, char **ret)
78 {
79 	TkName *names;
80 	TkImg *tki, *f;
81 	TkOptab tko[2];
82 	char buf[32];
83 	static int id;
84 	char *e = nil;
85 	Imgargs iargs;
86 	Rectangle r;
87 	Display *d;
88 	int chan;
89 	int locked;
90 
91 	d = t->display;
92 	locked = 0;
93 
94 	tki = malloc(sizeof(TkImg));
95 	if(tki == nil)
96 		return TkNomem;
97 
98 	tki->env = tkdefaultenv(t);
99 	if(tki->env == nil)
100 		goto err;
101 	tki->type = type;
102 	tki->ref = 1;
103 	tki->top = t;
104 
105 	iargs.fgimg = nil;
106 	iargs.maskimg = nil;
107 
108 	tko[0].ptr = &iargs;
109 	tko[0].optab = bitopt;
110 	tko[1].ptr = nil;
111 
112 	names = nil;
113 	e = tkparse(t, arg, tko, &names);
114 	if(e != nil)
115 		goto err;
116 
117 	if (iargs.fgimg == nil && iargs.maskimg != nil) {
118 		locked = lockdisplay(d);
119 		r = Rect(0, 0, Dx(iargs.maskimg->r), Dy(iargs.maskimg->r));
120 		tki->img = allocimage(d, r, CHAN2(CAlpha, 8, CGrey, 8), 0, DTransparent);
121 		if (tki->img != nil)
122 			draw(tki->img, r, nil, iargs.maskimg, iargs.maskimg->r.min);
123 		freeimage(iargs.maskimg);
124 
125 	} else if (iargs.fgimg != nil && iargs.maskimg != nil) {
126 		locked = lockdisplay(d);
127 		r = Rect(0, 0, Dx(iargs.fgimg->r), Dy(iargs.fgimg->r));
128 		if (tkchanhastype(iargs.fgimg->chan, CGrey))
129 			chan = CHAN2(CAlpha, 8, CGrey, 8);
130 		else
131 			chan = RGBA32;
132 		tki->img = allocimage(d, r, chan, 0, DTransparent);
133 		if (tki->img != nil)
134 			draw(tki->img, r, iargs.fgimg, iargs.maskimg, iargs.fgimg->r.min);
135 		freeimage(iargs.fgimg);
136 		freeimage(iargs.maskimg);
137 	} else {
138 		tki->img = iargs.fgimg;
139 	}
140 	if (locked)
141 		unlockdisplay(d);
142 
143 	if(names == nil) {
144 		sprint(buf, "image%d", id++);
145 		tki->name = tkmkname(buf);
146 		if(tki->name == nil)
147 			goto err;
148 	}
149 	else {
150 		/* XXX should mark as dirty any widgets using the named
151 		 * image - some notification scheme needs putting in place
152 		 */
153 		tki->name = names;
154 		tkfreename(names->link);
155 		names->link = nil;
156 	}
157 
158 	tksizeimage(t->root, tki);
159 
160 	if (tki->name != nil) {
161 		f = tkname2img(t, tki->name->name);
162 		if(f != nil)
163 			tkimgopts[f->type].delete(f);
164 	}
165 
166 	tki->link = t->imgs;
167 	t->imgs = tki;
168 
169 	if (tki->name != nil) {
170 		e = tkvalue(ret, "%s", tki->name->name);
171 		if(e == nil)
172 			return nil;
173 	}
174 err:
175 	tkputenv(tki->env);
176 	if(tki->img != nil) {
177 		locked = lockdisplay(d);
178 		freeimage(tki->img);
179 		if (locked)
180 			unlockdisplay(d);
181 	}
182 	tkfreename(tki->name);
183 	free(tki);
184 	return e != nil ? e : TkNomem;
185 }
186 
187 char*
188 tkimgbmdel(TkImg *tki)
189 {
190 	TkImg **l, *f;
191 
192 	l = &tki->top->imgs;
193 	for(f = *l; f; f = f->link) {
194 		if(f == tki) {
195 			*l = tki->link;
196 			tkimgput(tki);
197 			return nil;
198 		}
199 		l = &f->link;
200 	}
201 	return TkBadvl;
202 }
203 
204 void
205 tkimgbmfree(TkImg *tki)
206 {
207 	int locked;
208 	Display *d;
209 
210 	d = tki->top->display;
211 	locked = lockdisplay(d);
212 	freeimage(tki->img);
213 	if(locked)
214 		unlockdisplay(d);
215 
216 	free(tki->cursor);
217 	tkfreename(tki->name);
218 	tkputenv(tki->env);
219 
220 	free(tki);
221 }
222 
223 char*
224 tkimage(TkTop *t, char *arg, char **ret)
225 {
226 	int i;
227 	TkImg *tkim;
228 	char *fmt, *e, *buf, *cmd;
229 
230 	/* Note - could actually allocate buf and cmd in one buffer - DBK */
231 	buf = mallocz(Tkmaxitem, 0);
232 	if(buf == nil)
233 		return TkNomem;
234 	cmd = mallocz(Tkminitem, 0);
235 	if(cmd == nil) {
236 		free(buf);
237 		return TkNomem;
238 	}
239 
240 	arg = tkword(t, arg, cmd, cmd+Tkminitem, nil);
241 	if(strcmp(cmd, "create") == 0) {
242 		arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
243 		for(i = 0; tkimgopts[i].type != nil; i++)
244 			if(strcmp(buf, tkimgopts[i].type) == 0) {
245 				e = tkimgopts[i].create(t, arg, i, ret);
246 				goto ret;
247 			}
248 		e = TkBadvl;
249 		goto ret;
250 	}
251 	if(strcmp(cmd, "names") == 0) {
252 		fmt = "%s";
253 		for(tkim = t->imgs; tkim; tkim = tkim->link) {
254 		        if (tkim->name != nil) {
255 				e = tkvalue(ret, fmt, tkim->name->name);
256 				if(e != nil)
257 					goto ret;
258 			}
259 			fmt = " %s";
260 		}
261 		e = nil;
262 		goto ret;
263 	}
264 
265 	arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
266 	tkim = tkname2img(t, buf);
267 	if(tkim == nil) {
268 		e = TkBadvl;
269 		goto ret;
270 	}
271 
272 	if(strcmp(cmd, "height") == 0) {
273 		e = tkvalue(ret, "%d", tkim->h);
274 		goto ret;
275 	}
276 	if(strcmp(cmd, "width") == 0) {
277 		e = tkvalue(ret, "%d", tkim->w);
278 		goto ret;
279 	}
280 	if(strcmp(cmd, "type") == 0) {
281 		e = tkvalue(ret, "%s", tkimgopts[tkim->type].type);
282 		goto ret;
283 	}
284 	if(strcmp(cmd, "delete") == 0) {
285 		for (;;) {
286 			e = tkimgopts[tkim->type].delete(tkim);
287 			if (e != nil)
288 				break;
289 			arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
290 			if (buf[0] == '\0')
291 				 break;
292 			tkim = tkname2img(t, buf);
293 			if (tkim == nil) {
294 				e = TkBadvl;
295 				break;
296 			}
297 		}
298 		goto ret;
299 	}
300 
301 	e = TkBadcm;
302 ret:
303 	free(cmd);
304 	free(buf);
305 	return e;
306 }
307 
308 void
309 tkimgput(TkImg *tki)
310 {
311 	if(tki == nil)
312 		return;
313 
314 	if(--tki->ref > 0)
315 		return;
316 
317 	tkimgopts[tki->type].destroy(tki);
318 }
319 
320 TkImg*
321 tkauximage(TkTop *t, char* s, uchar* bytes, int nbytes, int chans, Rectangle r, int repl)
322 {
323 	TkName *name;
324 	TkCtxt *c;
325 	TkImg *tki;
326 	Display *d;
327 	Image *i;
328 	int locked;
329 
330 	tki = tkname2img(t, s);
331 	if (tki != nil) {
332 		tki->ref++;
333 		return tki;
334 	}
335 
336 	name = tkmkname(s);
337 	if (name == nil)
338 		return nil;
339 	tki = mallocz(sizeof(*tki), 0);
340 	if (tki == nil)
341 		goto err;
342 	tki->env = tkdefaultenv(t);
343 	if(tki->env == nil)
344 		goto err;
345 
346 	c = t->ctxt;
347 	d = c->display;
348 
349 	locked = lockdisplay(d);
350 	i = allocimage(d, r, chans, repl, DTransparent);
351 	if (i != nil) {
352 		if (loadimage(i, r, bytes, nbytes) != nbytes) {
353 			freeimage(i);
354 			i = nil;
355 		}
356 		if (repl)
357 			replclipr(i, 1, huger);
358 	}
359 	if (locked)
360 		unlockdisplay(d);
361 	if (i == nil)
362 		goto err;
363 	tki->top = t;
364 	tki->ref = 2;	/* t->imgs ref and the ref we are returning */
365 	tki->type = 0;	/* bitmap */
366 	tki->w = Dx(r);
367 	tki->h = Dy(r);
368 	tki->img = i;
369 	tki->name = name;
370 	tki->link = t->imgs;
371 	t->imgs = tki;
372 	return tki;
373 err:
374 	if (tki != nil) {
375 		tkputenv(tki->env);
376 		free(tki);
377 	}
378 	tkfreename(name);
379 	return nil;
380 }
381