xref: /inferno-os/libtk/image.c (revision 0db9190e73bd2b1391b1108a354b8cf089ab5374)
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*
tkname2img(TkTop * t,char * name)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
tksizeimage(Tk * tk,TkImg * tki)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*
tkimgbmcreate(TkTop * t,char * arg,int type,char ** ret)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*
tkimgbmdel(TkImg * tki)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
tkimgbmfree(TkImg * tki)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*
tkimage(TkTop * t,char * arg,char ** ret)224 tkimage(TkTop *t, char *arg, char **ret)
225 {
226 	int i;
227 	TkImg *tkim;
228 	char *fmt, *e, *buf, *cmd;
229 
230 	buf = mallocz(Tkmaxitem, 0);
231 	if(buf == nil)
232 		return TkNomem;
233 	cmd = mallocz(Tkminitem, 0);
234 	if(cmd == nil) {
235 		free(buf);
236 		return TkNomem;
237 	}
238 
239 	arg = tkword(t, arg, cmd, cmd+Tkminitem, nil);
240 	if(strcmp(cmd, "create") == 0) {
241 		arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
242 		for(i = 0; tkimgopts[i].type != nil; i++)
243 			if(strcmp(buf, tkimgopts[i].type) == 0) {
244 				e = tkimgopts[i].create(t, arg, i, ret);
245 				goto ret;
246 			}
247 		e = TkBadvl;
248 		goto ret;
249 	}
250 	if(strcmp(cmd, "names") == 0) {
251 		fmt = "%s";
252 		for(tkim = t->imgs; tkim; tkim = tkim->link) {
253 		        if (tkim->name != nil) {
254 				e = tkvalue(ret, fmt, tkim->name->name);
255 				if(e != nil)
256 					goto ret;
257 			}
258 			fmt = " %s";
259 		}
260 		e = nil;
261 		goto ret;
262 	}
263 
264 	arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
265 	tkim = tkname2img(t, buf);
266 	if(tkim == nil) {
267 		e = TkBadvl;
268 		goto ret;
269 	}
270 
271 	if(strcmp(cmd, "height") == 0) {
272 		e = tkvalue(ret, "%d", tkim->h);
273 		goto ret;
274 	}
275 	if(strcmp(cmd, "width") == 0) {
276 		e = tkvalue(ret, "%d", tkim->w);
277 		goto ret;
278 	}
279 	if(strcmp(cmd, "type") == 0) {
280 		e = tkvalue(ret, "%s", tkimgopts[tkim->type].type);
281 		goto ret;
282 	}
283 	if(strcmp(cmd, "delete") == 0) {
284 		for (;;) {
285 			e = tkimgopts[tkim->type].delete(tkim);
286 			if (e != nil)
287 				break;
288 			arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
289 			if (buf[0] == '\0')
290 				 break;
291 			tkim = tkname2img(t, buf);
292 			if (tkim == nil) {
293 				e = TkBadvl;
294 				break;
295 			}
296 		}
297 		goto ret;
298 	}
299 
300 	e = TkBadcm;
301 ret:
302 	free(cmd);
303 	free(buf);
304 	return e;
305 }
306 
307 void
tkimgput(TkImg * tki)308 tkimgput(TkImg *tki)
309 {
310 	if(tki == nil)
311 		return;
312 
313 	if(--tki->ref > 0)
314 		return;
315 
316 	tkimgopts[tki->type].destroy(tki);
317 }
318 
319 TkImg*
tkauximage(TkTop * t,char * s,TkMemimage * m,int repl)320 tkauximage(TkTop *t, char* s, TkMemimage *m, int repl)
321 {
322 	TkName *name;
323 	TkCtxt *c;
324 	TkImg *tki;
325 	Display *d;
326 	Image *i;
327 	int locked, nbytes;
328 
329 	tki = tkname2img(t, s);
330 	if (tki != nil) {
331 		tki->ref++;
332 		return tki;
333 	}
334 
335 	name = tkmkname(s);
336 	if (name == nil)
337 		return nil;
338 	tki = mallocz(sizeof(*tki), 1);
339 	if (tki == nil)
340 		goto err;
341 	tki->env = tkdefaultenv(t);
342 	if(tki->env == nil)
343 		goto err;
344 
345 	c = t->ctxt;
346 	d = c->display;
347 
348 	nbytes = bytesperline(m->r, chantodepth(m->chans))*Dy(m->r);
349 	locked = lockdisplay(d);
350 	i = allocimage(d, m->r, m->chans, repl, DTransparent);
351 	if (i != nil) {
352 		if (loadimage(i, m->r, m->data, nbytes) != nbytes) {
353 			freeimage(i);
354 			i = nil;
355 		}
356 		if (repl)
357 			replclipr(i, 1, huger);	/* TO DO: doesn't allocimage do this? */
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(m->r);
367 	tki->h = Dy(m->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