1 /* Copyright (C) 1992 Nathan Sidwell */
2 /* RCS $Id: scram.c,v 4.7 1993/11/09 16:27:47 nathan Stable $ */
3 /*{{{  about me*/
4 /* a simple program to compress the monochrome, mask & color sprites
5  * together into one set of planes. This is done by color separation
6  * overlay. Not particularly well written or documented, but then
7  * its not supposed to be a general tool.
8  * If anybody wants to make it more general, they are welcome
9  * args
10  * -n invert noswap
11  * +n add edge to noswap
12  * -s swap is ~noswap
13  * +s swap is same as noswap
14  * -m mask is solid (and therefore not present)
15  * -c insert noswap as first color plane
16  * -0 no monochrome
17  * -[1-9] no monochrome, set color planes
18  */
19 /*}}}*/
20 #include "ansiknr.h"
21 /*{{{  includes*/
22 #include <assert.h>
23 #include <stdio.h>
24 #include <stddef.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #ifdef TRANSPUTER
29 #include <iocntrl.h>
30 #else
31 #include <unistd.h>
32 #endif /* TRANSPUTER */
33 /*}}}*/
34 #define SUFFIXMONO  ".bw"
35 #define SUFFIXCOLOR ".rgb"
36 #define MAX_PLANES 5
37 #define MAX_COLORS (1 << MAX_PLANES)
38 /*{{{  typedef struct Coord*/
39 typedef struct Coord
40 /* general coordinate store */
41 {
42   int       x;
43   int       y;
44 } COORD;
45 /*}}}*/
46 /*{{{  typedef struct Size*/
47 typedef struct Size
48 /* general size store */
49 {
50   unsigned  x;
51   unsigned  y;
52 } SIZE;
53 /*}}}*/
54 /*{{{  typedef struct Bitmap*/
55 typedef struct Bitmap
56 {
57   char      *bits;    /* bit pattern */
58   SIZE      size;     /* size in pixels */
59   COORD     hot;      /* hot spot */
60   unsigned  scan;     /* scan line */
61 } BITMAP;
62 /*}}}*/
63 static BITMAP source;
64 static BITMAP current;
65 static BITMAP scratch;
66 static SIZE size;
67 static BITMAP color_bitmap;
68 static int color_planes = 0;
69 static unsigned long mono_noswap;
70 static unsigned long mono_swap;
71 static BITMAP bitmaps[MAX_COLORS];
72 static char *noswap_colors[MAX_COLORS];
73 static char *swap_colors[MAX_COLORS];
74 static int colors;
75 static char *filename;
76 static int error = 0;
77 #define GC_COPY 0
78 #define GC_AND  1
79 #define GC_XOR  2
80 #define GC_OR   3
81 #define GC_CLEAR 4
82 /*{{{  void *smalloc(size)*/
83 static VOID *smalloc
84 FUNCARG((size),
85 	size_t    size
86 )
87 {
88   VOID      *ptr;
89 
90   ptr = malloc(size);
91   if(!ptr)
92     {
93       fprintf(stderr, "Malloc failed\n");
94       exit(1);
95     }
96   return ptr;
97 }
98 /*}}}*/
99 /*{{{  unsigned read_bitmap(name, bitmap)*/
100 static unsigned read_bitmap
101 FUNCARG((name, bitmap),
102 	char CONST *name      /* name of file */
103 ARGSEP  BITMAP    *bitmap     /* bitmap ptr */
104 )
105   /* state 0    new block
106    * state 1    comment
107    * state 2    {
108    * state 3    reading
109    * state 4    done
110    */
111 {
112   unsigned  error;
113   FILE      *stream;
114   unsigned  state;
115   char      line[128];
116   SIZE      place;
117   unsigned  extra;
118 
119   stream = fopen(name, "r");
120   if(!stream)
121     return 1;
122   error = 0;
123   extra = 0;
124   bitmap->size.x = bitmap->size.y = bitmap->scan = 0;
125   bitmap->hot.x = bitmap->hot.y = -1;
126   bitmap->bits = NULL;
127   state = 0;
128   while(fgets(line, sizeof(line), stream))
129     {
130       char      *ptr;
131 
132       ptr = line;
133       while(*ptr == ' ' || *ptr == '\t')
134 	ptr++;
135       if(*ptr == '\n')
136 	continue;
137       /*{{{  new state?*/
138       if(state == 0)
139 	{
140 	  if(ptr[0] == '/' && ptr[1] == '*')
141 	    {
142 	      ptr += 2;
143 	      state = 1;
144 	    }
145 	  else if(!strncmp(ptr, "#define", 7))
146 	    /*{{{  define*/
147 	    {
148 	      /* some poor saps don't have offsetof */
149 	      /*{{{  static char CONST *table[] =*/
150 	      static char CONST *table[] =
151 	      {
152 		"width", "height", "x_hot", "y_hot"
153 	      };
154 	      /*}}}*/
155 	      size_t    length;
156 	      unsigned  ix;
157 	      char CONST **tptr;
158 
159 	      ptr += 8;
160 	      while(*ptr == ' ' || *ptr == '\t')
161 		ptr++;
162 	      for(length = 0; ptr[length] != ' ' && ptr[length] != '\t' &&
163 		  ptr[length]; length++)
164 		/* EMPTY */;
165 	      for(tptr = table, ix = 4; ix--; tptr++)
166 		{
167 		  size_t    len;
168 
169 		  len = strlen(*tptr);
170 		  if(length >= len && !strncmp(&ptr[length - len], *tptr, len))
171 		    {
172 		      int     value;
173 
174 		      ptr += length;
175 		      while(*ptr == ' ' || *ptr == '\t')
176 			ptr++;
177 		      value = strtol(ptr, &ptr, 0);
178 		      switch(ix)
179 		      {
180 			case 3:
181 			  bitmap->size.x = value;
182 			  break;
183 			case 2:
184 			  bitmap->size.y = value;
185 			  break;
186 			case 1:
187 			  bitmap->hot.x = value;
188 			  break;
189 			case 0:
190 			  bitmap->hot.y = value;
191 			  break;
192 		      }
193 		      bitmap->scan = (bitmap->size.x + 7) / 8;
194 		      break;
195 		    }
196 		}
197 	    }
198 	    /*}}}*/
199 	  else
200 	    {
201 	      char      *bptr;
202 
203 	      bptr = strchr(ptr, '[');
204 	      if(bptr && bptr - ptr > 3 && !strncmp(bptr - 4, "bits", 4) &&
205 		  bptr[1] == ']')
206 		{
207 		  state = 2;
208 		  ptr = bptr;
209 		}
210 	    }
211 	}
212       /*}}}*/
213       /*{{{  in comment?*/
214       if(state == 1)
215 	{
216 	  while(*ptr && state)
217 	    {
218 	      while(*ptr && *ptr != '*')
219 		ptr++;
220 	      if(*ptr)
221 		{
222 		  if(ptr[1] == '/')
223 		    {
224 		      ptr += 2;
225 		      state = 0;
226 		      break;
227 		    }
228 		  else
229 		    ptr++;
230 		}
231 	    }
232 	}
233       /*}}}*/
234       /*{{{  {?*/
235       if(state == 2)
236 	{
237 	  ptr = strchr(ptr, '{');
238 	  if(ptr)
239 	    {
240 	      int       count;
241 
242 	      state = 3;
243 	      ptr++;
244 	      if(!bitmap->size.x || !bitmap->size.y)
245 		{
246 		  error = 1;
247 		  break;
248 		}
249 	      count = bitmap->scan * bitmap->size.y;
250 	      bitmap->bits = smalloc(count);
251 	      place.x = place.y = 0;
252 	      for(; count--;)
253 		bitmap->bits[count] = 0;
254 	    }
255 	}
256       /*}}}*/
257       /*{{{  reading?*/
258       if(state == 3)
259 	{
260 	  while(state != 4 && *ptr && *ptr != '\n')
261 	    {
262 	      while(*ptr == ' ' || *ptr == '\t')
263 		ptr++;
264 	      if(*ptr == '}')
265 		state = 4;
266 	      else
267 		/*{{{  item*/
268 		{
269 		  unsigned  val;
270 
271 		  val = strtol(ptr, &ptr, 0);
272 		  if(*ptr == ',')
273 		    ptr++;
274 		  if(place.y != bitmap->size.y)
275 		    {
276 		      bitmap->bits[place.y * bitmap->scan + place.x] = val;
277 		      place.x++;
278 		      if(place.x == bitmap->scan)
279 			{
280 			  place.x = 0;
281 			  place.y++;
282 			}
283 		    }
284 		  else if(!extra)
285 		    {
286 		      fprintf(stderr, "Warning: Extra on end of bitmap '%s'\n",
287 			name);
288 		      extra = 1;
289 		    }
290 		}
291 		/*}}}*/
292 	    }
293 	}
294       /*}}}*/
295     }
296   error |= ferror(stream) || !bitmap->bits;
297   fclose(stream);
298   return error;
299 }
300 /*}}}*/
301 /*{{{  void malloc_bitmap(bitmap, x, y)*/
302 static VOIDFUNC malloc_bitmap
303 FUNCARG((bitmap, x, y),
304 	BITMAP    *bitmap
305 ARGSEP  unsigned  x
306 ARGSEP  unsigned  y
307 )
308 {
309   unsigned    ix;
310 
311   bitmap->size.x = x;
312   bitmap->size.y = y;
313   bitmap->hot.x = bitmap->hot.y = -1;
314   bitmap->scan = (x + 7) / 8;
315   ix = bitmap->scan * y;
316   bitmap->bits = smalloc(ix);
317   while(ix--)
318     bitmap->bits[ix] = 0;
319   return;
320 }
321 /*}}}*/
322 /*{{{  unsigned is_blank(bitmap)*/
323 static unsigned is_blank
324 FUNCARG((bitmap),
325 	BITMAP    *bitmap
326 )
327 {
328   unsigned  ix;
329   unsigned  bits;
330 
331   bits = 0;
332   for(ix = bitmap->scan * bitmap->size.y; ix--;)
333     bits |= bitmap->bits[ix] & 0xFF;
334   return !bits;
335 }
336 /*}}}*/
337 /*{{{  void fill_area(bitmap, gc, x, y, w, h)*/
338 static VOIDFUNC fill_area
339 FUNCARG((bitmap, gc, x, y, w, h),
340 	BITMAP    *bitmap
341 ARGSEP  unsigned  gc
342 ARGSEP  int       x
343 ARGSEP  int       y
344 ARGSEP  unsigned  w
345 ARGSEP  unsigned  h
346 )
347 {
348   unsigned  ix;
349   unsigned  iy;
350   int       count;
351 
352   assert(!(x & 7) && w && h);
353   assert(x >= 0 && y >= 0 && x + w <= bitmap->size.x &&
354       y + h <= bitmap->size.y);
355   for(iy = 0; h--; iy++)
356     {
357       char    *bptr;
358 
359       bptr = &bitmap->bits[iy * bitmap->scan];
360       for(ix = x / 8, count = w; count > 0; ix++, count -= 8)
361 	{
362 	  unsigned  mask;
363 
364 	  mask = count > 7 ? 0xFF : 0xFF >> (8 - count);
365 	  switch(gc)
366 	  {
367 	    case GC_COPY:
368 	      bptr[ix] |= mask;
369 	      break;
370 	    case GC_CLEAR:
371 	      bptr[ix] &= ~mask;
372 	      break;
373 	    case GC_XOR:
374 	      bptr[ix] ^= mask;
375 	      break;
376 	    case GC_OR:
377 	      bptr[ix] |= mask;
378 	      break;
379 	  }
380 	}
381     }
382   return;
383 }
384 /*}}}*/
385 /*{{{  void copy_area(source, dest, gc, sx, sy, w, h, dx, dy)*/
386 static VOIDFUNC copy_area
387 FUNCARG((source, dest, gc, sx, sy, w, h, dx, dy),
388 	BITMAP    *source
389 ARGSEP  BITMAP    *dest
390 ARGSEP  unsigned  gc
391 ARGSEP  int       sx
392 ARGSEP  int       sy
393 ARGSEP  unsigned  w
394 ARGSEP  unsigned  h
395 ARGSEP  int       dx
396 ARGSEP  int       dy
397 )
398 {
399   unsigned  ix;
400   unsigned  iy;
401   char      *sptr;
402   char      *dptr;
403   char      line[16];
404   int       count;
405 
406   assert(sx >= 0 && sy >= 0 && sx + w <= source->size.x &&
407       sy + h <= source->size.y);
408   assert(dx >= 0 && dy >= 0 && dx + w <= dest->size.x &&
409       dy + h <= dest->size.y);
410   assert(w < sizeof(line) * 8);
411   for(iy = h; iy--;)
412     {
413       sptr = &source->bits[(sy + iy) * source->scan + sx / 8];
414       /*{{{  set source pointer*/
415       if(sx & 7)
416 	{
417 	  for(ix = 0, count = w; count > 0; ix++, count -= 8)
418 	    {
419 	      line[ix] = (sptr[ix] & 0xFF) >> (sx & 7);
420 	      if(ix)
421 		line[ix - 1] |= (sptr[ix] & 0xFF) << (8 - (sx & 7));
422 	      else
423 		count += (sx & 7);
424 	    }
425 	  sptr = line;
426 	}
427       /*}}}*/
428       dptr = &dest->bits[(dy + iy) * dest->scan + dx / 8];
429       for(ix = 0, count = w; count > 0; ix++)
430 	{
431 	  unsigned  mask;
432 	  unsigned  shift;
433 
434 	  shift = dx & 7;
435 	  mask = (0xFF << shift) & 0xFF;
436 	  if(count < 8 - shift)
437 	    mask &= 0xFF >> ((8 - shift) - count);
438 	  /*{{{  first half*/
439 	  switch(gc)
440 	  {
441 	    case GC_COPY:
442 	      dptr[ix] &= ~mask;
443 	      dptr[ix] |= ((sptr[ix] & 0xFF) << shift) & mask;
444 	      break;
445 	    case GC_AND:
446 	      dptr[ix] &= (((sptr[ix] & 0xFF) << shift) & mask) | ~mask;
447 	      break;
448 	    case GC_OR:
449 	      dptr[ix] |= ((sptr[ix] & 0xFF) << shift) & mask;
450 	      break;
451 	    case GC_XOR:
452 	      dptr[ix] ^= ((sptr[ix] & 0xFF) << shift) & mask;
453 	      break;
454 	  }
455 	  /*}}}*/
456 	  count -= 8 - shift;
457 	  if(shift && count > 0)
458 	    {
459 	      mask = count >= 8 ? 0xFF : 0xFF >> (8 - count);
460 	      mask >>= 8 - shift;
461 	      /*{{{  second half*/
462 	      switch(gc)
463 	      {
464 		case GC_COPY:
465 		  dptr[ix + 1] &= ~mask;
466 		  dptr[ix + 1] |= ((sptr[ix] & 0xFF) >> (8 - shift)) & mask;
467 		  break;
468 		case GC_AND:
469 		  dptr[ix + 1] &= (((sptr[ix] & 0xFF) >> (8 - shift)) &
470 		      mask) | ~mask;
471 		  break;
472 		case GC_OR:
473 		  dptr[ix + 1] |= ((sptr[ix] & 0xFF) >> (8 - shift)) & mask;
474 		  break;
475 		case GC_XOR:
476 		  dptr[ix + 1] ^= ((sptr[ix] & 0xFF) >> (8 - shift)) & mask;
477 		  break;
478 	      }
479 	      /*}}}*/
480 	      count -= shift;
481 	    }
482 	}
483     }
484   return;
485 }
486 /*}}}*/
487 /*{{{  void draw_rectangle(bitmap, gc, x, y, w, h)*/
488 static VOIDFUNC draw_rectangle
489 FUNCARG((bitmap, gc, x, y, w, h),
490 	BITMAP    *bitmap
491 ARGSEP  unsigned  gc
492 ARGSEP  int       x
493 ARGSEP  int       y
494 ARGSEP  unsigned  w
495 ARGSEP  unsigned  h
496 )
497 {
498   unsigned  ix;
499   unsigned  iy;
500   int       count;
501   char      *bptr;
502   unsigned  mask;
503 
504   assert(w & h && !(x & 7));
505   assert(x >= 0 && y >= 0 && x + w < bitmap->size.x && y + h < bitmap->size.y);
506   bptr = &bitmap->bits[y * bitmap->scan];
507   /*{{{  top and bottom edges*/
508   for(ix = x / 8, count = w + 1; count > 0; ix++, count -= 8)
509     {
510       mask = count > 7 ? 0xFF : 0xFF >> (8 - count);
511       /*{{{  edge*/
512       switch(gc)
513       {
514 	case GC_COPY:
515 	case GC_OR:
516 	  bptr[ix] |= mask;
517 	  bptr[ix + h * bitmap->scan] |= mask;
518 	  break;
519 	case GC_CLEAR:
520 	  bptr[ix] &= ~mask;
521 	  bptr[ix + h * bitmap->scan] &= ~mask;
522 	  break;
523 	case GC_XOR:
524 	  bptr[ix] ^= mask;
525 	  bptr[ix + h * bitmap->scan] ^= mask;
526 	  break;
527 	case GC_AND:
528 	  break;
529       }
530       /*}}}*/
531     }
532   /*}}}*/
533   /*{{{  left and right edges*/
534   for(iy = y + 1, count = h - 2; count > 0; iy++, count--)
535     {
536       bptr = &bitmap->bits[iy * bitmap->scan + x / 8];
537       mask = 1 << (x & 7);
538       /*{{{  left edge*/
539       switch(gc)
540       {
541 	case GC_OR:
542 	case GC_COPY:
543 	  *bptr |= mask;
544 	  break;
545 	case GC_XOR:
546 	  *bptr ^= mask;
547 	  break;
548 	case GC_AND:
549 	  break;
550 	case GC_CLEAR:
551 	  *bptr &= ~mask;
552 	  break;
553       }
554       /*}}}*/
555       bptr = &bitmap->bits[iy * bitmap->scan + (x + w) / 8];
556       mask = 1 << ((x + w) & 7);
557       /*{{{  right edge*/
558       switch(gc)
559       {
560 	case GC_OR:
561 	case GC_COPY:
562 	  *bptr |= mask;
563 	  break;
564 	case GC_XOR:
565 	  *bptr ^= mask;
566 	  break;
567 	case GC_AND:
568 	  break;
569 	case GC_CLEAR:
570 	  *bptr &= ~mask;
571 	  break;
572       }
573       /*}}}*/
574     }
575   /*}}}*/
576   return;
577 }
578 /*}}}*/
579 /*{{{  void make_color_pixmaps(char **name, BITMAP *bitmaps, char CONST *title)*/
580 static VOIDFUNC make_color_pixmaps
581 FUNCARG((name, bitmaps, title),
582 	char      **name
583 ARGSEP  BITMAP    *bitmaps
584 ARGSEP  char CONST *title
585 )
586 {
587   unsigned ix;
588 
589   for(ix = 0; name[ix]; ix++)
590     if(ix >= (1 << color_planes))
591       fill_area(&bitmaps[ix], GC_CLEAR, 0, 0, size.x, size.y);
592     else
593       {
594 	unsigned bit;
595 
596 	copy_area(&source, &bitmaps[ix], GC_COPY, 0, 0, size.x, size.y, 0, 0);
597 	for(bit = 0; bit != color_planes; bit++)
598 	  if((1 << bit) & ix)
599 	    {
600 	      copy_area(&color_bitmap, &bitmaps[ix], GC_AND,
601 		0, (int)(size.y * bit), size.x, size.y, 0, 0);
602 	    }
603 	  else
604 	    {
605 	      copy_area(&color_bitmap, &current, GC_COPY,
606 		0, (int)(size.y * bit), size.x, size.y, 0, 0);
607 	      fill_area(&current, GC_XOR, 0, 0, size.x, size.y);
608 	      copy_area(&current, &bitmaps[ix], GC_AND,
609 		  0, 0, size.x, size.y, 0, 0);
610 	    }
611       }
612   copy_area(&source, &current, GC_COPY, 0, 0, size.x, size.y, 0, 0);
613   for(ix = 0; name[ix]; ix++)
614     copy_area(&bitmaps[ix], &current, GC_XOR, 0, 0, size.x, size.y, 0, 0);
615   if(!is_blank(&current))
616     {
617       unsigned  x, y;
618 
619       error = 1;
620       fprintf(stderr, "%s: Error on %s at", filename, title);
621       for(y = 0; y != size.y; y++)
622 	for(x = 0; x != size.x; x++)
623 	  if(current.bits[y * current.scan + x / 8] & 1 << (x & 7))
624 	    fprintf(stderr, " (%d, %d)", x, y);
625       fprintf(stderr, "\n");
626     }
627   return;
628 }
629 /*}}}*/
630 /*{{{  void add_edge(unsigned ix, BITMAP *bitmaps)*/
631 static VOIDFUNC add_edge
632 FUNCARG((ix, bitmaps),
633 	unsigned ix
634 ARGSEP  BITMAP *bitmaps
635 )
636 {
637   copy_area(&source, &current, GC_COPY, 0, 0, size.x, size.y, 0, 0);
638   copy_area(&source, &current, GC_AND, 0, 0, size.x, size.y - 1, 0, 1);
639   copy_area(&source, &current, GC_AND, 0, 1, size.x, size.y - 1, 0, 0);
640   copy_area(&source, &current, GC_AND, 0, 0, size.x - 1, size.y, 1, 0);
641   copy_area(&source, &current, GC_AND, 1, 0, size.x - 1, size.y, 0, 0);
642   draw_rectangle(&current, GC_CLEAR, 0, 0, size.x - 1, size.y - 1);
643   copy_area(&source, &current, GC_XOR, 0, 0, size.x, size.y, 0, 0);
644   copy_area(&current, &bitmaps[ix], GC_COPY, 0, 0, size.x, size.y, 0, 0);
645   fill_area(&current, GC_XOR, 0, 0, size.x, size.y);
646   while(ix--)
647     copy_area(&current, &bitmaps[ix], GC_AND, 0, 0, size.x, size.y, 0, 0);
648   return;
649 }
650 /*}}}*/
651 /*{{{  void compress_colors(char **name, BITMAP *bitmaps, char CONST *title)*/
652 static VOIDFUNC compress_colors
653 FUNCARG((name, bitmaps, title),
654 	char **name
655 ARGSEP  BITMAP *bitmaps
656 ARGSEP  char CONST *title
657 )
658 {
659   unsigned  ix, j;
660 
661   for(ix = 0; name[ix + 1]; ix++)
662     {
663       for(j = ix + 1; name[j]; j++)
664 	if(!strcmp(name[ix], name[j]))
665 	  {
666 	    copy_area(&bitmaps[j], &bitmaps[ix], GC_OR,
667 		0, 0, size.x, size.y, 0, 0);
668 	    fill_area(&bitmaps[j], GC_CLEAR, 0, 0, size.x, size.y);
669 	    name[j] = (char *)"-";
670 	  }
671     }
672   for(ix = 0; name[ix]; ix++)
673     {
674       if(is_blank(&bitmaps[ix]))
675 	{
676 	  BITMAP old;
677 
678 	  if(strcmp(name[ix], "-"))
679 	    fprintf(stderr, "Warning: %s color '%s' has no pixels\n",
680 		title, name[ix]);
681 	  memcpy(&old, &bitmaps[ix], sizeof(BITMAP));
682 	  for(j = ix; name[j + 1]; j++)
683 	    {
684 	      name[j] = name[j+1];
685 	      memcpy(&bitmaps[j], &bitmaps[j+1], sizeof(BITMAP));
686 	    }
687 	  name[j] = NULL;
688 	  memcpy(&bitmaps[j], &old, sizeof(BITMAP));
689 	  ix--;
690 	}
691       else if(!strcmp(name[ix], "-"))
692       {
693 	unsigned  x, y;
694 
695 	error = 1;
696 	fprintf(stderr, "%s: Void color on %s at", filename, title);
697 	for(y = 0; y != size.y; y++)
698 	  for(x = 0; x != size.x; x++)
699 	    if(bitmaps[ix].bits[y * bitmaps[ix].scan + x / 8] & (1 << x & 7))
700 	      fprintf(stderr, " (%d, %d)", x, y);
701 	fprintf(stderr, "\n");
702       }
703     }
704   return;
705 }
706 /*}}}*/
707 /*{{{  void merge_mono()*/
708 static VOIDFUNC merge_mono FUNCARGVOID
709 {
710   unsigned ix;
711 
712   for(ix = 1; ix != colors; ix++)
713     {
714       if(is_blank(&source))
715 	break;
716       copy_area(&source, &current, GC_COPY, 0, 0, size.x, size.y, 0, 0);
717       copy_area(&bitmaps[ix], &current, GC_AND, 0, 0, size.x, size.y, 0, 0);
718       if(!is_blank(&current))
719 	{
720 	  mono_swap |= (unsigned long)1 << ix;
721 	  copy_area(&current, &scratch, GC_COPY,
722 	      0, 0, size.x, size.y, 0, 0);
723 	  copy_area(&bitmaps[ix], &scratch, GC_XOR,
724 	      0, 0, size.x, size.y, 0, 0);
725 	  if(!is_blank(&scratch))
726 	    {
727 	      copy_area(&bitmaps[ix], &bitmaps[colors], GC_COPY,
728 		  0, 0, size.x, size.y, 0, 0);
729 	      copy_area(&current, &bitmaps[colors], GC_XOR,
730 		  0, 0, size.x, size.y, 0, 0);
731 	      noswap_colors[colors] = noswap_colors[ix];
732 	      swap_colors[colors] = swap_colors[ix];
733 	      mono_noswap = mono_noswap |
734 		  (((mono_noswap >> ix) & 1) << colors);
735 	      copy_area(&current, &bitmaps[ix], GC_COPY,
736 		  0, 0, size.x, size.y, 0, 0);
737 	      colors++;
738 	    }
739 	  copy_area(&bitmaps[ix], &source, GC_XOR,
740 	      0, 0, size.x, size.y, 0, 0);
741 	}
742     }
743   return;
744 }
745 /*}}}*/
746 /*{{{  int main(int argc, char **argv)*/
747 int main
748 FUNCARG((argc, argv),
749 	int argc
750 ARGSEP  char **argv
751 )
752 {
753   BITMAP    mono_bitmap;
754   BITMAP    mono_source;
755   int       planes;
756   char      *edge = NULL;
757   char      *noswap[MAX_COLORS + 1];
758   char      *swap[MAX_COLORS + 1];
759   BITMAP    noswap_bitmaps[MAX_COLORS];
760   BITMAP    swap_bitmaps[MAX_COLORS];
761   int       status;
762   unsigned  ix;
763   BITMAP    output_bitmap;
764   unsigned  noswap_edge = 0;
765   unsigned  noswap_invert = 0;
766   unsigned  swap_invert = 0;
767   unsigned  swap_copy = 0;
768   unsigned  no_mono = 0;
769   unsigned  mono_planes;
770   unsigned  color_copy = 0;
771   unsigned  solid_mask = 0;
772 
773   /*{{{  slurp up the arguments*/
774   {
775     filename = argv[1];
776     if(argc > 2)
777       {
778 	argv += 2;
779 	argc -= 2;
780 	for(; argc; argc--, argv++)
781 	  {
782 	    if(argv[0][0] == '+')
783 	      {
784 		if(argv[0][2])
785 		  error = 1;
786 		else if(argv[0][1] == 's')
787 		  swap_copy = 1;
788 		else if(argv[0][1] == 'n')
789 		  noswap_edge = 1;
790 		else
791 		  error = 1;
792 	      }
793 	    else if(argv[0][0] == '-')
794 	      {
795 		if(!argv[0][1])
796 		  break;
797 		else if(argv[0][2])
798 		  error = 1;
799 		else if(argv[0][1] == 's')
800 		  swap_invert = 1;
801 		else if(argv[0][1] == 'n')
802 		  noswap_invert = 1;
803 		else if(argv[0][1] == 'c')
804 		  color_copy = 1;
805 		else if(argv[0][1] == 'm')
806 		  solid_mask = 1;
807 		else if(argv[0][1] == '0')
808 		  no_mono = 1;
809 		else if(isdigit(argv[0][1]))
810 		  {
811 		    no_mono = 1;
812 		    color_planes = argv[0][1] - '0';
813 		  }
814 		else
815 		  error = 1;
816 	      }
817 	    else
818 	      break;
819 	    if(error)
820 	      {
821 		fprintf(stderr, "'%s' not a flag\n", argv[0]);
822 		argc = 0;
823 		break;
824 	      }
825 	  }
826       }
827     for(ix = 0; argc && strcmp(*argv, "+"); argc--, argv++)
828       noswap[ix++] = *argv;
829     if(argc)
830       argv++, argc--;
831     while(ix != MAX_COLORS + 1)
832       noswap[ix++] = NULL;
833     for(ix = 0; argc && strcmp(*argv, "+"); argc--, argv++)
834       swap[ix++] = *argv;
835     if(argc)
836       argv++, argc--;
837     while(ix != MAX_COLORS + 1)
838       swap[ix++] = NULL;
839     if(argc)
840       {
841 	edge = *argv;
842 	argv++, argc--;
843       }
844     if(argc || !filename || (no_mono &&
845 	(noswap_edge || noswap_invert || swap_invert || swap_copy)))
846       {
847 	fprintf(stderr,
848 	  "Usage: scram <name> [-<n>|[+n][+s][-n][-s]] <noswap> + <swap> + [<edge>]\n");
849 	return 1;
850       }
851   }
852   /*}}}*/
853   if(no_mono)
854     mono_planes = 1;
855   else
856     {
857       mono_planes = noswap_edge | noswap_invert | swap_copy | swap_invert ?
858 	  2 : 3;
859       if(solid_mask)
860 	mono_planes--;
861     }
862   /*{{{  read files*/
863   {
864     char *name;
865 
866     name = smalloc(strlen(filename) + 7);
867     strcpy(name, filename);
868     strcat(name, SUFFIXMONO);
869     status = read_bitmap(name, &mono_source);
870     size.x = mono_source.size.x;
871     size.y = mono_source.size.y;
872     if(status || size.y % mono_planes)
873       {
874 	fprintf(stderr, "Cannot read monochrome '%s'\n", name);
875 	return 1;
876       }
877     size.y /= mono_planes;
878     strcpy(name, filename);
879     strcat(name, SUFFIXCOLOR);
880     status = read_bitmap(name, &color_bitmap);
881     if(status)
882       {
883 	fprintf(stderr, "Cannot read colour '%s'\n", name);
884 	return 1;
885       }
886     if(color_planes)
887       size.y = color_bitmap.size.y / color_planes;
888     else
889       color_planes = color_bitmap.size.y / size.y;
890     if(color_bitmap.size.y % size.y || color_bitmap.size.x != size.x)
891       {
892 	fprintf(stderr, "Bitmaps are incompatible sizes\n");
893 	return 1;
894       }
895     free(name);
896   }
897   /*}}}*/
898   /*{{{  alloc some bitmaps*/
899   {
900     malloc_bitmap(&source, size.x, size.y);
901     malloc_bitmap(&current, size.x, size.y);
902     malloc_bitmap(&scratch, size.x, size.y);
903     malloc_bitmap(&mono_bitmap, size.x, size.y * 2);
904   }
905   /*}}}*/
906   /*{{{  solid mask?*/
907   if(solid_mask)
908     {
909       BITMAP  temp;
910 
911       if(!no_mono)
912 	mono_planes++;
913       malloc_bitmap(&temp, size.x, size.y * mono_planes);
914       copy_area(&mono_source, &temp, GC_COPY, 0, 0,
915 	  size.x, size.y * (mono_planes - 1), 0, size.y);
916       fill_area(&temp, GC_COPY, 0, 0, size.x, size.y);
917       memcpy(&temp.hot, &mono_source.hot, sizeof(COORD));
918       memcpy(&mono_source, &temp, sizeof(BITMAP));
919     }
920   /*}}}*/
921   /*{{{  copy color?*/
922   if(color_copy)
923     {
924       BITMAP temp;
925 
926       color_planes++;
927       malloc_bitmap(&temp, size.x, size.y * color_planes);
928       copy_area(&color_bitmap, &temp, GC_COPY, 0, 0,
929 	  size.x, size.y * (color_planes - 1), 0, size.y);
930       copy_area(&mono_source, &temp, GC_COPY,
931 	  0, size.y, size.x, size.y, 0, 0);
932       memcpy(&color_bitmap, &temp, sizeof(BITMAP));
933     }
934   /*}}}*/
935   /*{{{  alloc a pile of bitmaps*/
936   {
937     for(ix = 0; ix < MAX_COLORS; ix++)
938       {
939 	malloc_bitmap(&bitmaps[ix], size.x, size.y);
940 	fill_area(&bitmaps[ix], GC_CLEAR, 0, 0, size.x, size.y);
941       }
942     for(ix = 0; ix < MAX_COLORS; ix++)
943       {
944 	malloc_bitmap(&noswap_bitmaps[ix], size.x, size.y);
945 	fill_area(&noswap_bitmaps[ix], GC_CLEAR, 0, 0, size.x, size.y);
946       }
947     for(ix = 0; ix < MAX_COLORS; ix++)
948       {
949 	malloc_bitmap(&swap_bitmaps[ix], size.x, size.y);
950 	fill_area(&swap_bitmaps[ix], GC_CLEAR, 0, 0, size.x, size.y);
951       }
952   }
953   /*}}}*/
954   colors = !no_mono;
955   if(!no_mono)
956     {
957       /*{{{  fiddle mono noswap*/
958       {
959 	copy_area(&mono_source, &source, GC_COPY,
960 	    0, (int)size.y, size.x, size.y, 0, 0);
961 	if(noswap_invert)
962 	  fill_area(&source, GC_XOR, 0, 0, size.x, size.y);
963 	if(noswap_edge)
964 	  {
965 	    copy_area(&mono_source, &current, GC_COPY, 0, 0,
966 		size.x, size.y, 0, 0);
967 	    copy_area(&mono_source, &current, GC_AND, 0, 0,
968 		size.x, size.y - 1, 0, 1);
969 	    copy_area(&mono_source, &current, GC_AND, 0, 1,
970 		size.x, size.y - 1, 0, 0);
971 	    copy_area(&mono_source, &current, GC_AND, 0, 0,
972 		size.x - 1, size.y, 1, 0);
973 	    copy_area(&mono_source, &current, GC_AND, 1, 0,
974 		size.x - 1, size.y, 0, 0);
975 	    draw_rectangle(&current, GC_CLEAR, 0, 0, size.x - 1, size.y - 1);
976 	    fill_area(&current, GC_XOR, 0, 0, size.x, size.y);
977 	    copy_area(&current, &source, GC_OR, 0, 0, size.x, size.y, 0, 0);
978 	  }
979 	copy_area(&mono_source, &source, GC_AND, 0, 0, size.x, size.y, 0, 0);
980 	copy_area(&source, &mono_bitmap, GC_COPY,
981 	    0, 0, size.x, size.y, 0, 0);
982       }
983       /*}}}*/
984       /*{{{  fiddle mono swap*/
985       {
986 	copy_area(&mono_source, &source, GC_COPY,
987 	    0, (int)(size.y * (mono_planes - 1)), size.x, size.y, 0, 0);
988 	if(swap_invert)
989 	  fill_area(&source, GC_XOR, 0, 0, size.x, size.y);
990 	copy_area(&mono_source, &source, GC_AND,
991 	    0, 0, size.x, size.y, 0, 0);
992 	copy_area(&source, &mono_bitmap, GC_COPY,
993 	    0, 0, size.x, size.y, 0, (int)size.y);
994       }
995       /*}}}*/
996     }
997   copy_area(&mono_source, &source, GC_COPY, 0, 0, size.x, size.y, 0, 0);
998   make_color_pixmaps(noswap, noswap_bitmaps, "Noswap");
999   if(edge)
1000     {
1001       for(ix = 0; noswap[ix]; ix++)
1002 	/*EMPTY*/;
1003       add_edge(ix, noswap_bitmaps);
1004       noswap[ix] = edge;
1005     }
1006   copy_area(&mono_source, &source, GC_COPY, 0, 0, size.x, size.y, 0, 0);
1007   make_color_pixmaps(swap, swap_bitmaps, "Swap");
1008   compress_colors(noswap, noswap_bitmaps, "Noswap");
1009   compress_colors(swap, swap_bitmaps, "Swap");
1010   /*{{{  merge the noswap colors*/
1011   {
1012     for(ix = 0; noswap[ix]; ix++)
1013       {
1014 	copy_area(&noswap_bitmaps[ix], &bitmaps[ix + colors], GC_COPY,
1015 	  0, 0, size.x, size.y, 0, 0);
1016 	noswap_colors[ix + colors] = noswap[ix];
1017       }
1018     colors = ix + colors;
1019   }
1020   /*}}}*/
1021   /*{{{  merge the swap colors*/
1022   for(ix = 0; swap[ix]; ix++)
1023     {
1024       unsigned j;
1025 
1026       for(j = 0; j != colors; j++)
1027 	{
1028 	  if(is_blank(&swap_bitmaps[ix]))
1029 	    break;
1030 	  copy_area(&swap_bitmaps[ix], &current, GC_COPY,
1031 	      0, 0, size.x, size.y, 0, 0);
1032 	  copy_area(&bitmaps[j], &current, GC_AND,
1033 	      0, 0, size.x, size.y, 0, 0);
1034 	  if(!is_blank(&current))
1035 	    {
1036 	      swap_colors[j] = swap[ix];
1037 	      copy_area(&current, &scratch, GC_COPY,
1038 		  0, 0, size.x, size.y, 0, 0);
1039 	      copy_area(&bitmaps[j], &scratch, GC_XOR,
1040 		  0, 0, size.x, size.y, 0, 0);
1041 	      if(!is_blank(&scratch))
1042 		{
1043 		  copy_area(&bitmaps[j], &bitmaps[colors], GC_COPY,
1044 		      0, 0, size.x, size.y, 0, 0);
1045 		  copy_area(&current, &bitmaps[colors], GC_XOR,
1046 		      0, 0, size.x, size.y, 0, 0);
1047 		  noswap_colors[colors] = noswap_colors[j];
1048 		  copy_area(&current, &bitmaps[j], GC_COPY,
1049 		      0, 0, size.x, size.y, 0, 0);
1050 		  colors++;
1051 		}
1052 	      copy_area(&bitmaps[j], &swap_bitmaps[ix], GC_XOR,
1053 		  0, 0, size.x, size.y, 0, 0);
1054 	    }
1055 	}
1056     }
1057   /*}}}*/
1058   if(!no_mono)
1059     {
1060       /*{{{  merge the noswap mono*/
1061       {
1062 	copy_area(&mono_bitmap, &source, GC_COPY, 0, 0, size.x, size.y, 0, 0);
1063 	merge_mono();
1064 	mono_noswap = mono_swap;
1065 	mono_swap = 0;
1066       }
1067       /*}}}*/
1068       /*{{{  merge the swap mono*/
1069       {
1070 	copy_area(&mono_bitmap, &source, GC_COPY,
1071 	    0, (int)size.y, size.x, size.y, 0, 0);
1072 	merge_mono();
1073       }
1074       /*}}}*/
1075       /*{{{  add mono color masks*/
1076       {
1077 	char st[20];
1078 
1079 	sprintf(st, "0x%lx", mono_noswap);
1080 	noswap_colors[0] = strcpy(smalloc(strlen(st) + 1), st);
1081 	sprintf(st, "0x%lx", mono_swap);
1082 	swap_colors[0] = strcpy(smalloc(strlen(st) + 1), st);
1083       }
1084       /*}}}*/
1085     }
1086   /*{{{  create the output bitmap*/
1087   {
1088     unsigned mask;
1089 
1090     for(mask = 1, planes = 0; mask < colors; mask <<= 1)
1091       planes++;
1092     fprintf(stderr, "Creating %s.%s with %d colors on %d planes\n", filename,
1093 	no_mono ? "packed" : "h", colors, planes);
1094     malloc_bitmap(&output_bitmap, size.x, size.y * planes);
1095     fill_area(&output_bitmap, GC_CLEAR, 0, 0, size.x, size.y * planes);
1096     for(ix = 0; ix != colors; ix++)
1097       {
1098 	int bit;
1099 
1100 	for(bit = 0; bit != planes; bit++)
1101 	  if(ix & (1 << bit))
1102 	    copy_area(&bitmaps[ix], &output_bitmap, GC_OR,
1103 		0, 0, size.x, size.y, 0, (int)(bit * size.y));
1104       }
1105   }
1106   /*}}}*/
1107   /*{{{  write the file*/
1108   {
1109     char *name;
1110     FILE  *stream;
1111     char *ptr;
1112     unsigned  x, y;
1113     unsigned  count;
1114     unsigned  ix;
1115 
1116     name = smalloc(strlen(filename) + 8);
1117     strcpy(name, filename);
1118     strcat(name, no_mono ? ".packed" : ".h");
1119     ptr = strrchr(filename, '/');
1120     if(!ptr)
1121       ptr = filename;
1122     else
1123       ptr++;
1124     stream = fopen(name, "w");
1125     if(!stream)
1126       {
1127 	fprintf(stderr, "Cannot write file\n");
1128 	return 1;
1129       }
1130     fprintf(stream, "#define %s_width %d\n", ptr, (int)size.x);
1131     fprintf(stream, "#define %s_height %d\n", ptr, (int)(size.y * planes));
1132     if(mono_source.hot.x >= 0)
1133       {
1134 	fprintf(stream, "#define %s_x_hot %d\n", ptr, mono_source.hot.x);
1135 	fprintf(stream, "#define %s_y_hot %d\n", ptr, mono_source.hot.y);
1136       }
1137     if(!no_mono)
1138       fprintf(stream, "#define %s_depth %d\n", ptr, (int)planes);
1139     fprintf(stream, "static unsigned char %s_bits[] =\n{\n ", ptr);
1140     count = 10;
1141     for(y = 0; y < output_bitmap.size.y; y++)
1142       {
1143 	for(x = 0; x < (output_bitmap.size.x + 7) / 8; x++)
1144 	  {
1145 	    if(!count--)
1146 	      {
1147 		count = 9;
1148 		fputs("\n ", stream);
1149 	      }
1150 	    fprintf(stream, " 0x%02x,", ((size.x - x * 8) > 7 ?
1151 		0xFF : 0xFF >> (8 - (size.x - x * 8))) &
1152 		output_bitmap.bits[y * output_bitmap.scan + x]);
1153 	  }
1154       }
1155     fprintf(stream, "\n};\n");
1156     if(no_mono)
1157       {
1158 	fputs("  ", stdout);
1159 	for(ix = 0; ix != colors; ix++)
1160 	  fprintf(stdout, "%s ", noswap_colors[ix]);
1161 	fputs("+ \\\n", stdout);
1162 	fputs("  ", stdout);
1163 	for(ix = 0; ix != colors; ix++)
1164 	  fprintf(stdout, "%s ", swap_colors[ix]);
1165 	fputs("+\n", stdout);
1166       }
1167     else
1168       {
1169 	fprintf(stream, "static unsigned long %s_noswap[] =\n{\n", ptr);
1170 	for(ix = 0; ix != colors; ix++)
1171 	  fprintf(stream, "  %s,\n", noswap_colors[ix]);
1172 	while(ix++ < 1 << planes)
1173 	  fprintf(stream, "  ~(unsigned long)0,\n");
1174 	fprintf(stream, "};\n");
1175 	fprintf(stream, "static unsigned long %s_swap[] =\n{\n", ptr);
1176 	for(ix = 0; ix != colors; ix++)
1177 	  fprintf(stream, "  %s,\n", swap_colors[ix]);
1178 	while(ix++ < 1 << planes)
1179 	  fprintf(stream, "  ~(unsigned long)0,\n");
1180 	fprintf(stream, "};\n");
1181       }
1182     if(ferror(stream))
1183       {
1184 	fprintf(stderr, "Error writing file");
1185 	error = 1;
1186       }
1187     fclose(stream);
1188     if(error)
1189       unlink(name);
1190     free(name);
1191   }
1192   /*}}}*/
1193   return error;
1194 }
1195 /*}}}*/
1196