The R Project SVN R

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4394 ripley 1
/*
2
 * GraphApp - Cross-Platform Graphics Programming Library.
3
 *
4
 * File: image.c -- cross-platform image type.
5
 * Platform: Neutral  Version: 2.40  Date: 1998/05/05
6
 *
7
 * Version: 2.30  Changes: Original version by Lachlan Patrick.
8
 * Version: 2.40  Changes: Faster drawing, image scaling and cropping.
9
 */
10
 
11
/* Copyright (C) 1993-1998 Lachlan Patrick
12
 
13
   This file is part of GraphApp, a cross-platform C graphics library.
14
 
15
   GraphApp is free software; you can redistribute it and/or modify it
16
   under the terms of the GNU Library General Public License.
17
   GraphApp is distributed in the hope that it will be useful, but
18
   WITHOUT ANY WARRANTY.
19
 
20
   See the file COPYLIB.TXT for details.
21
*/
22
 
23
/*
24
 *  Image
25
 *  -----
26
 *  An image is a platform-indepedent representation of a
27
 *  rectangular picture, in RGB colour format. There are two
28
 *  possible formats the picture can use: 8-bit indexed colour,
29
 *  and 32-bit true colour (RGB plus alpha channel).
30
 *
31
 *  An image in 8-bit format has the following properties:
32
 *    - depth is set to 8
33
 *    - pixels is an array of (width*height) bytes
34
 *    - cmapsize is set to a non-zero value
35
 *    - cmap is an array of cmapsize rgb values
36
 *
37
 *  An image in 32-bit format has the following properties:
38
 *    - depth is set to 32
39
 *    - pixels is an array of (width * height) rgb values
40
 *    - cmapsize is zero and cmap is NULL (an empty array)
41
 *
42
 *  Any other depth is deliberately not supported.
43
 *  Transparency is handled in the alpha channel of each rgb value
44
 *  (either inside the cmap, or in the pixels array itself).
45
 */
46
 
47
#include "internal.h"
48
 
49
/*
50
 *  Create a new image:
51
 */
52
image newimage(int width, int height, int depth)
53
{
54
	image img;
55
 
56
	if ((depth != 8) && (depth != 32))
57
		return NULL;
58
 
59
	img = create(struct imagedata);
60
 
61
	if (! img)
62
		return img;
63
 
64
	img->width  = width;
65
	img->height = height;
66
 
67
	if (depth == 8) {
68
		img->depth  = 8;
69
		img->pixels = array(width*height, byte);
70
	}
71
	else {
72
		img->depth  = 32;
73
		img->pixels = (byte *) array(width*height, rgb);
74
	}
75
 
76
	return img;
77
}
78
 
79
/*
80
 *  Make a new copy of an image:
81
 */
82
image copyimage(image img)
83
{
84
	image new_img;
85
 
86
	if (! img)
87
		return img;
88
 
89
	new_img = newimage(img->width, img->height, img->depth);
90
	setpixels(new_img, img->pixels);
91
	setpalette(new_img, img->cmapsize, img->cmap);
92
 
93
	return new_img;
94
}
95
 
96
/*
97
 *  Delete an image:
98
 */
99
void delimage(image img)
100
{
101
	if (img) {
102
		discard(img->cmap);
103
		discard(img->pixels);
104
		discard(img);
105
	}
106
}
107
 
108
/*
109
 *  Print an image:
110
 */
111
#if DEBUG
112
PROTECTED
113
void printimage(FILE *file, image img)
114
{
115
	int i;
116
	long w, h, n;
117
 
118
	fprintf(file, "Pixmap:\n");
119
	fprintf(file, " depth     = %d\n", img->depth);
120
	fprintf(file, " width     = %d\n", img->width);
121
	fprintf(file, " height    = %d\n", img->height);
122
	fprintf(file, " cmapsize = %d\n", img->cmapsize);
123
	fprintf(file, " cmap:\n");
124
	for (i=0; i < img->cmapsize; i++)
125
		fprintf(file, "  %-02.2X = %-08.8lX\n", i, img->cmap[i]);
126
	fprintf(file, " pixels:\n");
127
	for (h=0; h < img->height; h++) {
128
		fprintf(file, "  [");
129
		for (w=0; w < (img->width * img->depth / 8); w++) {
130
			n = h * (img->width * img->depth / 8) + w;
131
			fprintf(file, "%-02.2X", img->pixels[n]);
132
		}
133
		fprintf(file, "]\n");
134
	}
135
}
136
#endif
137
 
138
/*
139
 *  Discover the dimensions of the image:
140
 */
141
 
142
int imagedepth(image img)  { return ((img)?(img->depth):0);}
143
int imagewidth(image img)  { return ((img)?(img->width):0);}
144
int imageheight(image img) { return ((img)?(img->height):0);}
145
 
146
/*
147
 *  Change an image's pixels:
148
 */
149
void setpixels(image img, byte pixels[])
150
{
151
	long i, length;
152
 
153
	if (! img)
154
		return;
155
 
156
	length = img->width * img->height;
157
	if (img->depth > 8)
158
		length = length * sizeof(rgb);
159
 
160
	for (i=0; i < length; i++)
161
		img->pixels[i] = pixels[i];
162
}
163
 
164
/*
165
 *  Return an image's pixel array:
166
 */
167
byte * getpixels(image img)
168
{
169
	if (img)
170
		return img->pixels;
171
	else
172
		return NULL;
173
}
174
 
175
/*
176
 *  Change an image's cmap:
177
 */
178
void setpalette(image img, int cmapsize, rgb *cmap)
179
{
180
	int i;
181
 
182
	if (! img)
183
		return;
184
 
185
	discard(img->cmap);
186
 
187
	img->cmapsize = cmapsize;
188
	img->cmap = array(cmapsize, rgb);
189
 
190
	for (i=0; i < cmapsize; i++)
191
		img->cmap[i] = cmap[i];
192
}
193
 
194
/*
195
 *  Return information about an image's cmap:
196
 */
197
rgb * getpalette(image img)
198
{
199
	if (img)
200
		return img->cmap;
201
	else
202
		return NULL;
203
}
204
 
205
int getpalettesize(image img)
206
{
207
	if (img)
208
		return img->cmapsize;
209
	else
210
		return 0;
211
}
212
 
213
/*
214
 *  Try to generate an 8-bit version of an image:
215
 *  If there are less than 256 unique colours in a 32-bit
216
 *  image, this routine will return the corresponding
217
 *  indexed 8-bit image.
218
 *  Returns NULL if more than 256 colours are found.
219
 */
220
static image fast_find_cmap (image img)
221
{
222
	image new_img;
223
	long   i, j, length;
224
	rgb *  pixel32;
225
	byte * pixel8;
226
	int    cmapsize, low, high, mid;
227
	rgb    col;
228
	rgb    cmap[256];
229
 
230
	pixel32 = (rgb *) img->pixels;
231
 
232
	/* the first colour goes into the cmap automatically: */
233
	length = img->width * img->height;
234
	cmapsize = 0;  mid = 0;
235
 
236
	for (i=0; i < length; i++)
237
	{
238
		col = *pixel32 ++;
239
		/* only allow one transparent colour in the cmap: */
240
		if (col & 0xF0000000UL)
241
			col  = 0xFFFFFFFFUL;	/* transparent */
242
		else
243
			col &= 0x00FFFFFFUL;	/* opaque */
244
 
245
		/* binary search the cmap: */
246
		low = 0;  high = cmapsize - 1;
247
		while (low <= high) {
248
			mid = (low+high)/2;
249
			if      (col < cmap[mid]) high = mid - 1;
250
			else if (col > cmap[mid]) low  = mid + 1;
251
			else break;
252
		}
253
 
254
		if (high < low) {
255
			/* didn't find colour in cmap, insert it: */
256
			if (cmapsize >= 256)
257
				return NULL;
258
			for (j=cmapsize; j > low; j--)
259
				cmap[j] = cmap[j-1];
260
			cmap[low] = col;
261
			cmapsize ++;
262
		}
263
	}
264
 
265
	/* now create the 8-bit indexed image: */
266
 
267
	new_img = newimage(img->width, img->height, 8);
268
	if (! new_img)
269
		return new_img;
270
	setpalette(new_img, cmapsize, cmap);
271
 
272
	/* now convert each 32-bit pixel into an 8-bit pixel: */
273
 
274
	pixel32 = (rgb *) img->pixels;
275
	pixel8 = (byte *) new_img->pixels;
276
 
277
	for (i=0; i < length; i++)
278
	{
279
		col = *pixel32 ++;
280
 
281
		/* only allow one transparent colour in the cmap: */
282
		if (col & 0xF0000000UL)
283
			col  = 0xFFFFFFFFUL;	/* transparent */
284
		else
285
			col &= 0x00FFFFFFUL;	/* opaque */
286
 
287
		/* binary search the cmap (the colour must be there): */
288
		low = 0;  high = cmapsize - 1;
289
		while (low <= high) {
290
			mid = (low+high)/2;
291
			if      (col < cmap[mid]) high = mid - 1;
292
			else if (col > cmap[mid]) low  = mid + 1;
293
			else break;
294
		}
295
 
296
		if (high < low) {
297
			/* impossible situation */
298
			delimage(new_img);
299
			return NULL;
300
		}
301
 
302
		*pixel8 = mid;  pixel8 ++;
303
	}
304
 
305
	return new_img;
306
}
307
 
308
/*
309
 *  Try to generate an 8-bit version of an image:
310
 *  This routine will approximate a 32-bit image using a
311
 *  7x7x5 colour cube.
312
 *  If it runs out of memory, it returns NULL.
313
 */
314
static image fast_generate_cmap (image img)
315
{
316
	image new_img;
317
	long   i, length, col;
318
	int    r, g, b, value;
319
	rgb *  pixel32;
320
	byte * pixel8;
321
	rgb    cmap[256];
322
 
323
	/* Generate the colour map: */
324
 
325
	for (r=0; r<7; r++)		/* 7x7x5 colour cube */
326
	  for (g=0; g<7; g++)
327
	    for (b=0; b<5; b++)
328
		cmap [r*35 + g*5 + b] = rgb(r,g,b);
329
 
330
	for (i=0; i<9; i++)		/* greyscale ramp */
331
	{
332
		value = 255 * i / 8;
333
		cmap [254 - 8 + i] = rgb(value, value, value);
334
	}
335
 
336
	cmap [255] = 0xFFFFFFFFUL;	/* transparent */
337
 
338
	/* Generate the 8-bit indexed image: */
339
 
340
	new_img = newimage(img->width, img->height, 8);
341
	if (! new_img)
342
		return new_img;
343
	setpalette(new_img, 256, cmap);
344
 
345
	/* Translate the pixels from 32-bit to 8-bit: */
346
 
347
	length = img->width * img->height;
348
	pixel32 = (rgb *) img->pixels;
349
	pixel8 = (byte *) new_img->pixels;
350
 
351
	for (i=0; i < length; i++) {
352
		col = *pixel32 ++;
353
		r = getred(col);
354
		g = getgreen(col);
355
		b = getblue(col);
356
 
357
		if (getalpha(col) > 0x7F)  /* transparent */
358
			value = 255;
359
 
360
		else if ((r == g) && (r == b))	/* grey */
361
		{
362
			r = (r + 16) / 32;
363
			if (r == 0)
364
				value = 0;	/* black */
365
			else
366
				value = 254 - 8 + r;
367
		}
368
 
369
		else	/* map to 7x7x5 colour cube */
370
		{
371
			r = (r + 21) / 42;
372
			g = (g + 21) / 42;
373
			b = (b + 32) / 64;
374
 
375
			value = r*35 + g*5 + b;
376
		}
377
 
378
		*pixel8 = value;  pixel8 ++;
379
	}
380
 
381
	return new_img;
382
}
383
 
384
/*
385
 *  Try to generate an 8-bit version of a 32-bit image:
386
 *  Return NULL on failure.
387
 */
388
image convert32to8 (image img)
389
{
390
	image new_img;
391
 
392
	if (! img)
393
		return img;
394
 
395
	if (img->depth <= 8)
396
		return copyimage(img);
397
 
398
	new_img = fast_find_cmap(img);
399
	if (! new_img)
400
		new_img = fast_generate_cmap(img);
401
 
402
	return new_img;
403
}
404
 
405
/*
406
 *  Try to generate a 32-bit version of an 8-bit image:
407
 *  Return NULL if there is no memory left.
408
 */
409
image convert8to32 (image img)
410
{
411
	image new_img;
412
	long i;
413
	rgb *pixel32;
414
	byte *pixel8;
415
	byte value;
416
 
417
	if (! img)
418
		return img;
419
 
420
	new_img = newimage(img->width, img->height, 32);
421
	if (! new_img)
422
		return new_img;
423
 
424
	pixel32 = (rgb *) new_img->pixels;
425
	pixel8 = (byte *) img->pixels;
426
 
427
	for (i=img->width * img->height; i; i--) {
428
		value = *pixel8 ++;
429
		if (value >= img->cmapsize)
430
			value = img->cmapsize - 1;
431
		*pixel32 ++ = img->cmap[value];
432
	}
433
 
434
	return new_img;
435
}
436
 
437
/*
438
 *  Sort an image's colour map, eliminating redudancies.
439
 *  This operation transforms an existing image.
440
 */
441
 
442
typedef int (*qsort_func)(const void *a, const void *b);
443
 
444
static int compare_freq(long *a, long *b)
445
{
446
	long freq_a, freq_b;
447
	int value_a, value_b;
448
 
449
	freq_a = (*a) >> 8;
450
	freq_b = (*b) >> 8;
451
	value_a = (*a) & 0x00FF;
452
	value_b = (*b) & 0x00FF;
453
 
454
	if (freq_a < freq_b)        return (+1);
455
	else if (freq_a > freq_b)   return (-1);
456
	else                        return (value_a - value_b);
457
}
458
 
459
void sortpalette(image img)
460
{
461
	long	i, j, length;
462
	int 	old_value, new_value;
463
	rgb 	col;
464
	int 	new_size;
465
	long *	histogram;
466
	byte *	translate;
467
	rgb *	new_cmap;
468
 
469
	if (! img)
470
		return;
471
	if (img->depth > 8)
472
		return;
473
 
474
	histogram = array(256, long);
475
	translate = array(256, byte);
476
 
477
	/* Generate a colour histogram: */
478
	length = img->width * img->height;
479
	for (i=0; i < length; i++)
480
		histogram[img->pixels[i]] ++;
481
 
482
	/* Place colour indexes in low byte of histogram: */
483
	for (i=0; i < 256; i++) {
484
		histogram[i] <<= 8;
485
		histogram[i] |= i;
486
	}
487
 
488
	/* Sort the histogram in decreasing frequency order: */
489
	qsort(histogram, 256, sizeof(long), (qsort_func) compare_freq);
490
 
491
	/* Generate a colour translation table: */
492
	new_size = img->cmapsize;
493
 
494
	for (i=255; i >= 0; i--)
495
	{
496
		old_value = histogram[i] & 0x00FFL;
497
		new_value = i;
498
		col = img->cmap[i];
499
 
500
		/* coalesce identical colours in cmap */
501
		for (j=i-1; j >= 0; j--) {
502
			if (img->cmap[j] == col)
503
				new_value = j;
504
		}
505
 
506
		translate[old_value] = new_value;
507
 
508
		/* find smallest useless colour */
509
		if ((histogram[i] >> 8) == 0)
510
			new_size = i;
511
	}
512
 
513
	/* Generate a sorted colour map: */
514
	new_cmap = array(new_size, rgb);
515
 
516
	for (i=0; i < new_size; i++) {
517
		old_value = histogram[i] & 0x00FFL;
518
		new_value = i;
519
		new_cmap[new_value] = img->cmap[old_value];
520
	}
521
 
522
	/* Change the existing colour map: */
523
	img->cmapsize = new_size;
524
	for (i=0; i < new_size; i++)
525
		img->cmap[i] = new_cmap[i];
526
	discard(new_cmap);
527
 
528
	/* Translate the pixels to the new colour map: */
529
	for (i=0 ; i < length; i++)
530
		img->pixels[i] = translate[img->pixels[i]];
531
 
532
	/* Clean up and return new image: */
533
	discard(translate);
534
	discard(histogram);
535
}
536
 
537
/*
538
 *  Load and save images (utility functions):
539
 */
540
static int string_ends_with(char *name, char *ending)
541
{
542
	int i, j, result;
543
 
544
	if (name == NULL)
545
		return (ending == NULL);
546
 
547
	result = 1;
548
	for (i=0; name[i]; )
549
		i = i + 1;
550
	for (j=0; ending[j]; )
551
		j = j + 1;
552
	while (result && (i >= 0) && (j >= 0)) {
553
		if (tolower(name[i]) != tolower(ending[j]))
554
			result = 0;
555
		i = i - 1;
556
		j = j - 1;
557
	}
558
	if ((i == 0) && (j > 0))
559
		result = 0;
560
	return result;
561
}
562
 
563
static unsigned char read_hex_byte(FILE *file)
564
{
565
	int ch;
566
	int i, value;
567
	unsigned char result = 0;
568
 
569
	ch = 0;
570
	while (ch != EOF) {
571
		if ((ch = getc(file)) != '0') continue;
572
		if ((ch = getc(file)) != 'x') continue;
573
 
574
		for (i=0; i<2; i++) {
575
			ch = getc(file);
576
			if (isdigit(ch))
577
				value = ch - '0';
578
			else if (isalpha(ch))
579
				value = tolower(ch) - 'a' + 10;
580
			else
581
				return result;
582
			result = (result << 4) | value;
583
		}
584
		break;
585
	}
586
	return result;
587
}
588
 
589
static unsigned long read_hex_long(FILE *file)
590
{
591
	int ch;
592
	int i, value;
593
	unsigned long result = 0;
594
 
595
	ch = 0;
596
	while (ch != EOF) {
597
		if ((ch = getc(file)) != '0') continue;
598
		if ((ch = getc(file)) != 'x') continue;
599
 
600
		for (i=0; i<8; i++) {
601
			ch = getc(file);
602
			if (isdigit(ch))
603
				value = ch - '0';
604
			else if (isalpha(ch)) {
605
				ch = tolower(ch);
606
				if (ch > 'f')
607
					return result;
608
				value = ch - 'a' + 10;
609
			}
610
			else
611
				return result;
612
			result = (result << 4) | value;
613
		}
614
		break;
615
	}
616
	return result;
617
}
618
 
619
static char * header_comment   = "/* GraphApp image type 1 */\n";
620
static char * depth_comment    = "/* depth  = %d */\n";
621
static char * width_comment    = "/* width  = %d */\n";
622
static char * height_comment   = "/* height = %d */\n";
623
static char * cmapsize_comment = "/* cmapsize = %d */\n";
624
 
625
static image load_header_image_file(FILE *file)
626
{
627
	char  line[100];
628
	long  i, size;
629
	int   width = 0, height = 0, depth = 8;
630
	int   cmapsize = 0;
631
	rgb * cmap = NULL;
632
	rgb  *pixel32;
633
	byte *pixel8;
634
	image img = NULL;
635
 
636
	if (file == NULL)
637
		return NULL;
638
 
639
	if (fgets(line, sizeof(line)-2, file) == NULL)
640
		return NULL;
641
	if (strcmp(line, header_comment))
642
		return NULL;
643
 
644
	for (i=0; i<4; i++) {
645
		if (fgets(line, sizeof(line)-2, file) == NULL)
646
			return NULL;
647
		if (! strncmp(line, depth_comment, 12))
648
			depth = atoi(line+12);
649
		if (! strncmp(line, width_comment, 12))
650
			width = atoi(line+12);
651
		if (! strncmp(line, height_comment, 12))
652
			height = atoi(line+12);
653
		if (! strncmp(line, cmapsize_comment, 14))
654
			cmapsize = atoi(line+14);
655
	}
656
 
657
	img = newimage(width, height, depth);
658
	if (img == NULL)
659
		return NULL;
660
 
661
	if (depth <= 8) {
662
		if (fgets(line, sizeof(line)-2, file) == NULL)
663
			return NULL;
664
		if (strncmp(line, "rgb ", 4) != 0) {
665
			delimage(img);
666
			return NULL;
667
		}
668
		for (i=0; i<cmapsize; i++) {
669
			append(cmap, read_hex_long(file));
670
		}
671
		if (fgets(line, sizeof(line)-2, file) == NULL)
672
			return NULL;
673
 
674
		img->cmapsize = cmapsize;
675
		img->cmap = cmap;
676
	}
677
 
678
	pixel32 = (rgb *) img->pixels;
679
	pixel8  = img->pixels;
680
	size    = (long) width * height;
681
 
682
	if (fgets(line, sizeof(line)-2, file) == NULL)
683
		return NULL;
684
 
685
	if (depth <= 8) {
686
		for (i=0; i<size; i++)
687
			pixel8[i] = read_hex_byte(file);
688
	}
689
	else {
690
		for (i=0; i<size; i++)
691
			pixel32[i] = read_hex_long(file);
692
	}
693
 
694
	return img;
695
}
696
 
697
static void save_header_image_file(FILE *file, char *name, image img)
698
{
699
	long i, size;
700
	int width;
701
	rgb * pixel32;
702
	byte *pixel8;
703
 
704
	if (file == NULL)
705
		return;
706
	if (img == NULL)
707
		return;
708
 
709
	fprintf(file, header_comment);
710
	fprintf(file, depth_comment,  img->depth);
711
	fprintf(file, width_comment,  img->width);
712
	fprintf(file, height_comment, img->height);
713
	fprintf(file, cmapsize_comment, img->cmapsize);
714
 
715
	if (img->depth <= 8) {
716
		fprintf(file, "rgb %s_cmap [] = {\n", name);
717
		for (i=0; i<img->cmapsize; i++)
718
			fprintf(file, "\t0x%-8.8lXUL,\n", img->cmap[i]);
719
		fprintf(file, "};\n");
720
	}
721
 
722
	pixel32 = (rgb *) img->pixels;
723
	pixel8  = img->pixels;
724
	size    = (long) img->width * img->height;
725
	width   = img->width;
726
 
727
	if (img->depth <= 8) {
728
		fprintf(file, "byte %s_pixels [] = {", name);
729
		if (width > 12) width = 12;
730
		for (i=0; i<size; i++) {
731
			if ((i%width) == 0)
732
				fprintf(file, "\n  ");
733
			fprintf(file, "0x%-2.2X, ", pixel8[i]);
734
		}
735
		fprintf(file, "\n};\n");
736
	}
737
	else {
738
		fprintf(file, "rgb %s_pixels [] = {", name);
739
		if (width > 5) width = 5;
740
		for (i=0; i<size; i++) {
741
			if ((i%width) == 0)
742
				fprintf(file, "\n  ");
743
			fprintf(file, "0x%-8.8lXUL, ", pixel32[i]);
744
		}
745
		fprintf(file, "\n};\n");
746
	}
747
 
748
	fprintf(file, "imagedata %s_imagedata = {\n", name);
749
	fprintf(file, "\t%d,\t/* depth */\n",  img->depth);
750
	fprintf(file, "\t%d,\t/* width */\n",  img->width);
751
	fprintf(file, "\t%d,\t/* height */\n", img->height);
752
	fprintf(file, "\t%d,\t/* cmapsize */\n", img->cmapsize);
753
	if (img->depth <= 8) {
754
		fprintf(file, "\t%s_cmap,\n", name);
755
		fprintf(file, "\t%s_pixels\n", name);
756
	}
757
	else {
758
		fprintf(file, "\t(rgb *) 0\n");
759
		fprintf(file, "\t(byte *) %s_pixels\n", name);
760
	}
761
	fprintf(file, "};\n");
762
	fprintf(file, "image %s_image = & %s_imagedata;\n",
763
			name, name);
764
	fprintf(file, "\n");
765
}
766
 
767
static image load_header_image(char *filename)
768
{
769
	FILE *file;
770
	image img;
771
 
772
	file = fopen(filename, "r");
773
	img = load_header_image_file(file);
774
	fclose(file);
775
	return img;
776
}
777
 
778
static char * base_file_name(char *filename)
779
{
780
	char *name = NULL;
781
	int i, start, end;
782
 
783
	end = strlen(filename);
784
	while (filename[end] != '.')
785
		end--;
786
	for (start=end; start > 0; start--) {
787
		if ((filename[start] == '\\')
788
		 || (filename[start] == '/')
789
		 || (filename[start] == ':')) {
790
			start ++;
791
			break;
792
		}
793
	}
794
	for (i=start; i < end; i++)
795
		append(name, tolower(filename[i]));
796
	return name;
797
}
798
 
799
static void save_header_image(image img, char *filename)
800
{
801
	FILE *file;
802
	char *name;
803
 
804
	file = fopen(filename, "w");
805
	name = base_file_name(filename);
806
	save_header_image_file(file, name, img);
807
	discard(name);
808
	fclose(file);
809
}
810
 
811
/*
812
 *  Top-level functions for loading and saving images:
813
 */
814
image loadimage(char *filename)
815
{
816
	if (string_ends_with(filename, ".gif"))
817
		return load_gif(filename);
818
	else if (string_ends_with(filename, ".h"))
819
		return load_header_image(filename);
820
	else if (string_ends_with(filename, ".img"))
821
		return load_header_image(filename);
822
	else
823
		return NULL;
824
}
825
 
826
void saveimage(image img, char *filename)
827
{
828
	if (string_ends_with(filename, ".gif"))
829
		save_gif(img, filename);
830
	else if (string_ends_with(filename, ".h"))
831
		save_header_image(img, filename);
832
	else if (string_ends_with(filename, ".img"))
833
		save_header_image(img, filename);
834
}
835
 
836
/*
837
 *  Changing an rgb's value:
838
 */
839
 
840
rgb darker(rgb pixel)
841
{
842
	int r, g, b;
843
 
844
	if (getalpha(pixel) > 0x7F)
845
		return Transparent;
846
 
847
	r = getred(pixel);
848
	g = getgreen(pixel);
849
	b = getblue(pixel);
850
 
851
	return rgb((r+1)*3/4,(g+1)*3/4,(b+1)*3/4);
852
}
853
 
854
rgb brighter(rgb pixel)
855
{
856
	int r, g, b;
857
 
858
	if (getalpha(pixel) > 0x7F)
859
		return Transparent;
860
 
861
	r = getred(pixel);
862
	g = getgreen(pixel);
863
	b = getblue(pixel);
864
 
865
	r = (r) * 4 / 3; if (r > 255) r = 255;
866
	g = (g) * 4 / 3; if (g > 255) g = 255;
867
	b = (b) * 4 / 3; if (b > 255) b = 255;
868
	return rgb(r,g,b);
869
}
870
 
871
static rgb monochrome(rgb pixel)
872
{
873
	int min, max, g, b;
874
 
875
	if (getalpha(pixel) > 0x7F)
876
		return Transparent;
877
 
878
	max = min = getred(pixel);
879
	g = getgreen(pixel);
880
	if      (g < min) min = g;
881
	else if (g > max) max = g;
882
	b = getblue(pixel);
883
	if      (b < min) min = b;
884
	else if (b > max) max = b;
885
 
886
	if (min > 0xE0) 	pixel = White;
887
	else if (max < 0x10)	pixel = Black;
888
	else if (max < 0x60)	pixel = Black;
889
	else if (max < 0xD0)	pixel = Black;
890
	else			pixel = White;
891
 
892
	return pixel;
893
}
894
 
895
static rgb greyscale(rgb pixel)
896
{
897
	int min, max, g, b;
898
 
899
	if (getalpha(pixel) > 0x7F)
900
		return Transparent;
901
 
902
	max = min = getred(pixel);
903
	g = getgreen(pixel);
904
	if      (g < min) min = g;
905
	else if (g > max) max = g;
906
	b = getblue(pixel);
907
	if      (b < min) min = b;
908
	else if (b > max) max = b;
909
 
910
	if (min > 0xE0) 	pixel = White;
911
	else if (max < 0x10)	pixel = Black;
912
	else if (max < 0x60)	pixel = DarkGrey;
913
	else if (max < 0xD0)	pixel = Grey;
914
	else			pixel = LightGrey;
915
 
916
	return pixel;
917
}
918
 
919
/*
920
 *  Determine pixel values from an image:
921
 */
922
 
923
PROTECTED
924
rgb get_image_pixel(image img, int x, int y)
925
{
926
	int value;
927
	rgb pixel;
928
 
929
	if ((x < 0) || (x >= img->width))	return Transparent;
930
	if ((y < 0) || (y >= img->height))	return Transparent;
931
 
932
	if (img->depth <= 8) {
933
		value = img->pixels[y*img->width + x];
934
		pixel = img->cmap[value];
935
	} else {
936
		pixel = ((rgb *)(img->pixels))[y*img->width + x];
937
	}
938
 
939
	if (getalpha(pixel) > 0x7F) return Transparent;
940
	return (pixel & White);
941
}
942
 
943
PROTECTED
944
rgb get_monochrome_pixel(image img, int x, int y)
945
{
946
	return monochrome(get_image_pixel(img, x, y));
947
}
948
 
949
PROTECTED
950
rgb get_grey_pixel(image img, int x, int y)
951
{
952
	return greyscale(get_image_pixel(img, x, y));
953
}
954
 
955
/*
956
 *  Return an image scaled to a new width and/or height.
957
 *  The source rectangle sr can be used to crop the source image.
958
 *  The returned image should be deleted using del() when
959
 *  it is no longer needed.
960
 */
961
 
962
static void scale_8_bit_image(image dest, image src, rect dr, rect sr)
963
{
964
	int value;
965
	long x, y;
966
	long dx, dy, sx, sy;
967
	long dw, dh, sw, sh;
968
	byte * src_pixels = src->pixels;
969
	byte * dest_pixels = dest->pixels;
970
 
971
	dw = dest->width;
972
	dh = dest->height;
973
	sw = src->width;
974
	sh = src->height;
975
 
976
	for (y=0; y < dr.height; y++)
977
	  for (x=0; x < dr.width; x++) {
978
		sy = sr.y + y * sr.height / dr.height;
979
		sx = sr.x + x * sr.width / dr.width;
980
		if ((sx >= 0) && (sx < sw) && (sy >= 0) && (sy < sh))
981
			value = src_pixels [sy * sw + sx];
982
		else
983
			value = 0;
984
		dy = dr.y + y;
985
		dx = dr.x + x;
986
		if ((dx >= 0) && (dx < dw) && (dy >= 0) && (dy < dh))
987
			dest_pixels [dy * dw + dx] = value;
988
	  }
989
}
990
 
991
static void scale_32_bit_image(image dest, image src, rect dr, rect sr)
992
{
993
	rgb value;
994
	long x, y;
995
	long dx, dy, sx, sy;
996
	long dw, dh, sw, sh;
997
	rgb * src_pixels = (rgb *) src->pixels;
998
	rgb * dest_pixels = (rgb *) dest->pixels;
999
 
1000
	dw = dest->width;
1001
	dh = dest->height;
1002
	sw = src->width;
1003
	sh = src->height;
1004
 
1005
	for (y=0; y < dr.height; y++)
1006
	  for (x=0; x < dr.width; x++) {
1007
		sy = sr.y + y * sr.height / dr.height;
1008
		sx = sr.x + x * sr.width / dr.width;
1009
		if ((sx >= 0) && (sx < sw) && (sy >= 0) && (sy < sh))
1010
			value = src_pixels [sy * sw + sx];
1011
		else
1012
			value = 0;
1013
		dy = dr.y + y;
1014
		dx = dr.x + x;
1015
		if ((dx >= 0) && (dx < dw) && (dy >= 0) && (dy < dh))
1016
			dest_pixels [dy * dw + dx] = value;
1017
	  }
1018
}
1019
 
1020
image scaleimage(image src, rect dr, rect sr)
1021
{
1022
	image dest;
1023
 
1024
	if (! src)
1025
		return NULL;
1026
 
1027
	dest = newimage(dr.width, dr.height, src->depth);
1028
	if (! dest)
1029
		return NULL;
1030
 
1031
	if (src->depth == 8) {
1032
		setpalette(dest, src->cmapsize, src->cmap);
1033
		scale_8_bit_image(dest, src, dr, sr);
1034
	}
1035
	else if (src->depth == 32) {
1036
		scale_32_bit_image(dest, src, dr, sr);
1037
	}
1038
	else {
1039
		del(dest);
1040
		dest = NULL;
1041
	}
1042
 
1043
	return dest;
1044
}
1045
 
1046
/*
1047
 *  Functions for drawing an image:
1048
 */
1049
 
1050
static int get_mono_pixval(image src, image dest, int x, int y)
1051
{
1052
	int i;
1053
	rgb pixel = get_monochrome_pixel(src, x, y);
1054
 
1055
	for (i=0; i < dest->cmapsize; i++)
1056
		if (pixel == dest->cmap[i])
1057
			return i;
1058
	return dest->cmapsize - 1;
1059
}
1060
 
1061
static int get_grey_pixval(image src, image dest, int x, int y)
1062
{
1063
	int i;
1064
	rgb pixel = get_grey_pixel(src, x, y);
1065
 
1066
	for (i=0; i < dest->cmapsize; i++)
1067
		if (pixel == dest->cmap[i])
1068
			return i;
1069
	return dest->cmapsize - 1;
1070
}
1071
 
1072
void drawmonochrome(image src, rect dr, rect sr)
1073
{
1074
	image dest;
1075
	int x, y;
1076
	rgb cmap[3] = {Black, White, Transparent};
1077
 
1078
	if (! src)
1079
		return;
1080
 
1081
	dest = newimage(src->width, src->height, 8);
1082
	setpalette(dest, 3, cmap);
1083
 
1084
	for (y=0; y < dest->height; y++)
1085
	  for (x=0; x < dest->width; x++)
1086
		dest->pixels[y * dest->width + x]
1087
			= get_mono_pixval(src, dest, x, y);
1088
	drawimage(dest, dr, sr);
1089
	del(dest);
1090
}
1091
 
1092
void drawgreyscale(image src, rect dr, rect sr)
1093
{
1094
	image dest;
1095
	int x, y;
1096
	rgb cmap[6] = {Black, DarkGrey, Grey, LightGrey, White, Transparent};
1097
 
1098
	if (! src)
1099
		return;
1100
 
1101
	dest = newimage(src->width, src->height, 8);
1102
	setpalette(dest, 6, cmap);
1103
 
1104
	for (y=0; y < dest->height; y++)
1105
	  for (x=0; x < dest->width; x++)
1106
		dest->pixels[y * dest->width + x]
1107
			= get_grey_pixval(src, dest, x, y);
1108
	drawimage(dest, dr, sr);
1109
	del(dest);
1110
}
1111
 
1112
void drawdarker(image src, rect dr, rect sr)
1113
{
1114
	image dest = NULL;
1115
	int i, x, y;
1116
	rgb *newcmap = NULL, *oldcmap = NULL;
1117
	rgb *pixels;
1118
 
1119
	if (! src)
1120
		return;
1121
 
1122
	if (src->depth == 8) {
1123
		newcmap = array(src->cmapsize, rgb);
1124
		for (i=0; i < src->cmapsize; i++)
1125
			newcmap[i] = darker(src->cmap[i]);
1126
		oldcmap = src->cmap;
1127
		src->cmap = newcmap;
1128
		dest = src;
1129
	}
1130
	else if (src->depth == 32) {
1131
		dest = newimage(src->width, src->height, src->depth);
1132
		if (dest) {
1133
		  pixels = (rgb *) dest->pixels;
1134
		  for (y=0; y < dest->height; y++)
1135
		    for (x=0; x < dest->width; x++)
1136
			pixels[y * dest->width + x]
1137
				= darker(get_image_pixel(src, x, y));
1138
		} else {
1139
			dest = src;
1140
		}
1141
	}
1142
	drawimage(dest, dr, sr);
1143
	if (dest != src)
1144
		del(dest);
1145
	if (src->cmap == newcmap)
1146
		src->cmap = oldcmap;
1147
	discard(newcmap);
1148
}
1149
 
1150
void drawbrighter(image src, rect dr, rect sr)
1151
{
1152
	image dest = NULL;
1153
	int i, x, y;
1154
	rgb *newcmap = NULL, *oldcmap = NULL;
1155
	rgb *pixels;
1156
 
1157
	if (! src)
1158
		return;
1159
 
1160
	if (src->depth == 8) {
1161
		newcmap = array(src->cmapsize, rgb);
1162
		for (i=0; i < src->cmapsize; i++)
1163
			newcmap[i] = brighter(src->cmap[i]);
1164
		oldcmap = src->cmap;
1165
		src->cmap = newcmap;
1166
		dest = src;
1167
	}
1168
	else if (src->depth == 32) {
1169
		dest = newimage(src->width, src->height, src->depth);
1170
		if (dest) {
1171
		  pixels = (rgb *) dest->pixels;
1172
		  for (y=0; y < dest->height; y++)
1173
		    for (x=0; x < dest->width; x++)
1174
			pixels[y * dest->width + x]
1175
				= brighter(get_image_pixel(src,x,y));
1176
		} else {
1177
			dest = src;
1178
		}
1179
	}
1180
	drawimage(dest, dr, sr);
1181
	if (dest != src)
1182
		del(dest);
1183
	if (src->cmap == newcmap)
1184
		src->cmap = oldcmap;
1185
	discard(newcmap);
1186
}