1 /* Interpretation of generic GDL images for Xconq.
2 Copyright (C) 1994-2001 Stanley T. Shebs.
3
4 Xconq is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version. See the file COPYING. */
8
9 /* Note! This file does not use the standard "conq.h" header, so
10 can't assume all the usual definitions. */
11
12 #include "config.h"
13 #include "misc.h"
14 #include "lisp.h"
15 #include "imf.h"
16 #include "module.h"
17 #include "system.h"
18
19 /* RGB above this value should be considered white. */
20
21 #define WHITE_THRESHOLD (65535 - 256)
22
23 /* RGB below this value should be considered black. */
24
25 #define BLACK_THRESHOLD (0 + 255)
26
27 enum {
28 K_MONO_,
29 K_MASK_,
30 K_COLR_,
31 K_FILE_,
32 K_OTHER_
33 };
34
35 typedef struct _MFEntry {
36 int value, count;
37 } MFEntry;
38 typedef MFEntry *ModeFilter;
39
40 static Image *get_subimg(ImageFamily *imf, int w, int h);
41 static Image *largest_image(ImageFamily *imf);
42 static int image_pixel_at(Image *img, int imtype, int x, int y);
43 static void set_image_pixel_at(Image *img, int imtype, int x, int y, int val);
44 static ImageFamily *new_imf(char *name);
45 static int bitmaps_match(int w, int h, Obj *lispdata, char *rawdata);
46 static int color_matches_mono(Image *img);
47 static void write_pixmap(FILE *fp, int w, int h, int aw, int ah,
48 int pixelsize, int orig_pixelsize,
49 Obj *palette, int *rawpalette, int numcolors,
50 Obj *lispdata, char *rawdata);
51 static void write_bitmap(FILE *fp, char *subtyp, int w, int h,
52 Obj *data, char *rawdata);
53 static void write_palette_contents(FILE *fp, Obj *palette,
54 int *rawpalette, int numcolors);
55 static void write_color(FILE *fp, int n, int r, int g, int b);
56 static ModeFilter new_mf(int maxvals);
57 static void add_to_mf(ModeFilter mf, int val);
58 static int mode_of_mf(ModeFilter mf);
59 static void zero_mf(ModeFilter mf);
60 static void delete_mf(ModeFilter mf);
61 static void add_hex_mask(Image *img);
62 static void remove_hex_mask(Image *img);
63 static void scale_image_layer(Image *imgin, Image *imgout, int layer,
64 int use_mask, ModeFilter mf);
65 static void scale_image(ImageFamily *imf, Image *img, Image *img2, int dhm);
66 static Image *add_scaled_image(ImageFamily *imf, Image *img, int w, int h);
67 static int size_match_score(int wa, int ha, int wb, int hb);
68 static int calculate_hch(int h);
69
70 /* This is the array and count of known image families. */
71
72 ImageFamily **images;
73
74 int numimages = 0;
75
76 /* Head of the linked list of image files. */
77
78 ImageFile *image_files;
79
80 /* Head of the linked list of "file" images used by several image families. */
81
82 FileImage *file_images;
83
84 ImageFamily *(*imf_load_hook)(ImageFamily *imf);
85
86 ImageFamily *(*imf_interp_hook)(ImageFamily *imf, Image *img, int force);
87
88 short write_synthetic_also;
89
90 /* There are two ways to use image families. The "old" way is to use
91 the image families' masks and other bitmaps as clip masks. tcl/tk
92 for Windows doesn't support clip masks however, so the "new" way is
93 to do a GXand with the mask, carving out a mask-shaped black hole,
94 then do a GXor with the image proper. Note that the part of the
95 image that is not included in the mask must be converted to black
96 if the ORing is to have the right effect. X doesn't guarantee that
97 ANDing and ORing will work in general though; we just know that the
98 X emulation in tcl/tk for Windows can be used in this way. */
99
100 /* This should be TRUE everywhere except on Windows. */
101
102 int use_clip_mask = TRUE;
103
104 /* Flag that indicates limited GDI memory in Windows ME and below. */
105
106 short poor_memory = FALSE;
107
108 /* Create and return an image family. */
109
110 static ImageFamily *
new_imf(char * name)111 new_imf(char *name)
112 {
113 ImageFamily *imf;
114
115 imf = (ImageFamily *) xmalloc(sizeof(ImageFamily));
116 imf->name = name;
117 imf->notes = lispnil;
118 return imf;
119 }
120
121 ImageFamily *
clone_imf(ImageFamily * imf)122 clone_imf(ImageFamily *imf)
123 {
124 Image *img, *img2, *truenext;
125 ImageFamily *imf2;
126
127 imf2 = new_imf(imf->name);
128 memcpy(imf2, imf, sizeof(ImageFamily));
129 /* Clear the hook, we expect that the caller of this routine will
130 supply any new hook that might be necessary. */
131 imf2->hook = NULL;
132 imf2->images = NULL;
133 imf2->numsizes = 0;
134 /* Clone the images. */
135 for_all_images(imf, img) {
136 img2 = get_img(imf2, img->w, img->h);
137 truenext = img2->next;
138 memcpy(img2, img, sizeof(Image));
139 /* Clear the hook, we expect that the caller of this routine
140 will supply any new hook that might be necessary. */
141 img2->hook = NULL;
142 /* Restore the link. */
143 img2->next = truenext;
144 /* Note that pointers to raw image data and suchlike can be
145 left as-is, since they should be shared by image clones. */
146 /* (should copy anyway, for safety?) */
147 }
148 return imf2;
149 }
150
151 /* Test that the given name is a valid image family name (all alphanumeric,
152 hyphens anywhere but as first char). */
153
154 int
valid_imf_name(char * name)155 valid_imf_name(char *name)
156 {
157 char *tmp;
158
159 for (tmp = name; *tmp; ++tmp) {
160 if (!(isalnum(*tmp)
161 || (tmp != name && *tmp == '-')))
162 return FALSE;
163 }
164 return TRUE;
165 }
166
167 /* Bash invalid chars in a prospective imf name. */
168
169 void
validify_imf_name(char * buf)170 validify_imf_name(char *buf)
171 {
172 char *tmp;
173
174 for (tmp = buf; *tmp; ++tmp) {
175 if (tmp == buf && *tmp == '-')
176 *tmp = 'Z';
177 if (!isalnum(*tmp) && *tmp != '-')
178 *tmp = '-';
179 }
180 }
181
182 /* Given a name, find or create an image family with that name. */
183
184 ImageFamily *
get_imf(char * name)185 get_imf(char *name)
186 {
187 ImageFamily *imf = NULL;
188
189 if (name == NULL) {
190 init_warning("can't get an unnamed image family");
191 return NULL;
192 }
193 if (!valid_imf_name(name)) {
194 init_warning("\"%s\" is not a valid image family name", name);
195 return NULL;
196 }
197 if (images == NULL) {
198 images =
199 (ImageFamily **) xmalloc(MAXIMAGEFAMILIES * sizeof(ImageFamily *));
200 }
201 imf = find_imf(name);
202 if (imf == NULL) {
203 if (numimages >= MAXIMAGEFAMILIES) {
204 init_warning("MAXIMAGEFAMILIES exceeded, skipping image family %s", name);
205 return NULL;
206 }
207 imf = new_imf(copy_string(name));
208 if (imf != NULL) {
209 images[numimages++] = imf;
210 }
211 }
212 return imf;
213 }
214
215 ImageFile *
get_image_file(char * name)216 get_image_file(char *name)
217 {
218 ImageFile *imfile;
219
220 if (name == NULL)
221 run_error("can't get an unnamed image file");
222 for (imfile = image_files; imfile != NULL; imfile = imfile->next) {
223 if (strcmp(name, imfile->name) == 0)
224 return imfile;
225 }
226 imfile = (ImageFile *) xmalloc(sizeof(ImageFile));
227 imfile->name = copy_string(name);
228 imfile->next = image_files;
229 image_files = imfile;
230 return imfile;
231 }
232
233 void
load_image_families(FILE * fp,int loadnow,void (* callback)(ImageFamily * imf,int loadnow))234 load_image_families(FILE *fp, int loadnow,
235 void (*callback)(ImageFamily *imf, int loadnow))
236 {
237 int done = FALSE, first = TRUE;
238 char buf[BUFSIZE], *buf1, *buf2, *tmp;
239 ImageFamily *imf = NULL;
240 ImageFile *imfile;
241
242 while (!done) {
243 /* Get a line from the file and parse it. */
244 if (fgets(buf, BUFSIZE-1, fp)) {
245 buf1 = buf;
246 buf2 = strchr(buf, ' ');
247 if (buf2 == NULL)
248 break;
249 *buf2 = '\0';
250 ++buf2;
251 tmp = strchr(buf2, '\n');
252 if (tmp)
253 *tmp = '\0';
254 } else
255 break;
256 if (strcmp(buf1, ".") == 0
257 && strcmp(buf2, ".") == 0)
258 done = TRUE;
259 else if (first) {
260 if (strcmp(buf1, "ImageFamilyName") == 0
261 && strcmp(buf2, "FileName") == 0)
262 first = FALSE;
263 else {
264 init_warning("File not a valid imf dir, will close and ignore");
265 /* We've already given a warning message, so pretend we're done
266 so the format error message doesn't get displayed below. */
267 done = TRUE;
268 break;
269 }
270 } else {
271 imf = get_imf(buf1);
272 if (imf != NULL) {
273 imfile = get_image_file(buf2);
274 imf->location = imfile;
275 if (loadnow && !imfile->loaded) {
276 load_imf_file(imfile->name, callback);
277 imfile->loaded = TRUE;
278 } else {
279 if (callback != NULL)
280 (*callback)(imf, loadnow);
281 }
282 }
283 }
284 }
285 if (!done) {
286 init_warning("Format error in imf dir near %s, will only use part",
287 (imf ? imf->name : "???"));
288 }
289 }
290
291 /* Given a filename, open it and read/interpret all the image-related
292 forms therein. */
293
294 int
load_imf_file(char * filename,void (* callback)(ImageFamily * imf,int loadnow))295 load_imf_file(char *filename, void (*callback)(ImageFamily *imf, int loadnow))
296 {
297 int startlineno = 1, endlineno = 1;
298 Obj *form;
299 FILE *fp;
300
301 fp = open_file(filename, "r");
302 if (fp != NULL) {
303 /* Read everything in the file. */
304 while ((form = read_form(fp, &startlineno, &endlineno)) != lispeof) {
305 interp_imf_form(form, filename, callback);
306 }
307 fclose(fp);
308 return TRUE;
309 }
310 return FALSE;
311 }
312
313 /* Interpret a form, looking specifically for image-related forms. */
314
315 void
interp_imf_form(Obj * form,char * filename,void (* imf_callback)(ImageFamily * imf,int loadnow))316 interp_imf_form(Obj *form, char *filename, void (*imf_callback)(ImageFamily *imf, int loadnow))
317 {
318 Obj *head;
319 ImageFamily *imf;
320 ImageFile *imfile;
321
322 /* Ignore any non-lists, we might be reading from a normal game design. */
323 if (!consp(form))
324 return;
325 head = car(form);
326 if (match_keyword(head, K_IMF)) {
327 imf = interp_imf(form);
328 if (imf != NULL) {
329 imfile = get_image_file(filename);
330 imf->location = imfile;
331 imf->location->loaded = TRUE;
332 if (imf_callback != NULL) {
333 (*imf_callback)(imf, TRUE);
334 }
335 }
336 } else {
337 /* Ignore any non-image forms, we might be reading from a
338 normal game design. */
339 }
340 }
341
342 /* Find the image family of the given name, if it exists. */
343
344 ImageFamily *
find_imf(char * name)345 find_imf(char *name)
346 {
347 int i;
348
349 for (i = 0; i < numimages; ++i) {
350 if (strcmp(name, images[i]->name) == 0)
351 return images[i];
352 }
353 return NULL;
354 }
355
356 /* Get an image of the given size from the family, creating a new one
357 if necessary. */
358
359 Image *
get_img(ImageFamily * imf,int w,int h)360 get_img(ImageFamily *imf, int w, int h)
361 {
362 Image *img, *nimg, *previmg;
363
364 for_all_images(imf, img) {
365 if (w == img->w && h == img->h)
366 return img;
367 }
368 /* Not found; create a new image and add it to the family. */
369 nimg = (Image *) xmalloc(sizeof(Image));
370 nimg->w = w; nimg->h = h;
371 nimg->embedx = nimg->embedy = -1;
372 nimg->embedw = nimg->embedh = -1;
373 nimg->monodata = nimg->colrdata = nimg->maskdata = lispnil;
374 nimg->filedata = lispnil;
375 nimg->palette = lispnil;
376 nimg->actualw = w; nimg->actualh = h;
377 nimg->notes = lispnil;
378 nimg->bboxw = w; nimg->bboxh = h;
379 /* Rely on zeroing of xmalloc blocks to avoid clearing other fields. */
380 /* Link in order by size, smallest first. */
381 previmg = NULL;
382 for_all_images(imf, img) {
383 if ((nimg->w < img->w)
384 || (nimg->w == img->w && nimg->h < img->h))
385 break;
386 previmg = img;
387 }
388 if (previmg != NULL) {
389 nimg->next = previmg->next;
390 previmg->next = nimg;
391 } else {
392 nimg->next = imf->images;
393 imf->images = nimg;
394 }
395 ++(imf->numsizes);
396 return nimg;
397 }
398
399 /* Get an image of the given size from the family, creating a new one
400 if necessary. */
401
402 Image *
get_subimg(ImageFamily * imf,int w,int h)403 get_subimg(ImageFamily *imf, int w, int h)
404 {
405 Image *nimg;
406
407 /* Not found; create a new image and add it to the family. */
408 nimg = (Image *) xmalloc(sizeof(Image));
409 nimg->w = w; nimg->h = h;
410 nimg->embedx = nimg->embedy = -1;
411 nimg->embedw = nimg->embedh = -1;
412 nimg->monodata = nimg->colrdata = nimg->maskdata = lispnil;
413 nimg->filedata = lispnil;
414 nimg->palette = lispnil;
415 nimg->actualw = w; nimg->actualh = h;
416 nimg->notes = lispnil;
417 nimg->bboxw = w; nimg->bboxh = h;
418 return nimg;
419 }
420
421 Image *
find_img(ImageFamily * imf,int w,int h)422 find_img(ImageFamily *imf, int w, int h)
423 {
424 Image *img;
425
426 for_all_images(imf, img) {
427 if (w == img->w && h == img->h)
428 return img;
429 }
430 return NULL;
431 }
432
433 ImageFamily *
interp_imf(Obj * form)434 interp_imf(Obj *form)
435 {
436 ImageFamily *imf;
437
438 if (stringp(cadr(form))) {
439 imf = get_imf(c_string(cadr(form)));
440 if (imf != NULL) {
441 interp_imf_contents(imf, cddr(form));
442 }
443 return imf;
444 } else {
445 run_warning("image family name must be a string");
446 }
447 return NULL;
448 }
449
450 /* Interpret the image family definition as a list of images and/or a
451 notes property. */
452
453 void
interp_imf_contents(ImageFamily * imf,Obj * clauses)454 interp_imf_contents(ImageFamily *imf, Obj *clauses)
455 {
456 Obj *rest, *clause;
457
458 for_all_list(clauses, rest) {
459 clause = car(rest);
460 if (consp(clause)) {
461 if (symbolp(car(clause))) {
462 if (match_keyword(car(clause), K_NOTES)) {
463 imf->notes = cadr(clause);
464 syntax_error(clause, "extra junk after property value");
465 } else {
466 syntax_error(clause, "unknown image family property");
467 }
468 } else if (consp(car(clause))) {
469 interp_image(imf, car(clause), cdr(clause));
470 } else {
471 syntax_error(clause, "not image or image family property");
472 }
473 } else {
474 syntax_error(clause, "bogus clause");
475 }
476 }
477 compute_image_bboxes(imf);
478 }
479
480 /* Given an image family, a size, and a list describing the elements
481 of a single image, parse the size and elements, put those into the
482 right slots of an image object. Also detect and warn about changes
483 to an existing image, since this usually indicates some kind of
484 problem. */
485
486 void
interp_image(ImageFamily * imf,Obj * size,Obj * parts)487 interp_image(ImageFamily *imf, Obj *size, Obj *parts)
488 {
489 int w, h, imtype, emx, emy, emw, emh, numsubs, subi;
490 char *name;
491 Image *img, *subimg;
492 Obj *head, *rest, *typ, *prop, *proptype, *datalist;
493
494 w = c_number(car(size)); h = c_number(cadr(size));
495 img = get_img(imf, w, h);
496 if (img == NULL)
497 run_error("no image?");
498 if (img->w == 1 && img->h == 1) {
499 /* A color is more like a tile than an icon. */
500 img->istile = TRUE;
501 img->palette = cons(cons(new_number(0), parts), lispnil);
502 return;
503 }
504 if (match_keyword(car(cddr(size)), K_TILE))
505 img->istile = TRUE;
506 if (match_keyword(car(cddr(size)), K_TERRAIN))
507 img->isterrain = TRUE;
508 if (match_keyword(car(cddr(size)), K_CONNECTION))
509 img->isconnection = TRUE;
510 if (match_keyword(car(cddr(size)), K_BORDER))
511 img->isborder = TRUE;
512 if (match_keyword(car(cddr(size)), K_TRANSITION))
513 img->istransition = TRUE;
514 numsubs = 0;
515 for_all_list(parts, rest) {
516 head = car(rest);
517 typ = car(head);
518 imtype = K_OTHER_;
519 if (match_keyword(typ, K_MONO)) {
520 imtype = K_MONO_;
521 } else if (match_keyword(typ, K_MASK)) {
522 imtype = K_MASK_;
523 } else if (match_keyword(typ, K_COLOR)) {
524 imtype = K_COLR_;
525 } else if (match_keyword(typ, K_FILE)) {
526 imtype = K_FILE_;
527 } else if (match_keyword(typ, K_EMBED)) {
528 name = c_string(cadr(head));
529 if (img->embedname != NULL
530 && strcmp(img->embedname, name) != 0)
531 run_warning("Changing embed name from \"%s\" to \"%s\" in %dx%d image of \"%s\"",
532 img->embedname, name, w, h, imf->name);
533 img->embedname = name;
534 } else if (match_keyword(typ, K_EMBED_AT)) {
535 emx = c_number(cadr(head)); emy = c_number(caddr(head));
536 if ((img->embedx >= 0 && emx != img->embedx)
537 || (img->embedy >= 0 && emy != img->embedy))
538 run_warning("Changing embed x,y from %d,%d to %d,%d in %dx%d image of \"%s\"",
539 img->embedx, img->embedy, emx, emy, w, h, imf->name);
540 img->embedx = emx; img->embedy = emy;
541 } else if (match_keyword(typ, K_EMBED_SIZE)) {
542 emw = c_number(cadr(head)); emh = c_number(caddr(head));
543 if ((img->embedw >= 0 && emw != img->embedw)
544 || (img->embedh >= 0 && emh != img->embedh))
545 run_warning("Changing embed w,h from %d,%d to %d,%d in %dx%d image of \"%s\"",
546 img->embedw, img->embedh, emw, emh, w, h, imf->name);
547 img->embedw = emw; img->embedh = emh;
548 } else if (match_keyword(typ, K_HEXGRID)) {
549 img->hexgridx = c_number(cadr(head));
550 img->hexgridy = c_number(caddr(head));
551 numsubs = img->hexgridx*img->hexgridy;
552 } else if (match_keyword(typ, K_NOTES)) {
553 img->notes = cadr(head);
554 syntax_error(head, "extra junk after image notes property");
555 } else if (match_keyword(typ, K_X)) {
556 numsubs = c_number(cadr(head));
557 if (cddr(head) != lispnil) {
558 img->subx = c_number(caddr(head));
559 img->suby = c_number(car(cdddr(head)));
560 }
561 } else {
562 run_warning("unknown image property in \"%s\"", imf->name);
563 }
564 /* If there is no actual image data to process, skip to the next
565 clause in the form. */
566 if (imtype == K_OTHER_)
567 continue;
568 datalist = cdr(head);
569 /* Interpret random image subproperties. */
570 while (consp(car(datalist))) {
571 prop = car(datalist);
572 proptype = car(prop);
573 if (match_keyword(proptype, K_ACTUAL)) {
574 img->actualw = c_number(cadr(prop));
575 img->actualh = c_number(caddr(prop));
576 } else if (match_keyword(proptype, K_PIXEL_SIZE)) {
577 img->pixelsize = c_number(cadr(prop));
578 } else if (match_keyword(proptype, K_PALETTE)) {
579 img->palette = cdr(prop);
580 } else {
581 char imferrbuf[200];
582
583 sprintlisp(imferrbuf, prop, 100);
584 run_warning("unknown image subproperty in \"%s\": %s",
585 imf->name, imferrbuf);
586 }
587 datalist = cdr(datalist);
588 }
589 switch (imtype) {
590 case K_MONO_:
591 if (img->monodata != lispnil && !equal(datalist, img->monodata))
592 run_warning("Changing mono data in %dx%d image of \"%s\"",
593 w, h, imf->name);
594 img->monodata = datalist;
595 break;
596 case K_COLR_:
597 if (img->colrdata != lispnil && !equal(datalist, img->colrdata))
598 run_warning("Changing color data in %dx%d image of \"%s\"",
599 w, h, imf->name);
600 img->colrdata = datalist;
601 break;
602 case K_MASK_:
603 if (img->maskdata != lispnil && !equal(datalist, img->maskdata))
604 run_warning("Changing mask data in %dx%d image of \"%s\"",
605 w, h, imf->name);
606 img->maskdata = datalist;
607 break;
608 case K_FILE_:
609 if (img->filedata != lispnil && !equal(datalist, img->filedata))
610 run_warning("Changing file data in %dx%d image of \"%s\"",
611 w, h, imf->name);
612 img->filedata = datalist;
613 break;
614 default:
615 break;
616 }
617 }
618 /* Allocate space for any subimages that might be needed. */
619 /* First set some standard numbers of subimages. */
620 if (img->isborder) {
621 numsubs = 16;
622 } else if (img->isconnection) {
623 numsubs = 64;
624 } else if (img->istransition) {
625 numsubs = 4 * 4;
626 /* Limit the number of terrain subimages if we lack memory. */
627 } else if (poor_memory) { /* FIXME - what about hexgrid? */
628 numsubs = min(numsubs, 3);
629 }
630 /* Deal with possible weird situations. */
631 if (img->numsubimages > 0 && numsubs != img->numsubimages) {
632 run_warning("Going from %d to %d subimages in %dx%d image of \"%s\"",
633 img->numsubimages, numsubs, w, h, imf->name);
634 img->subimages = NULL;
635 }
636 img->numsubimages = numsubs;
637 if (img->subimages == NULL) {
638 img->subimages = (Image **) xmalloc(numsubs * sizeof(Image *));
639 for (subi = 0; subi < numsubs; ++subi) {
640 subimg = get_subimg(imf, img->w, img->h);
641 img->subimages[subi] = subimg;
642 }
643 }
644 }
645
646 void
compute_image_bboxes(ImageFamily * imf)647 compute_image_bboxes(ImageFamily *imf)
648 {
649 Image *img;
650
651 if (imf == NULL)
652 return;
653 for_all_images(imf, img) {
654 compute_image_bbox(img);
655 }
656 }
657
658 void
compute_image_bbox(Image * img)659 compute_image_bbox(Image *img)
660 {
661 int numbytes, i, j = 0, byte, x, y, x1, x2, k;
662 int xmin, ymin, xmax, ymax;
663 char *data = NULL;
664 Obj *datalist, *next;
665
666 datalist = img->maskdata;
667 numbytes = img->h * computed_rowbytes(img->w, 1);
668 x = y = 0;
669 xmin = img->w; ymin = img->h;
670 xmax = 0; ymax = 0;
671 for (i = 0; i < numbytes; ++i) {
672 if (img->maskdata != lispnil) {
673 if (data == NULL || data[j] == '\0') {
674 next = car(datalist);
675 if (!stringp(next)) {
676 syntax_error(datalist, "garbage in image data list");
677 return;
678 }
679 data = c_string(next);
680 j = 0;
681 datalist = cdr(datalist);
682 }
683 /* Just skip over slashes, which are for readability only. */
684 if (data[j] == '/')
685 ++j;
686 byte = hextoi(data[j]) * 16 + hextoi(data[j+1]);
687 j += 2;
688 } else if (img->rawmaskdata != NULL) {
689 byte = img->rawmaskdata[i] & 0xff;
690 } else {
691 byte = 0xff;
692 }
693 if (byte != 0) {
694 /* Find the most-significant and least-significant bits in
695 the mask byte. */
696 x1 = x2 = -1;
697 k = 0;
698 while (byte != 0) {
699 if ((byte & 0x1) != 0 && x2 < 0)
700 x2 = x + 7 - k;
701 byte >>= 1;
702 if (byte == 0 && x1 < 0)
703 x1 = x + 7 - k;
704 ++k;
705 }
706 xmin = min(x1, xmin); ymin = min(y, ymin);
707 xmax = max(x2, xmax); ymax = max(y, ymax);
708 }
709 x += 8;
710 if (x >= img->w) {
711 x = 0;
712 ++y;
713 }
714 }
715 /* Compute position and size of bounding box. */
716 if (xmin <= xmax && ymin <= ymax) {
717 img->bboxx = xmin; img->bboxy = ymin;
718 img->bboxw = xmax - xmin; img->bboxh = ymax - ymin;
719 }
720 }
721
722 /* Get a single pixel from the given type of data of an image. */
723
724 int
image_pixel_at(Image * img,int imtype,int x,int y)725 image_pixel_at(Image *img, int imtype, int x, int y)
726 {
727 int rowbytes, psize, i, byte, rslt;
728 char *rawdata = NULL;
729
730 if (imtype == K_MONO_) {
731 rawdata = img->rawmonodata;
732 psize = 1;
733 } else if (imtype == K_MASK_) {
734 rawdata = img->rawmaskdata;
735 psize = 1;
736 } else if (imtype == K_COLR_) {
737 rawdata = img->rawcolrdata;
738 psize = img->pixelsize;
739 }
740 if (rawdata == NULL)
741 return 0;
742 rowbytes = computed_rowbytes(img->w, psize);
743 i = y * rowbytes + ((x * psize) >> 3);
744 byte = rawdata[i];
745 rslt = (byte >> ((8 - psize) - ((x * psize) & 0x7))) & ((1 << psize) - 1);
746 return rslt;
747 }
748
749 /* Set a single pixel in the given type of data of an image. */
750
751 void
set_image_pixel_at(Image * img,int imtype,int x,int y,int val)752 set_image_pixel_at(Image *img, int imtype, int x, int y, int val)
753 {
754 int rowbytes, psize, i, byte;
755 char *rawdata = NULL;
756
757 if (imtype == K_MONO_) {
758 rawdata = img->rawmonodata;
759 psize = 1;
760 } else if (imtype == K_MASK_) {
761 rawdata = img->rawmaskdata;
762 psize = 1;
763 } else if (imtype == K_COLR_) {
764 rawdata = img->rawcolrdata;
765 psize = img->pixelsize;
766 }
767 if (rawdata != NULL) {
768 rowbytes = computed_rowbytes(img->w, psize);
769 i = y * rowbytes + ((x * psize) >> 3);
770 byte = rawdata[i];
771 byte &= ~ (((1 << psize) - 1) << ((8 - psize) - ((x * psize) & 0x7)));
772 byte |= val << ((8 - psize) - ((x * psize) & 0x7));
773 rawdata[i] = byte;
774 }
775 }
776
777 /* Create a new data structure for mode filtering; maxvals is how many
778 distinct values we might call add_to_mf on. The way this works is that
779 you create the structure with new_mf, then you call add_to_mf a bunch of
780 times with different values. Then you can call mode_of_mf and it'll
781 tell which value you used the most times in calls to add_to_mf. Calling
782 zero_mf resets the filter in a way that is cheaper than deleting and
783 re-creating it, and delete_mf is called when you're finally finished. */
784
785 static ModeFilter
new_mf(int maxval)786 new_mf(int maxval)
787 {
788 ModeFilter rval = (ModeFilter)xmalloc(sizeof(MFEntry)*(maxval+1));
789
790 return rval;
791 }
792
793 /* Add a new value to the mode filter. */
794
795 static void
add_to_mf(ModeFilter mf,int val)796 add_to_mf(ModeFilter mf, int val)
797 {
798 int i;
799 MFEntry mfe;
800
801 if (mf == NULL) return;
802 for (i = 0; (mf[i].value != val) && (mf[i].count != 0); i++);
803 mf[i].value = val;
804 mf[i].count++;
805 if (mf[i].count > mf[0].count) {
806 mfe = mf[i];
807 mf[i] = mf[0];
808 mf[0] = mfe;
809 }
810 }
811
812 /* Find the most common value in the mode filter. */
813
814 static int
mode_of_mf(ModeFilter mf)815 mode_of_mf(ModeFilter mf)
816 {
817 if (mf != NULL)
818 return mf[0].value;
819 else
820 return 0;
821 }
822
823 /* Empty the mode filter of all counts. */
824
825 static void
zero_mf(ModeFilter mf)826 zero_mf(ModeFilter mf)
827 {
828 int i;
829
830 if (mf == NULL) return;
831 for (i = 0; mf[i].count != 0; i++)
832 mf[i].count = 0;
833 mf[0].value = 0;
834 }
835
836 /* Get rid of the mode filter. */
837
838 static void
delete_mf(ModeFilter mf)839 delete_mf(ModeFilter mf)
840 {
841 if (mf != NULL) free(mf);
842 }
843
844 /* Calculate hch by a formula that matches Stan's magic tables, but also
845 gives reasonable results at intermediate sizes. Needed because the
846 standard hex shapes have been tweaked away from geometric perfection,
847 for graphics expediency. */
848
849 static int
calculate_hch(int h)850 calculate_hch(int h)
851 {
852 return (h*3)/4 + h/72 + h/144 + 1;
853 }
854
855 /* Mask the image down to a hexagon that fills the image rectangle - points
856 at the centres of the top and bottom, flat sides along left and right. */
857
858 static void
add_hex_mask(Image * img)859 add_hex_mask(Image *img)
860 {
861 int tw, th, x, y, numbytes;
862
863 if (img == NULL)
864 return;
865 if (img->w > img->h)
866 return;
867
868 /* Ensure that binary version of mask exists. Create one even if none
869 was specified. */
870 if (img->rawmaskdata == NULL) {
871 img->rawmaskdata = (char *)xmalloc(numbytes);
872 if (img->maskdata != lispnil)
873 interp_bytes(img->maskdata, numbytes, img->rawmaskdata, 0);
874 else
875 for (y = 0; y < img->h; y++)
876 for (x = 0; x < img->w; x++)
877 set_image_pixel_at(img, K_MASK_, x, y, 1);
878 }
879
880 /* Calculate size of triangles to mask out */
881 tw = img->w / 2 - 1;
882 th = img->h - calculate_hch(img->h) - 1;
883
884 /* Mask out the bits */
885 for (x = 1; x <= tw; x++)
886 for (y = 0; y <= (x*th) / tw; y++) {
887 set_image_pixel_at(img, K_MASK_, tw-x, y, 0);
888 set_image_pixel_at(img, K_MASK_, tw+1+x, y, 0);
889 set_image_pixel_at(img, K_MASK_, tw-x, img->h-1-y, 0);
890 set_image_pixel_at(img, K_MASK_, tw+1+x, img->h-1-y, 0);
891 }
892 }
893
894 /* Remove the mask from the four triangular areas outside the inscribed
895 hexagon (see add_hex_mask above). Fill in those triangles with data
896 copied from the diagonal edges just inside the hex area. This is
897 useful because it means we can do remove_hex_mask, scale up, and then
898 add_hex_mask, and the result will have smooth edges instead of
899 magnified jaggies from the lower resolution. Warning: caller must
900 load raw mono and color data before calling this, this function won't
901 do that. */
902
903 static void
remove_hex_mask(Image * img)904 remove_hex_mask(Image *img)
905 {
906 int tw, th, x, y, ylim, c1, c2, c3, c4, numbytes;
907
908 if (img == NULL)
909 return;
910 if (img->w > img->h)
911 return;
912
913 /* Ensure that binary version of mask exists. Abort if no mask. */
914 if (img->rawmaskdata == NULL) {
915 img->rawmaskdata = (char *)xmalloc(numbytes);
916 if (img->maskdata != lispnil)
917 interp_bytes(img->maskdata, numbytes, img->rawmaskdata, 0);
918 else
919 return;
920 }
921
922 /* Calculate size of triangles to unmask */
923 tw = img->w / 2 - 1;
924 th = img->h - calculate_hch(img->h) - 1;
925
926 /* Unmask the pixels */
927 for (x = 1; x <= tw; x++) {
928 ylim = (x*th) / tw;
929 c1 = image_pixel_at(img, K_MASK_, tw-x, ylim+1);
930 c2 = image_pixel_at(img, K_MASK_, tw+1+x, ylim+1);
931 c3 = image_pixel_at(img, K_MASK_, tw-x, img->h-2-ylim);
932 c4 = image_pixel_at(img, K_MASK_, tw+1+x, img->h-2-ylim);
933 for (y = 0; y <= ylim; y++) {
934 set_image_pixel_at(img, K_MASK_, tw-x, y, c1);
935 set_image_pixel_at(img, K_MASK_, tw+1+x, y, c2);
936 set_image_pixel_at(img, K_MASK_, tw-x, img->h-1-y, c3);
937 set_image_pixel_at(img, K_MASK_, tw+1+x, img->h-1-y, c4);
938 }
939 if (img->rawmonodata != NULL) {
940 c1 = image_pixel_at(img, K_MONO_, tw-x, ylim+1);
941 c2 = image_pixel_at(img, K_MONO_, tw+1+x, ylim+1);
942 c3 = image_pixel_at(img, K_MONO_, tw-x, img->h-2-ylim);
943 c4 = image_pixel_at(img, K_MONO_, tw+1+x, img->h-2-ylim);
944 for (y = 0; y <= ylim; y++) {
945 set_image_pixel_at(img, K_MONO_, tw-x, y, c1);
946 set_image_pixel_at(img, K_MONO_, tw+1+x, y, c2);
947 set_image_pixel_at(img, K_MONO_, tw-x, img->h-1-y, c3);
948 set_image_pixel_at(img, K_MONO_, tw+1+x, img->h-1-y, c4);
949 }
950 }
951 if (img->rawcolrdata != NULL) {
952 c1 = image_pixel_at(img, K_COLR_, tw-x, ylim+1);
953 c2 = image_pixel_at(img, K_COLR_, tw+1+x, ylim+1);
954 c3 = image_pixel_at(img, K_COLR_, tw-x, img->h-2-ylim);
955 c4 = image_pixel_at(img, K_COLR_, tw+1+x, img->h-2-ylim);
956 for (y = 0; y <= ylim; y++) {
957 set_image_pixel_at(img, K_COLR_, tw-x, y, c1);
958 set_image_pixel_at(img, K_COLR_, tw+1+x, y, c2);
959 set_image_pixel_at(img, K_COLR_, tw-x, img->h-1-y, c3);
960 set_image_pixel_at(img, K_COLR_, tw+1+x, img->h-1-y, c4);
961 }
962 }
963 }
964 }
965
966 /* Scale one layer of an image. */
967
968 static void
scale_image_layer(Image * imgin,Image * imgout,int layer,int use_mask,ModeFilter mf)969 scale_image_layer(Image *imgin, Image *imgout, int layer, int use_mask,
970 ModeFilter mf)
971 {
972 int u, v, x, y, xa, xb, ya, yb, tmp;
973
974 /* Perform a mode-filtered scaling operation. */
975
976 /* The way this works is that u and v are coordinates in the output image
977 rval. For each pixel (u,v) we compute the rectangle (xa..xb,ya..yb)
978 in the input image img, which rectangle covers all the pixels that are
979 touched by the pixel (u,v) when it's projected onto img, noting that
980 a pixel is defined to be closed on the sides with lesser coordinate
981 values and open on the sides with greater coordinate values. We
982 perform a mode filter over (xa..xb,ya..yb), that is, we find the
983 pixel value that occurs most commonly in that rectangle, splitting
984 ties by preferring the value that occurs first in reading order; the
985 result is the pixel value for the pixel (u,v). We do not count input
986 pixels that are masked out. If the scaling is by less than a factor
987 of 2 up or down, the result will be basically the same as
988 nearest-neighbour resampling. */
989
990 /* Loop for each pixel (u,v) */
991 for (v = 0; v < imgout->h; v++) {
992
993 /* Might as well calculate ya and yb here because v determines them */
994 ya = (v*imgin->h)/imgout->h;
995 yb = ((v+1)*imgin->h-1)/imgout->h;
996
997 for (u = 0; u < imgout->w; u++) {
998
999 /* Calculate xa and xb */
1000 xa = (u*imgin->w)/imgout->w;
1001 xb = ((u+1)*imgin->w-1)/imgout->w;
1002
1003 /* If the rectangle is small, and we don't have a mask, then
1004 skip the mode filter business because the first pixel wins. */
1005 if ((xb-xa+1)*(yb-ya+1) <= 2 && !use_mask) {
1006 set_image_pixel_at(imgout, layer, u, v,
1007 image_pixel_at(imgin, layer, xa, ya));
1008
1009 /* Otherwise we have to do the filter thing. */
1010 } else {
1011
1012 /* Look through the input rectangle and compute modes */
1013 for (y = yb; y >= ya; y--)
1014 for (x = xb; x >= xa; x--)
1015 if (!use_mask
1016 || image_pixel_at(imgin, K_MASK_, x, y) != 0)
1017 add_to_mf(mf, image_pixel_at(imgin, layer, x, y));
1018
1019 /* Set output pixel */
1020 set_image_pixel_at(imgout, layer, u, v, mode_of_mf(mf));
1021 zero_mf(mf);
1022 }
1023 }
1024 }
1025 }
1026
1027 /* Scale an image (or subimage). */
1028
1029 static void
scale_image(ImageFamily * imf,Image * img,Image * img2,int dhm)1030 scale_image(ImageFamily *imf, Image *img, Image *img2, int dhm)
1031 {
1032 int numbytes, numbytes2;
1033 ModeFilter mf;
1034
1035 /* Scale the embedding coordinates. */
1036 img2->embedx = (img->embedx*img2->w)/img->w;
1037 img2->embedy = (img->embedy*img2->h)/img->h;
1038 img2->embedw = (img->embedw*img2->w)/img->w;
1039 img2->embedh = (img->embedh*img2->h)/img->h;
1040
1041 /* Actually get the input image if we haven't yet. */
1042 if (img->rawcolrdata == NULL) {
1043 /* Try different ways to get some image data. */
1044 if (img->colrdata != lispnil) {
1045 numbytes = img->h * computed_rowbytes(img->w, img->pixelsize);
1046 img->rawcolrdata = (char *)xmalloc(numbytes);
1047 interp_bytes(img->colrdata, numbytes, img->rawcolrdata, 0);
1048 } else if (img->filedata != lispnil) {
1049 make_image_from_file_image(imf, img, img, 0);
1050 }
1051 }
1052
1053 /* Copy over a bunch of other fields. */
1054 img2->pixelsize = img->pixelsize;
1055 img2->palette = img->palette;
1056 img2->rawpalette = img->rawpalette;
1057 img2->numcolors = img->numcolors;
1058 img2->istile = img->istile;
1059 img2->isterrain = img->isterrain;
1060 img2->isconnection = img->isconnection;
1061 img2->isborder = img->isborder;
1062 img2->istransition = img->istransition;
1063 img2->hexgridx = img->hexgridx;
1064 img2->hexgridy = img->hexgridy;
1065
1066 /* Mark the image as having been computed rather than read in. */
1067 img2->synthetic = TRUE;
1068
1069 if (img->rawcolrdata != NULL) {
1070 numbytes2 = img2->h * computed_rowbytes(img2->w, img2->pixelsize);
1071 img2->rawcolrdata = (char *)xmalloc(numbytes2);
1072 }
1073 numbytes = img->h * computed_rowbytes(img->w, 1);
1074 numbytes2 = img2->h * computed_rowbytes(img2->w, 1);
1075
1076 /* Ensure that binary version of mono image exists. */
1077 make_raw_mono_data(img, FALSE);
1078 if (img->rawmonodata != NULL)
1079 img2->rawmonodata = (char *)xmalloc(numbytes2);
1080
1081 /* Ensure that binary version of mask exists. */
1082 if (img->rawmaskdata == NULL && img->maskdata != lispnil) {
1083 img->rawmaskdata = (char *)xmalloc(numbytes);
1084 interp_bytes(img->maskdata, numbytes, img->rawmaskdata, 0);
1085 }
1086 if (img->rawmaskdata != NULL)
1087 img2->rawmaskdata = (char *)xmalloc(numbytes2);
1088
1089 /* Tweak the input image's mask if appropriate */
1090 if (dhm)
1091 remove_hex_mask(img);
1092
1093 /* Do the scaling operation */
1094 mf = new_mf(256);
1095 if (img2->rawmaskdata != NULL) {
1096 if (img2->rawcolrdata != NULL)
1097 scale_image_layer(img, img2, K_COLR_, TRUE, mf);
1098 if (img2->rawmonodata != NULL)
1099 scale_image_layer(img, img2, K_MONO_, TRUE, mf);
1100 scale_image_layer(img, img2, K_MASK_, FALSE, mf);
1101 } else {
1102 if (img2->rawcolrdata != NULL)
1103 scale_image_layer(img, img2, K_COLR_, FALSE, mf);
1104 if (img2->rawmonodata != NULL)
1105 scale_image_layer(img, img2, K_MONO_, FALSE, mf);
1106 }
1107 delete_mf(mf);
1108
1109 /* Put the hex masks back */
1110 if (dhm) {
1111 add_hex_mask(img);
1112 add_hex_mask(img2);
1113 }
1114
1115 /* Set the image bounding box */
1116 compute_image_bbox(img2);
1117
1118 /* Recognize flat colors */
1119 if (img2->w == 1 && img2->h == 1 && img2->rawcolrdata != NULL) {
1120 if (img2->rawpalette == NULL)
1121 make_raw_palette(img2);
1122 img2->istile = TRUE;
1123 img2->r = img2->rawpalette[4 * img2->rawcolrdata[0] + 1];
1124 img2->g = img2->rawpalette[4 * img2->rawcolrdata[0] + 2];
1125 img2->b = img2->rawpalette[4 * img2->rawcolrdata[0] + 3];
1126 }
1127
1128 /* Call the interface hook */
1129 if (imf_interp_hook)
1130 (*imf_interp_hook)(imf, img2, FALSE);
1131 }
1132
1133 /* Scale image img in family imf to size w by h and add it to the family. */
1134
1135 static Image *
add_scaled_image(ImageFamily * imf,Image * img,int w,int h)1136 add_scaled_image(ImageFamily *imf, Image *img, int w, int h)
1137 {
1138 Image *rval;
1139 int i, dhm;
1140
1141 /* We must have a family and an input image, and the size must be sane. */
1142 if (imf == NULL || img == NULL || w <= 0 || h <= 0)
1143 return NULL;
1144
1145 /* Create a data structure for the output. */
1146 rval = get_img(imf, w, h);
1147
1148 /* Figure out whether to Do Hex Masking */
1149 dhm = (img->isterrain || img->isconnection
1150 || img->istransition) && (img->h >= img->w) && (h >= w);
1151
1152 /* Scale the main image */
1153 scale_image(imf, img, rval, dhm);
1154
1155 /* Handle subimages */
1156 if (img->numsubimages > 0 && img->subimages != NULL) {
1157 rval->numsubimages = img->numsubimages;
1158 rval->subimages
1159 = (Image **) xmalloc(img->numsubimages * sizeof(Image *));
1160 for (i = 0; i < img->numsubimages; i++) {
1161 rval->subimages[i] = get_subimg(imf, rval->w, rval->h);
1162 scale_image(imf, img->subimages[i], rval->subimages[i], dhm);
1163 }
1164 }
1165
1166 /* Return result */
1167 return rval;
1168 }
1169
1170 void
make_raw_mono_data(Image * img,int force)1171 make_raw_mono_data(Image *img, int force)
1172 {
1173 int numbytes = img->h * computed_rowbytes(img->w, 1);
1174
1175 if ((img->rawmonodata == NULL || force) && img->monodata != lispnil) {
1176 img->rawmonodata = (char *)xmalloc(numbytes);
1177 interp_bytes(img->monodata, numbytes, img->rawmonodata, 0);
1178 }
1179 }
1180
1181 /* Given a list of strings, interpret the hex digits and put the
1182 results at the given address. */
1183
1184 void
interp_bytes(Obj * datalist,int numbytes,char * destaddr,int jump)1185 interp_bytes(Obj *datalist, int numbytes, char *destaddr, int jump)
1186 {
1187 int i, j = 0;
1188 char *data = NULL;
1189
1190 for (i = 0; i < numbytes; ++i) {
1191 if (data == NULL || data[j] == '\0') {
1192 if (datalist == lispnil) {
1193 return;
1194 } else if (stringp(car(datalist))) {
1195 data = c_string(car(datalist));
1196 j = 0;
1197 } else {
1198 syntax_error(datalist, "Non-string in image data list");
1199 /* Have to give up now. */
1200 return;
1201 }
1202 datalist = cdr(datalist);
1203 }
1204 /* Just skip over slashes, which are for readability only. */
1205 if (data[j] == '/')
1206 ++j;
1207 destaddr[i] = hextoi(data[j]) * 16 + hextoi(data[j+1]);
1208 if (jump == 1 || (jump > 0 && i % jump == 0)) {
1209 i += jump;
1210 /* Be neat, put a zero in the location we're jumping over. */
1211 /* (doesn't work for jump > 1, but that never happens anymore?) */
1212 destaddr[i] = 0;
1213 }
1214 j += 2;
1215 }
1216 }
1217
1218 /* Compute a score describing how much scaling or tiling a given image will
1219 need to match a given size. */
1220
1221 static int
size_match_score(int wa,int ha,int wb,int hb)1222 size_match_score(int wa, int ha, int wb, int hb)
1223 {
1224 if (!wb) wb = 1; if (!wa) wa = 1; if (!hb) hb = 1; if (!ha) ha = 1;
1225 return (wa*100)/wb + (wb*100)/wa + (ha*100)/hb + (hb*100)/ha - 400;
1226 }
1227
1228 /* Try to find an image within the specified range and as close to the
1229 specified size as possible in the family. If no designer-specified
1230 image is available in the range, generate one of size w by h by
1231 scaling. */
1232
1233 Image *
best_image_in_range(ImageFamily * imf,int w,int h,int wmin,int hmin,int wmax,int hmax)1234 best_image_in_range(ImageFamily *imf, int w, int h, int wmin, int hmin,
1235 int wmax, int hmax)
1236 {
1237 Image *img, *best_nonsynth = NULL, *best_in_range = NULL,
1238 *best_tile = NULL, *exact_match = NULL;
1239 int best_nonsynth_score = INT_MAX, best_tile_score = INT_MAX,
1240 best_in_range_score = INT_MAX, s;
1241
1242 if (imf == NULL || imf->images == NULL)
1243 return NULL;
1244
1245 for_all_images(imf, img) {
1246
1247 /* Skip all basic terrain images except power 4 and 5
1248 if we are low on memory. */
1249 if (poor_memory
1250 && img->isterrain
1251 && img->w != 24
1252 && img->w != 44) {
1253 continue;
1254 }
1255
1256 /* Don't cross the isometric/non-isometric boundary */
1257 if (img->isterrain || img->isborder || img->isconnection ||
1258 img->istransition) {
1259 if (img->w > img->h && w <= h)
1260 continue;
1261 if (img->w <= img->h && w > h)
1262 continue;
1263 }
1264
1265 /* Check if there's an exact match - this is no longer an immediate
1266 success because it might be synthetic */
1267 if (img->w == w && img->h == h && !img->istile)
1268 exact_match = img;
1269
1270 /* Find best image that isn't synthetic or tile (scaling candidate) */
1271 if (!img->istile && !img->synthetic) {
1272 s = size_match_score(w, h, img->w, img->h);
1273 if (best_nonsynth == NULL || s < best_nonsynth_score) {
1274 best_nonsynth = img;
1275 best_nonsynth_score = s;
1276 }
1277
1278 /* Find best in range */
1279 if ((best_in_range == NULL || s < best_in_range_score)
1280 && img->w >= wmin && img->h >= hmin
1281 && img->w <= wmax && img->h <= hmax) {
1282 best_in_range = img;
1283 best_in_range_score = s;
1284 }
1285 }
1286
1287 /* Find best tile */
1288 if (img->istile) {
1289 s = size_match_score(w, h, img->w, img->h);
1290 if (best_tile == NULL || s < best_tile_score) {
1291 best_tile = img;
1292 best_tile_score = s;
1293 }
1294 }
1295 } /* for_all_images */
1296
1297 /* If we have a "best in range" then use it */
1298 if (best_in_range != NULL)
1299 return best_in_range;
1300
1301 /* If we have an exact size match, use that */
1302 if (exact_match != NULL)
1303 return exact_match;
1304
1305 /* If we have a tile, and we're low on memory, we can't scale, or
1306 the best scaling candidate isn't as good as the tile, use tile. */
1307 if (best_tile != NULL && (poor_memory || best_nonsynth == NULL ||
1308 best_tile_score < best_nonsynth_score))
1309 return best_tile;
1310
1311 /* Now we know we want to scale, so there had better exist a candidate.
1312 If not, we still might try looking for a flat colour. */
1313 if (best_nonsynth == NULL) {
1314 if ((w != 1) || (h != 1)) {
1315 return best_image_in_range(imf, 1, 1, 1, 1, 1, 1);
1316 } else {
1317 return NULL;
1318 }
1319 }
1320
1321 /* Scale the candidate. */
1322 return add_scaled_image(imf, best_nonsynth, w, h);
1323 }
1324
1325 Image *
smallest_image(ImageFamily * imf)1326 smallest_image(ImageFamily *imf)
1327 {
1328 Image *img, *smallest = NULL;
1329
1330 if (imf == NULL)
1331 return NULL;
1332 for_all_images(imf, img) {
1333 if (smallest == NULL || (img->w < smallest->w && img->h < smallest->h))
1334 smallest = img;
1335 }
1336 return smallest;
1337 }
1338
1339 Image *
largest_image(ImageFamily * imf)1340 largest_image(ImageFamily *imf)
1341 {
1342 Image *img, *largest = NULL;
1343
1344 if (imf == NULL)
1345 return NULL;
1346 for_all_images(imf, img) {
1347 if (largest == NULL || (img->w > largest->w && img->h > largest->h))
1348 largest = img;
1349 }
1350 return largest;
1351 }
1352
1353 /* Compute the right location for the given emblem and unit images. */
1354
1355 static int tmpbw; /* work around Think C bug */
1356
1357 int
emblem_position(Image * uimg,char * ename,ImageFamily * eimf,int sw,int sh,int vpuh,int vphh,int * exxp,int * eyyp,int * ewp,int * ehp)1358 emblem_position(Image *uimg, char *ename, ImageFamily *eimf, int sw, int sh,
1359 int vpuh, int vphh, int *exxp, int *eyyp, int *ewp, int *ehp)
1360 {
1361 int ew1, eh1, ex, ey, ew, eh, bx, by, bw, bh, overlap;
1362 Image *eimg;
1363
1364 /* Check if correct emblem is part of the unit's image, and don't draw
1365 if it is. */
1366 if (uimg
1367 && uimg->embedname
1368 && ename != NULL
1369 && strcmp(uimg->embedname, ename) == 0) {
1370 return FALSE;
1371 }
1372 /* (should use emblem bbox to help calc) */
1373 /* Get the size of the emblem, either from the image or by computing
1374 a reasonable default. */
1375 if (uimg && uimg->embedw > 0 && uimg->embedh > 0) {
1376 ew = uimg->embedw; eh = uimg->embedh;
1377 } else {
1378 ew1 = min(sw, max(8, sw / 4)); eh1 = min(sh, max(8, sh / 4));
1379 eimg = NULL;
1380 /* Look up the best emblem for the current zoom. */
1381 if (eimf != NULL) {
1382 eimg = best_image(eimf, ew1, eh1);
1383 }
1384 if (eimg) {
1385 ew = eimg->w; eh = eimg->h;
1386 /* Make a default 8x6 size for a solid color emblem. */
1387 if (ew == 1 && eh == 1) {
1388 ew = 8; eh = 6;
1389 }
1390 } else {
1391 ew = ew1; eh = eh1;
1392 }
1393 }
1394 /* Position the emblem, either explicitly, or default to UR corner
1395 (note that we need the emblem's width to do this) */
1396 if (uimg && uimg->embedx >= 0 && uimg->embedy >= 0) {
1397 ex = uimg->embedx; ey = uimg->embedy;
1398 /* Don't let the emblem stick out of the unit's area. */
1399 if (ex + ew > sw)
1400 ex = sw - ew;
1401 if (ey + eh > sh)
1402 ey = sh - eh;
1403 } else if (uimg && (uimg->bboxw != uimg->w || uimg->bboxh != uimg->h)) {
1404 overlap = FALSE;
1405 /* Scale bounding box by space given to image. */
1406 bx = (uimg->bboxx * sw) / uimg->w; by = (uimg->bboxy * sh) / uimg->h;
1407 tmpbw = (uimg->bboxw * sw) / uimg->w;
1408 bh = (uimg->bboxh * sh) / uimg->h;
1409 bw = tmpbw;
1410 /* Position the emblem outside the image's bbox if possible,
1411 moving in if necessary to stay inside the image's allowed
1412 area (sw x sh). */
1413 ex = bx + bw;
1414 if (ex + ew > sw) {
1415 /* Emblem too wide to fit between unit bbox and edge of
1416 area; butt it against edge of area, note the
1417 overlap. */
1418 ex = sw - ew;
1419 overlap = TRUE;
1420 }
1421 if (ex < 0)
1422 ex = 0;
1423 ey = by;
1424 if (overlap)
1425 ey -= eh;
1426 if (ey + eh > sh)
1427 ey = sh - eh;
1428 if (ey < 0)
1429 ey = 0;
1430 } else {
1431 ex = sw - ew; ey = 0;
1432 }
1433 /* Adjust for an oversized unit image. */
1434 if (sh > vpuh) {
1435 ey += (vphh - vpuh) / 2;
1436 #if (0)
1437 /* Tweaked by hand. */
1438 if (vpuh > 16) {
1439 ex -= 1;
1440 }
1441 #endif
1442 }
1443 /* Adjust for a shrunken unit image. */
1444 if (sh < vpuh) {
1445 ex = sw - ew;
1446 /* Tweaked by hand. */
1447 if (vpuh > 8) {
1448 ey = vpuh / 8;
1449 }
1450 }
1451 /* Return the results. */
1452 *exxp = ex; *eyyp = ey;
1453 *ewp = ew; *ehp = eh;
1454 return TRUE;
1455 }
1456
1457 /* Transform the masked-out part of an image into a single chosen
1458 color. */
1459
1460 void
blacken_masked_area(ImageFamily * imf,Image * img,int rd,int g,int b)1461 blacken_masked_area(ImageFamily *imf, Image *img, int rd, int g, int b)
1462 {
1463 int r, ri, rc, c, rmask;
1464 int rmi, rmc, rmmask;
1465 char *rp, *rmp;
1466 int black = -1;
1467 int rowsz = -1;
1468
1469 if (img->rawpalette == NULL)
1470 make_raw_palette(img);
1471 for (c = 0; c < img->numcolors; c++) {
1472 if (img->rawpalette[4 * c + 1] == rd
1473 && img->rawpalette[4 * c + 2] == g
1474 && img->rawpalette[4 * c + 3] == b) {
1475 black = c;
1476 break;
1477 }
1478 }
1479 if (black < 0
1480 && (1 << img->pixelsize) == img->numcolors
1481 && img->pixelsize < 8) {
1482 char *newdata, *nrp;
1483 int newpsize, nrmask, nri;
1484
1485 newpsize = img->pixelsize * 2;
1486 /* Don't need to mess with palette because it already has an
1487 additional spot allocated, and we only need the one. */
1488 rmask = (1 << img->pixelsize) - 1;
1489 ri = 8 - img->pixelsize;
1490 rp = img->rawcolrdata;
1491 newdata = (char *)xmalloc(img->h * computed_rowbytes(img->w, newpsize));
1492 nrmask = (1 << newpsize) - 1;
1493 nri = 8 - newpsize;
1494 nrp = newdata;
1495 for (r = 0; r < img->h; r++) {
1496 for (c = 0; c < img->w; c++) {
1497 rc = ((int) (*rp >> ri)) & rmask;
1498 /* OR the color data into its new location. */
1499 *nrp |= (char) (rc << nri);
1500 if (ri) {
1501 ri -= img->pixelsize;
1502 } else {
1503 ri = 8 - img->pixelsize;
1504 ++rp;
1505 }
1506 if (nri) {
1507 nri -= newpsize;
1508 } else {
1509 nri = 8 - newpsize;
1510 ++nrp;
1511 }
1512 }
1513 if ((img->pixelsize * img->w) % 8) {
1514 ri = 8 - img->pixelsize;
1515 ++rp;
1516 }
1517 if ((newpsize * img->w) % 8) {
1518 nri = 8 - newpsize;
1519 ++nrp;
1520 }
1521 }
1522 img->orig_pixelsize = img->pixelsize;
1523 img->pixelsize = newpsize;
1524 img->rawcolrdata = newdata;
1525 }
1526 /* If a spare color is available, put black there. */
1527 if (black < 0 && (1 << img->pixelsize) > img->numcolors) {
1528 black = img->numcolors;
1529 img->rawpalette[4 * black + 0] = black;
1530 img->rawpalette[4 * black + 1] = rd;
1531 img->rawpalette[4 * black + 2] = g;
1532 img->rawpalette[4 * black + 3] = b;
1533 ++(img->numcolors);
1534 }
1535 rmask = (1 << img->pixelsize) - 1;
1536 ri = 8 - img->pixelsize;
1537 rp = img->rawcolrdata;
1538 /* OK, now we're getting desperate; use the color in the upper left
1539 corner and pretend it's a transparent color. But, only if it
1540 appears to be the vertex of two edges of the same color. */
1541 /*! \note This is a stupid hack. In cases where the UL corner cannot
1542 be relied upon, the designer should have the option of
1543 specifying a 'mask-color' keyword with a RGB triplet. */
1544 if (black < 0) {
1545 black = ((int) *rp >> ri) & rmask;
1546 rowsz = computed_rowbytes(img->w, img->pixelsize);
1547 /* Check pixels in UR and LL corners. */
1548 /* If the corners don't match, then grab something from the LR
1549 corner and hope for the best. (This is very hackish!) */
1550 if ((black != (((int) rp[rowsz - 1] >> ri) & rmask))
1551 || (black != (((int) rp[rowsz * (img->h - 2)] >> ri) & rmask))) {
1552 black = ((int) rp[(rowsz * img->h) - 1] >> ri) & rmask;
1553 }
1554 img->rawpalette[4 * black + 1] = rd;
1555 img->rawpalette[4 * black + 2] = g;
1556 img->rawpalette[4 * black + 3] = b;
1557 }
1558 rmmask = 1;
1559 rmi = 7;
1560 rmp = img->rawmaskdata;
1561 for (r = 0; r < img->h; r++) {
1562 for (c = 0; c < img->w; c++) {
1563 rc = ((int) (*rp >> ri)) & rmask;
1564 rmc = ((int) (*rmp >> rmi)) & rmmask;
1565 if (rmc == 0) {
1566 /* Mask off the old value. */
1567 *rp &= (char) (~ (rmask << ri));
1568 /* Insert the color for black. */
1569 *rp |= (char) (black << ri);
1570 }
1571 if (ri) {
1572 ri -= img->pixelsize;
1573 } else {
1574 ri = 8 - img->pixelsize;
1575 ++rp;
1576 }
1577 if (rmi) {
1578 rmi -= 1;
1579 } else {
1580 rmi = 7;
1581 ++rmp;
1582 }
1583 }
1584 if ((img->pixelsize * img->w) % 8) {
1585 ri = 8 - img->pixelsize;
1586 ++rp;
1587 }
1588 if (img->w % 8) {
1589 rmi = 7;
1590 ++rmp;
1591 }
1592 }
1593 }
1594
1595 void
blacken_mono_masked_area(ImageFamily * imf,Image * img,int rd,int g,int b)1596 blacken_mono_masked_area(ImageFamily *imf, Image *img, int rd, int g, int b)
1597 {
1598 int r, ri, rc, c, rmask;
1599 int rmi, rmc, rmmask;
1600 char *rp, *rmp;
1601
1602 rmask = 1;
1603 ri = 7;
1604 rp = img->rawmonodata;
1605 rmmask = 1;
1606 rmi = 7;
1607 rmp = img->rawmaskdata;
1608 for (r = 0; r < img->h; r++) {
1609 for (c = 0; c < img->w; c++) {
1610 rc = ((int) (*rp >> ri)) & rmask;
1611 rmc = ((int) (*rmp >> rmi)) & rmmask;
1612 if (rmc == 0) {
1613 /* Mask off the old value. */
1614 *rp &= (char) (~ (rmask << ri));
1615 /* Insert the color for black. */
1616 *rp |= (char) (1 << ri);
1617 }
1618 if (ri) {
1619 ri -= 1;
1620 } else {
1621 ri = 7;
1622 ++rp;
1623 }
1624 if (rmi) {
1625 rmi -= 1;
1626 } else {
1627 rmi = 7;
1628 ++rmp;
1629 }
1630 }
1631 if (img->w % 8) {
1632 ri = 7;
1633 ++rp;
1634 }
1635 if (img->w % 8) {
1636 rmi = 7;
1637 ++rmp;
1638 }
1639 }
1640 }
1641
1642 void
make_raw_palette(Image * img)1643 make_raw_palette(Image *img)
1644 {
1645 int ipal[4][256];
1646 int c, ln;
1647 Obj *pal;
1648
1649 /* Parse the Lispified palette. */
1650 /* (should allocate and store directly instead of using ipal) */
1651 c = 0;
1652 for_all_list(img->palette, pal) {
1653 parse_lisp_palette_entry(car(pal), &ipal[0][c],
1654 &ipal[1][c], &ipal[2][c], &ipal[3][c]);
1655 c++;
1656 }
1657 img->numcolors = c;
1658 if (c == 0)
1659 return;
1660 /* store palette */
1661 img->rawpalette = (int *) xmalloc(257/*(img->numcolors + 1)*/ * 4 * sizeof(int));
1662 for (c = 0; c < img->numcolors; c++) {
1663 for (ln = 0; ln < 4; ln++) {
1664 img->rawpalette[4 * c + ln] = ipal[ln][c];
1665 }
1666 }
1667 }
1668
1669 /* The comparison function for the image list just does "strcmp" order
1670 and *requires* that all image families be named and named uniquely. */
1671
1672 static int
image_name_compare(CONST void * imf1,CONST void * imf2)1673 image_name_compare(CONST void *imf1, CONST void *imf2)
1674 {
1675 return strcmp((*((ImageFamily **) imf1))->name,
1676 (*((ImageFamily **) imf2))->name);
1677 }
1678
1679 void
sort_all_images(void)1680 sort_all_images(void)
1681 {
1682 qsort(&(images[0]), numimages, sizeof(ImageFamily *), image_name_compare);
1683 }
1684
1685 /* Check Lisp-format and binary-format data for consistency. */
1686
1687 void
check_imf(ImageFamily * imf)1688 check_imf(ImageFamily *imf)
1689 {
1690 Image *img;
1691
1692 if (imf == NULL)
1693 return;
1694 if (imf->name == NULL) {
1695 return;
1696 }
1697 for_all_images(imf, img) {
1698 /* Check consistency of Lisp and binary data. */
1699 if (img->colrdata != lispnil && img->rawcolrdata) {
1700 /* (should add color image comparison) */
1701 }
1702 if (img->monodata != lispnil && img->rawmonodata) {
1703 if (!bitmaps_match(img->w, img->h, img->monodata, img->rawmonodata))
1704 run_warning("mono bitmap data not consistent in %dx%d image of \"%s\"",
1705 img->w, img->h, imf->name);
1706 }
1707 if (img->maskdata != lispnil && img->rawmaskdata) {
1708 if (!bitmaps_match(img->w, img->h, img->maskdata, img->rawmaskdata))
1709 run_warning("mask bitmap data not consistent in %dx%d image of \"%s\"",
1710 img->w, img->h, imf->name);
1711 }
1712 }
1713 }
1714
1715 static int
bitmaps_match(int w,int h,Obj * lispdata,char * rawdata)1716 bitmaps_match(int w, int h, Obj *lispdata, char *rawdata)
1717 {
1718 int i, j = 0, rowbytes, numbytes, byte;
1719 char *datastr = NULL;
1720
1721 rowbytes = computed_rowbytes(w, 1);
1722 numbytes = h * rowbytes;
1723 for (i = 0; i < numbytes; ++i) {
1724 if (datastr == NULL || datastr[j] == '\0') {
1725 if (!stringp(car(lispdata)))
1726 break;
1727 datastr = c_string(car(lispdata));
1728 j = 0;
1729 lispdata = cdr(lispdata);
1730 }
1731 if (datastr[j] == '/')
1732 ++j;
1733 byte = hextoi(datastr[j]) * 16 + hextoi(datastr[j+1]);
1734 j += 2;
1735 if (byte != rawdata[i])
1736 return FALSE;
1737 }
1738 return TRUE;
1739 }
1740
1741 /* Write the imf directory for the given set of images. */
1742
1743 void
write_imf_dir(char * filename,ImageFamily ** imfimages,int num)1744 write_imf_dir(char *filename, ImageFamily **imfimages, int num)
1745 {
1746 int i;
1747 char *loc, *token, *delims;
1748 ImageFamily *imf;
1749 FILE *fp;
1750
1751 fp = open_file(filename, "w");
1752 if (fp != NULL) {
1753 fprintf(fp, "ImageFamilyName FileName\n");
1754 for (i = 0; i < num; ++i) {
1755 imf = images[i];
1756 loc = "???";
1757 if (imf->location && !empty_string(imf->location->name)) {
1758 /* First remove any Unix, Mac or Windows pathnames. */
1759 loc = copy_string(imf->location->name);
1760 delims = "/:\\";
1761 token = strtok(loc, delims);
1762 while (token != NULL) {
1763 loc = token;
1764 token = strtok(NULL, delims);
1765 }
1766 /* Remove any leading dots left from Unix pathnames. */
1767 loc += strspn(loc, ".");
1768 }
1769 fprintf(fp, "%s %s\n", imf->name, loc);
1770 /* (to write imf files, should scan through images once for
1771 each file, writing all images found that are in that file) */
1772 }
1773 fprintf(fp, ". .\n");
1774 fclose(fp);
1775 } else {
1776 run_warning("could not open file \"%s\" for writing", filename);
1777 }
1778 }
1779
1780 /* Write out the entire image family. */
1781
1782 void
write_imf(FILE * fp,ImageFamily * imf)1783 write_imf(FILE *fp, ImageFamily *imf)
1784 {
1785 Obj *palent, *posdata;
1786 Image *img;
1787
1788 if (fp == NULL || imf == NULL)
1789 return;
1790 if (imf->name == NULL) {
1791 fprintf(fp, "; garbage image family?\n");
1792 return;
1793 }
1794 if (imf->notes != lispnil) {
1795 fprintf(fp, "(%s \"%s\"", keyword_name(K_IMF), imf->name);
1796 fprintf(fp, "\n (%s ", keyword_name(K_NOTES));
1797 fprintlisp(fp, imf->notes);
1798 fprintf(fp, "))\n");
1799 }
1800 for_all_images(imf, img) {
1801 if (img->monodata != lispnil
1802 || img->maskdata != lispnil
1803 || img->colrdata != lispnil
1804 || img->filedata != lispnil
1805 || img->rawmonodata != NULL
1806 || img->rawmaskdata != NULL
1807 || img->rawcolrdata != NULL
1808 || (img->w == 1 && img->h == 1)) {
1809 /* Skip over synthesized images. */
1810 if (img->synthetic && !write_synthetic_also) {
1811 continue;
1812 }
1813 /* Skip over empty (undefined) 1 x 1 images. */
1814 if (img->w == 1
1815 && img->h == 1
1816 && img->rawpalette == NULL
1817 && img->palette == lispnil) {
1818 continue;
1819 }
1820 fprintf(fp, "(%s \"%s\"", keyword_name(K_IMF), imf->name);
1821 fprintf(fp, " (");
1822 fprintf(fp, "(%d %d", img->w, img->h);
1823 if (img->istile && !(img->w == 1 && img->h == 1))
1824 fprintf(fp, " %s", keyword_name(K_TILE));
1825 if (img->isterrain)
1826 fprintf(fp, " %s", keyword_name(K_TERRAIN));
1827 if (img->isconnection)
1828 fprintf(fp, " %s", keyword_name(K_CONNECTION));
1829 if (img->isborder)
1830 fprintf(fp, " %s", keyword_name(K_BORDER));
1831 if (img->istransition)
1832 fprintf(fp, " %s", keyword_name(K_TRANSITION));
1833 fprintf(fp, ")");
1834 if (img->numsubimages > 0) {
1835 fprintf(fp, " (%s %d", keyword_name(K_X), img->numsubimages);
1836 if (img->subx > 0 || img->suby > 0)
1837 fprintf(fp, " %d %d", img->subx, img->suby);
1838 fprintf(fp, ")");
1839 }
1840 if (img->hexgridx > 0 && img->hexgridy > 0) {
1841 fprintf(fp, " (%s %d %d)", keyword_name(K_HEXGRID),
1842 img->hexgridx, img->hexgridy);
1843 }
1844 if (img->embedname) {
1845 fprintf(fp, " (%s \"%s\")",
1846 keyword_name(K_EMBED), img->embedname);
1847 }
1848 if (img->embedx >= 0 && img->embedy >= 0) {
1849 fprintf(fp, " (%s %d %d)",
1850 keyword_name(K_EMBED_AT), img->embedx, img->embedy);
1851 }
1852 if (img->embedw >= 0 && img->embedh >= 0) {
1853 fprintf(fp, " (%s %d %d)",
1854 keyword_name(K_EMBED_SIZE), img->embedw, img->embedh);
1855 }
1856 if (img->notes != lispnil) {
1857 fprintf(fp, "\n (%s ", keyword_name(K_NOTES));
1858 fprintlisp(fp, img->notes);
1859 fprintf(fp, ")\n ");
1860 }
1861 /* Write a single color if that's what this image is. */
1862 if (img->w == 1 && img->h == 1) {
1863 if (img->rawpalette != NULL) {
1864 write_color(fp, -1,
1865 img->rawpalette[1],
1866 img->rawpalette[2],
1867 img->rawpalette[3]);
1868 } else if (img->palette != lispnil) {
1869 palent = cdr(car(img->palette));
1870 if (stringp(car(palent)) || symbolp(car(palent))) {
1871 fprintf(fp, " %s", c_string(car(palent)));
1872 } else {
1873 write_color(fp, -1,
1874 c_number(car(palent)),
1875 c_number(cadr(palent)),
1876 c_number(caddr(palent)));
1877 }
1878 }
1879 } else if (img->filedata != lispnil) {
1880 fprintf(fp, " (%s ", keyword_name(K_FILE));
1881 fprintf(fp, " \"%s\"", c_string(car(img->filedata)));
1882 posdata = cdr(img->filedata);
1883 if (posdata != lispnil) {
1884 if (symbolp(car(posdata))) {
1885 fprintf(fp, " %s", c_string(car(posdata)));
1886 posdata = cdr(posdata);
1887 }
1888 fprintf(fp, " %d %d",
1889 c_number(car(posdata)), c_number(cadr(posdata)));
1890 }
1891 fprintf(fp, ")");
1892 } else if ((img->colrdata != lispnil || img->rawcolrdata)
1893 && !color_matches_mono(img)) {
1894 fprintf(fp, "\n ");
1895 write_pixmap(fp, img->w, img->h, img->actualw, img->actualh,
1896 img->pixelsize, img->orig_pixelsize,
1897 img->palette, img->rawpalette, img->numcolors,
1898 img->colrdata, img->rawcolrdata);
1899 }
1900 if (img->monodata != lispnil || img->rawmonodata) {
1901 fprintf(fp, "\n ");
1902 write_bitmap(fp, keyword_name(K_MONO), img->w, img->h,
1903 img->monodata, img->rawmonodata);
1904 }
1905 if (img->maskdata != lispnil || img->rawmaskdata) {
1906 fprintf(fp, "\n ");
1907 write_bitmap(fp, keyword_name(K_MASK), img->w, img->h,
1908 img->maskdata, img->rawmaskdata);
1909 }
1910 fprintf(fp, "))\n");
1911 }
1912 }
1913 }
1914
1915 /* Study an ostensibly color image to see if its color table includes
1916 black and white only (white first, then black), and if its data is
1917 the same as the mono version of the image. */
1918
1919 static int
color_matches_mono(Image * img)1920 color_matches_mono(Image *img)
1921 {
1922 int i, cj, mj, rowbytes, numbytes, cbyte, mbyte;
1923 int col[2], red[2], grn[2], blu[2];
1924 char *cdatastr = NULL, *mdatastr = NULL;
1925 Obj *clispdata = img->colrdata, *mlispdata = img->monodata, *palette;
1926
1927 if (img->pixelsize != 1)
1928 return FALSE;
1929
1930 /* No match possible if not a black-white-only palette. */
1931 if (img->numcolors > 2)
1932 return FALSE;
1933
1934 if (img->rawpalette != NULL) {
1935 for (i = 0; i < 2; i++) {
1936 col[i] = img->rawpalette[4*i+0];
1937 red[i] = img->rawpalette[4*i+1];
1938 grn[i] = img->rawpalette[4*i+2];
1939 blu[i] = img->rawpalette[4*i+3];
1940 }
1941 } else if (img->palette != lispnil) {
1942 palette = img->palette;
1943 parse_lisp_palette_entry(car(palette), &col[0],
1944 &red[0], &grn[0], &blu[0]);
1945 if (cdr(palette) == lispnil) {
1946 /* If only one color in the palette, say the other one is
1947 black. */
1948 col[1] = 1;
1949 red[1] = grn[1] = blu[1] = 0;
1950 /* If the one color is black, say it's white. */
1951 if (col[0] == 0
1952 && red[0] < BLACK_THRESHOLD
1953 && grn[0] < BLACK_THRESHOLD
1954 && blu[0] < BLACK_THRESHOLD) {
1955 col[0] = 0;
1956 red[0] = grn[0] = blu[0] = 65535;
1957 }
1958 } else {
1959 /* Parse the second entry in the palette. */
1960 parse_lisp_palette_entry(cadr(palette), &col[1],
1961 &red[1], &grn[1], &blu[1]);
1962 }
1963 } else {
1964 return FALSE;
1965 }
1966
1967 if (!(col[0] == 0
1968 && red[0] > WHITE_THRESHOLD
1969 && grn[0] > WHITE_THRESHOLD
1970 && blu[0] > WHITE_THRESHOLD
1971 && col[1] == 1
1972 && red[1] < BLACK_THRESHOLD
1973 && grn[1] < BLACK_THRESHOLD
1974 && blu[1] < BLACK_THRESHOLD))
1975 return FALSE;
1976
1977 /* Now compare the contents. */
1978 rowbytes = computed_rowbytes(img->w, 1);
1979 numbytes = img->h * rowbytes;
1980 cj = mj = 0;
1981 for (i = 0; i < numbytes; ++i) {
1982 /* Extract one byte of the color image. */
1983 if (clispdata != lispnil) {
1984 if (cdatastr == NULL || cdatastr[cj] == '\0') {
1985 if (!stringp(car(clispdata)))
1986 break;
1987 cdatastr = c_string(car(clispdata));
1988 cj = 0;
1989 clispdata = cdr(clispdata);
1990 }
1991 if (cdatastr[cj] == '/')
1992 ++cj;
1993 cbyte = hextoi(cdatastr[cj]) * 16 + hextoi(cdatastr[cj+1]);
1994 cj += 2;
1995 } else if (img->rawcolrdata != NULL) {
1996 cbyte = (img->rawcolrdata)[i];
1997 } else {
1998 return FALSE;
1999 }
2000 /* Extract one byte of the mono image. */
2001 if (mlispdata != lispnil) {
2002 if (mdatastr == NULL || mdatastr[mj] == '\0') {
2003 if (!stringp(car(mlispdata)))
2004 break;
2005 mdatastr = c_string(car(mlispdata));
2006 mj = 0;
2007 mlispdata = cdr(mlispdata);
2008 }
2009 if (mdatastr[mj] == '/')
2010 ++mj;
2011 mbyte = hextoi(mdatastr[mj]) * 16 + hextoi(mdatastr[mj+1]);
2012 mj += 2;
2013 } else if (img->rawmonodata != NULL) {
2014 mbyte = (img->rawmonodata)[i];
2015 } else {
2016 return FALSE;
2017 }
2018 /* Compare the bytes. */
2019 if (cbyte != mbyte)
2020 return FALSE;
2021 }
2022 return TRUE;
2023 }
2024
2025 static void
write_pixmap(FILE * fp,int w,int h,int actualw,int actualh,int pixelsize,int orig_pixelsize,Obj * palette,int * rawpalette,int numcolors,Obj * lispdata,char * rawdata)2026 write_pixmap(FILE *fp, int w, int h, int actualw, int actualh,
2027 int pixelsize, int orig_pixelsize,
2028 Obj *palette, int *rawpalette, int numcolors,
2029 Obj *lispdata, char *rawdata)
2030 {
2031 int dolisp, i, j = 0, rowbytes, numbytes, byte;
2032 char *datastr = NULL;
2033
2034 actualw = (actualw != 0 ? actualw : w);
2035 actualh = (actualh != 0 ? actualh : h);
2036 dolisp = (lispdata != lispnil);
2037 /* If the pixel size was mangled (Windows) we need to use the original pixel size. */
2038 rowbytes = computed_rowbytes(actualw, (orig_pixelsize ? orig_pixelsize : pixelsize));
2039 numbytes = actualh * rowbytes;
2040 fprintf(fp, "(%s", keyword_name(K_COLOR));
2041 if (actualw != w || actualh != h)
2042 fprintf(fp, " (%s %d %d)", keyword_name(K_ACTUAL), actualw, actualh);
2043 /* (should not use orig_pixelsize if !dolisp?) Yes! See above. */
2044 fprintf(fp, " (%s %d)", keyword_name(K_PIXEL_SIZE),
2045 (orig_pixelsize ? orig_pixelsize : pixelsize));
2046 if (palette != lispnil || (rawpalette && numcolors))
2047 write_palette_contents(fp, palette, rawpalette, numcolors);
2048 fprintf(fp, "\n \"");
2049 for (i = 0; i < numbytes; ++i) {
2050 if (i > 0 && i % 32 == 0)
2051 fprintf(fp, "\"\n \"");
2052 if (i > 0 && i % 32 != 0 && i % rowbytes == 0)
2053 fprintf(fp, "/");
2054 if (dolisp) {
2055 if (datastr == NULL || datastr[j] == '\0') {
2056 if (!stringp(car(lispdata)))
2057 break;
2058 datastr = c_string(car(lispdata));
2059 j = 0;
2060 lispdata = cdr(lispdata);
2061 }
2062 if (datastr[j] == '/')
2063 ++j;
2064 byte = hextoi(datastr[j]) * 16 + hextoi(datastr[j+1]);
2065 j += 2;
2066 } else {
2067 byte = rawdata[i];
2068 }
2069 fprintf(fp, "%02x", (unsigned char) byte);
2070 }
2071 fprintf(fp, "\")");
2072 }
2073
2074 static void
write_bitmap(FILE * fp,char * subtyp,int w,int h,Obj * lispdata,char * rawdata)2075 write_bitmap(FILE *fp, char *subtyp, int w, int h, Obj *lispdata,
2076 char *rawdata)
2077 {
2078 int dolisp, i, j = 0, rowbytes, numbytes, byte;
2079 char *datastr = NULL;
2080
2081 /* Lisp data overrides raw byte data. */
2082 dolisp = (lispdata != lispnil);
2083 rowbytes = computed_rowbytes(w, 1);
2084 numbytes = h * rowbytes;
2085 fprintf(fp, "(%s", subtyp);
2086 if (w > 16 || h > 16)
2087 fprintf(fp, "\n ");
2088 fprintf(fp, " \"");
2089 for (i = 0; i < numbytes; ++i) {
2090 if (i > 0 && i % 32 == 0)
2091 fprintf(fp, "\"\n \"");
2092 if (i > 0 && i % 32 != 0 && i % rowbytes == 0)
2093 fprintf(fp, "/");
2094 if (dolisp) {
2095 if (datastr == NULL || datastr[j] == '\0') {
2096 if (!stringp(car(lispdata)))
2097 break;
2098 datastr = c_string(car(lispdata));
2099 j = 0;
2100 lispdata = cdr(lispdata);
2101 }
2102 /* Ignore any slashes, they're for human readability. */
2103 if (datastr[j] == '/')
2104 ++j;
2105 byte = hextoi(datastr[j]) * 16 + hextoi(datastr[j+1]);
2106 j += 2;
2107 } else {
2108 byte = rawdata[i];
2109 }
2110 fprintf(fp, "%02x", (unsigned char) byte);
2111 }
2112 fprintf(fp, "\")");
2113 }
2114
2115 static void
write_palette_contents(FILE * fp,Obj * palette,int * rawpalette,int numcolors)2116 write_palette_contents(FILE *fp, Obj *palette, int *rawpalette, int numcolors)
2117 {
2118 int len, i, col, red, grn, blu;
2119 Obj *restpal;
2120
2121 len = (palette != lispnil ? length(palette) : numcolors);
2122 if (len > 2)
2123 fprintf(fp, "\n ");
2124 fprintf(fp, " (%s", keyword_name(K_PALETTE));
2125 if (palette != lispnil) {
2126 for_all_list(palette, restpal) {
2127 parse_lisp_palette_entry(car(restpal), &col, &red, &grn, &blu);
2128 if (len > 2)
2129 fprintf(fp, "\n ");
2130 write_color(fp, col, red, grn, blu);
2131 }
2132 } else if (rawpalette != NULL) {
2133 for (i = 0; i < numcolors; i++) {
2134 if (len > 2)
2135 fprintf(fp, "\n ");
2136 write_color(fp, rawpalette[4*i],
2137 rawpalette[4*i+1], rawpalette[4*i+2], rawpalette[4*i+3]);
2138 }
2139 } else {
2140 fprintf(fp, " #| no palette? |# ");
2141 }
2142 fprintf(fp, ")");
2143 }
2144
2145 static void
write_color(FILE * fp,int n,int r,int g,int b)2146 write_color(FILE *fp, int n, int r, int g, int b)
2147 {
2148 char *colorname;
2149
2150 if (n >= 0)
2151 fprintf(fp, " (%d", n);
2152 colorname = find_color_name(r, g, b);
2153 if (!empty_string(colorname)) {
2154 /* Write color name. Note that we write as a symbol, so that
2155 each instance of "white" doesn't become a separate string. */
2156 fprintf(fp, " %s", colorname);
2157 } else {
2158 /* Write individual color components. */
2159 fprintf(fp, " %d %d %d", r, g, b);
2160 }
2161 if (n >= 0)
2162 fprintf(fp, ")");
2163 }
2164
2165 /* Given rgb components, return names of standard colors if the match
2166 is close. */
2167
2168 char *
find_color_name(int r,int g,int b)2169 find_color_name(int r, int g, int b)
2170 {
2171 if (r > WHITE_THRESHOLD
2172 && g > WHITE_THRESHOLD
2173 && b > WHITE_THRESHOLD)
2174 return "white";
2175 else if (r < BLACK_THRESHOLD
2176 && g < BLACK_THRESHOLD
2177 && b < BLACK_THRESHOLD)
2178 return "black";
2179 else if (r > WHITE_THRESHOLD
2180 && g < BLACK_THRESHOLD
2181 && b < BLACK_THRESHOLD)
2182 return "red";
2183 else if (r < BLACK_THRESHOLD
2184 && g > WHITE_THRESHOLD
2185 && b < BLACK_THRESHOLD)
2186 return "green";
2187 else if (r < BLACK_THRESHOLD
2188 && g < BLACK_THRESHOLD
2189 && b > WHITE_THRESHOLD)
2190 return "blue";
2191 else
2192 return NULL;
2193 }
2194
2195 void
parse_lisp_palette_entry(Obj * palentry,int * col,int * red,int * grn,int * blu)2196 parse_lisp_palette_entry(Obj *palentry, int *col, int *red, int *grn, int *blu)
2197 {
2198 Obj *colorcomp;
2199 char *colorname;
2200
2201 *col = c_number(car(palentry));
2202 colorcomp = cdr(palentry);
2203 if (colorcomp == lispnil)
2204 return;
2205 if (symbolp(car(colorcomp)) || stringp(car(colorcomp))) {
2206 colorname = c_string(car(colorcomp));
2207 *red = *grn = *blu = 0;
2208 if (strcmp(colorname, "white") == 0) {
2209 *red = *grn = *blu = 65535;
2210 } else if (strcmp(colorname, "black") == 0) {
2211 /* done */
2212 } else if (strcmp(colorname, "red") == 0) {
2213 *red = 65535;
2214 } else if (strcmp(colorname, "green") == 0) {
2215 *grn = 65535;
2216 } else if (strcmp(colorname, "blue") == 0) {
2217 *blu = 65535;
2218 } else {
2219 init_warning("No color named \"%s\" found, substituting gray",
2220 colorname);
2221 *red = *grn = *blu = 128 * 256;
2222 }
2223 } else if (numberp(car(colorcomp))) {
2224 *red = c_number(car(colorcomp));
2225 *grn = c_number(cadr(colorcomp));
2226 *blu = c_number(caddr(colorcomp));
2227 /* Assume small values are 8-bit rather than 16-bit colors.
2228 (Works because as 16-bit colors, they would all be nearly
2229 identical shades of black.) */
2230 if (*red < 256)
2231 *red *= 256;
2232 if (*grn < 256)
2233 *grn *= 256;
2234 if (*blu < 256)
2235 *blu *= 256;
2236 } else {
2237 init_warning("palette color info is not a name or set of numbers, ignoring");
2238 }
2239 }
2240
2241 /* Given a filename, find or create a file image structure for it. */
2242
get_file_image(char * fname)2243 FileImage *get_file_image(char *fname)
2244 {
2245 FileImage *fimg, *newfimg;
2246
2247 for (fimg = file_images; fimg != NULL; fimg = fimg->next) {
2248 if (strcmp(fimg->name, fname) == 0)
2249 return fimg;
2250 }
2251 newfimg = (FileImage *) xmalloc(sizeof(FileImage));
2252 newfimg->name = fname;
2253 newfimg->next = file_images;
2254 file_images = newfimg;
2255 return newfimg;
2256 }
2257
2258 /* Collect the file image for the given image and use it to generate
2259 the image's (or subimage's) raw data. */
2260
2261 void
make_image_from_file_image(ImageFamily * imf,Image * img,Image * subimg,int subi)2262 make_image_from_file_image(ImageFamily *imf, Image *img, Image *subimg,
2263 int subi)
2264 {
2265 int hch, pad = 2, stdlayout = FALSE;
2266 int xoffset, yoffset, xoff, yoff;
2267 Obj *posdata;
2268 FileImage *fimg;
2269
2270 if (img->filedata != lispnil) {
2271 if (img->file_image == NULL) {
2272 img->file_image = get_file_image(c_string(car(img->filedata)));
2273 }
2274 }
2275 fimg = img->file_image;
2276 load_file_image(fimg);
2277 if (!fimg->loaded) {
2278 init_warning("Could not load file \"%s\" for image family \"%s\", ignoring",
2279 fimg->name, imf->name);
2280 return;
2281 }
2282 xoffset = yoffset = 0;
2283 stdlayout = FALSE;
2284 posdata = cdr(img->filedata);
2285 if (posdata != lispnil) {
2286 if (match_keyword(car(posdata), K_STD)) {
2287 stdlayout = TRUE;
2288 posdata = cdr(posdata);
2289 }
2290 xoffset = c_number(car(posdata));
2291 yoffset = c_number(cadr(posdata));
2292 if (stdlayout) {
2293 xoffset = (xoffset * (img->w + 2)) + 2;
2294 yoffset = (yoffset * (img->h + 2)) + 2;
2295 }
2296 }
2297 /* All subimages share color data. */
2298 if (subimg == img ||
2299 (img->subimages != NULL && subimg == img->subimages[0])) {
2300 subimg->pixelsize = 8;
2301 /* Copy the palette over verbatim from the file image. */
2302 subimg->numcolors = fimg->numcolors;
2303 subimg->rawpalette =
2304 (int *) xmalloc(257 /*subimg->numcolors*/ * 4 * sizeof(int));
2305 memcpy(subimg->rawpalette, fimg->palette,
2306 subimg->numcolors * 4 * sizeof(int));
2307 } else {
2308 /* Inherit color data from the first subimage. */
2309 subimg->pixelsize = img->subimages[0]->pixelsize;
2310 subimg->numcolors = img->subimages[0]->numcolors;
2311 subimg->rawpalette = img->subimages[0]->rawpalette;
2312 }
2313 if (img->isborder) {
2314 hch = calculate_hch(img->h);
2315 xoff = xoffset + (subi % 4) * img->w;
2316 yoff = yoffset + (subi / 4) * hch;
2317 copy_from_file_image(subimg, fimg, xoff, yoff, 0, hch);
2318 } else if (img->isconnection) {
2319 xoff = xoffset + (subi % 8) * (img->w + pad);
2320 yoff = yoffset + (subi / 8) * (img->h + pad);
2321 copy_from_file_image(subimg, fimg, xoff, yoff, 0, 0);
2322 } else if (img->istransition) {
2323 xoff = xoffset + (subi % 4) * (img->w + pad);
2324 yoff = yoffset + (subi / 4) * (img->h + pad);
2325 copy_from_file_image(subimg, fimg, xoff, yoff, 0, 0);
2326 } else if (img->numsubimages > 0) {
2327 /* Figure where the next subimage is at. */
2328 if (img->hexgridx > 0 && img->hexgridy > 0) {
2329 xoff = xoffset + (subi%img->hexgridx) * img->w;
2330 if ((subi/img->hexgridx)%2 != 0)
2331 xoff += (img->w/2);
2332 yoff = yoffset + (subi/img->hexgridx) * ((img->h*3)/4 + 1);
2333 } else if (img->subx != 0 || img->suby != 0) {
2334 xoff = xoffset + subi * img->subx;
2335 yoff = yoffset + subi * img->suby;
2336 } else {
2337 xoff = xoffset + subi * img->w;
2338 yoff = yoffset;
2339 }
2340 copy_from_file_image(subimg, fimg, xoff, yoff, 0, 0);
2341 } else {
2342 copy_from_file_image(img, fimg, xoffset, yoffset, 0, 0);
2343 }
2344
2345 /* If this is any sort of terrain, apply a hex mask, and blow away
2346 the LISP version of the mask if it no longer matches */
2347 if (img->isterrain || img->isconnection ||
2348 img->istransition) {
2349 add_hex_mask(subimg);
2350 if (subimg->maskdata != lispnil && !bitmaps_match(subimg->w, subimg->h,
2351 subimg->maskdata, subimg->rawmaskdata))
2352 subimg->maskdata = lispnil;
2353 }
2354 }
2355
2356 /* Extract a single color image from a file image. Also create mask
2357 data for the image, if the file image has any transparent colors. */
2358
2359 void
copy_from_file_image(Image * img,FileImage * fimg,int xoffset,int yoffset,int actualw,int actualh)2360 copy_from_file_image(Image *img, FileImage *fimg, int xoffset, int yoffset,
2361 int actualw, int actualh)
2362 {
2363 char pix;
2364 int i, j, k, ii, val, kk, kkb;
2365
2366 /* Make space for the color data (assuming 1 byte/pixel). */
2367 img->rawcolrdata = (char *) xmalloc(img->w * img->h);
2368 if (fimg->numtransparent > 0)
2369 img->rawmaskdata =
2370 (char *) xmalloc(img->h * computed_rowbytes(img->w, 1));
2371 /* Scan through all the pixels of the subimage we're building. */
2372 for (i = 0; i < img->h; ++i) {
2373 for (j = 0; j < img->w; ++j) {
2374 k = (yoffset + i) * fimg->width + xoffset + j;
2375 pix = fimg->data[k];
2376 kk = i * img->w + j;
2377 img->rawcolrdata[kk] = pix;
2378 if ((actualw > 0 && j >= actualw)
2379 || (actualh > 0 && i >= actualh))
2380 img->rawcolrdata[kk] = img->rawcolrdata[0];
2381 /* If there are transparent colors in the image, modify
2382 the mask bitmap. */
2383 if (fimg->numtransparent > 0) {
2384 val = 1;
2385 for (ii = 0; ii < fimg->numtransparent; ++ii) {
2386 if (pix == fimg->transparent[ii]) {
2387 val = 0;
2388 break;
2389 }
2390 }
2391 if (val) {
2392 kkb = i * computed_rowbytes(img->w, 1) + j / 8;
2393 img->rawmaskdata[kkb] |= 1 << (7 - j % 8);
2394 if ((actualw > 0 && j >= actualw)
2395 || (actualh > 0 && i >= actualh))
2396 img->rawmaskdata[kkb] = 0;
2397 }
2398 }
2399 }
2400 }
2401 }
2402
2403 /* Give a file image, attempt to load it into memory. */
2404
2405 void
load_file_image(FileImage * fimg)2406 load_file_image(FileImage *fimg)
2407 {
2408 int rslt;
2409
2410 if (fimg->loaded)
2411 return;
2412 /* Only doing GIFs right now. */
2413 rslt = get_gif(fimg);
2414 if (rslt)
2415 fimg->loaded = TRUE;
2416 }
2417
2418 /* Generic image setup. */
2419
2420 static short imf_dir_loaded;
2421
2422 ImageFamily *
get_generic_images(char * name)2423 get_generic_images(char *name)
2424 {
2425 FILE *fp;
2426 ImageFamily *imf;
2427 LibraryPath *path;
2428
2429 imf = get_imf(name);
2430 if (imf == NULL)
2431 return NULL;
2432 if (imf->numsizes > 0 && imf_interp_hook != NULL)
2433 imf = (*imf_interp_hook)(imf, NULL, FALSE);
2434 if (imf_load_hook != NULL)
2435 imf = (*imf_load_hook)(imf);
2436 /* Always check that the imf location is loaded
2437 (fixes emblem loading from saved games on the mac). */
2438 if (imf->numsizes == 0
2439 || (imf->location && imf->location->loaded != TRUE)) {
2440 /* Maybe collect the names/locations of all image families. */
2441 if (!imf_dir_loaded) {
2442 /* (should let name be decided by platform?) */
2443 fp = open_library_file("imf.dir");
2444 if (fp != NULL) {
2445 load_image_families(fp, FALSE, NULL);
2446 fclose(fp);
2447 } else {
2448 init_warning("Cannot open \"%s\", will not use it", "imf.dir");
2449 }
2450 imf_dir_loaded = TRUE;
2451 }
2452 /* Get a (possibly empty) family. */
2453 imf = get_imf(name);
2454 if (imf == NULL)
2455 return NULL;
2456 if (imf->location != NULL) {
2457 /* Load data filling in the family. */
2458 for_all_library_paths(path) {
2459 make_pathname(path->path, imf->location->name, "", spbuf);
2460 if (load_imf_file(spbuf, NULL)) {
2461 imf->location->loaded = TRUE;
2462 break;
2463 }
2464 }
2465 /* Maybe try the plain filename, just in case. */
2466 if (!imf->location->loaded) {
2467 imf->location->loaded =
2468 load_imf_file(imf->location->name, NULL);
2469 }
2470 /* We don't complain here about not finding the file, because
2471 we'll get a more useful warning alert later on. */
2472 if (imf_interp_hook != NULL)
2473 imf = (*imf_interp_hook)(imf, NULL, FALSE);
2474 }
2475 }
2476 return imf;
2477 }
2478