1 /*
2 * A Z-Machine
3 * Copyright (C) 2000 Andrew Hunter
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 /*
21 * Generic image routines, using libpng
22 */
23
24 #include "../config.h"
25
26 #ifdef HAVE_LIBPNG
27
28 #define QUALITY_HIGH /* Undefine to use a 3x3 filter */
29 #undef QUALITY_REALLYLOW /* Define (and undefine the above) to use no filter at all */
30
31 #if defined(QUALITY_HIGH) && defined(QUALITY_REALLYLOW)
32 # error You cannot define both QUALITY_HIGH and QUALITY_REALLYLOW
33 #endif
34
35 #ifdef QUALITY_HIGH
36 # define MATRIX_SIZE 5
37 #elif defined(QUALITY_REALLYLOW)
38 # define MATRIX_SIZE 1
39 #else
40 # define MATRIX_SIZE 3
41 #endif
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include <png.h>
48
49 #include "image.h"
50
51 struct image_data
52 {
53 ZFile* file;
54 int offset;
55
56 png_uint_32 width, height;
57 int depth, colour;
58
59 png_bytep image;
60 png_bytep* row;
61
62 png_colorp pal;
63 int pal_size;
64
65 /* Take our palette from this image... */
66 image_data* pal_image;
67
68 void (*data_destruct)(image_data*, void*);
69 void* data;
70 };
71
72 struct file_data
73 {
74 ZFile* file;
75 int pos;
76 };
77
image_read(png_structp png_ptr,png_bytep data,png_size_t len)78 static void image_read(png_structp png_ptr,
79 png_bytep data,
80 png_size_t len)
81 {
82 struct file_data* fl;
83 void* stuff;
84
85 fl = png_get_io_ptr(png_ptr);
86
87 stuff = read_block(fl->file, fl->pos, fl->pos+len);
88 fl->pos += len;
89 memcpy(data, stuff, len);
90 free(stuff);
91 }
92
iload(image_data * resin,image_data * palimg,ZFile * file,int offset,int realread)93 static image_data* iload(image_data* resin,
94 image_data* palimg,
95 ZFile* file,
96 int offset,
97 int realread)
98 {
99 struct file_data fl;
100
101 image_data* res;
102 png_structp png;
103 png_infop png_info;
104 png_infop end_info;
105
106 int x;
107
108 res = resin;
109
110 png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
111 (png_voidp)NULL,
112 NULL,
113 NULL);
114 if (!png)
115 return NULL;
116
117 png_info = png_create_info_struct(png);
118 if (!png_info)
119 {
120 png_destroy_read_struct(&png, NULL, NULL);
121 return NULL;
122 }
123
124 end_info = png_create_info_struct(png);
125 if (!end_info)
126 {
127 png_destroy_read_struct(&png, &png_info, NULL);
128 return NULL;
129 }
130
131 fl.file = file;
132 fl.pos = offset;
133 png_set_read_fn(png, &fl, image_read);
134
135 if (res == NULL)
136 {
137 res = malloc(sizeof(image_data));
138
139 res->file = file;
140 res->offset = offset;
141 res->row = NULL;
142 res->image = NULL;
143
144 res->pal = NULL;
145 res->pal_size = -1;
146 res->pal_image = palimg;
147
148 res->data = NULL;
149 res->data_destruct = NULL;
150 }
151
152 png_read_info(png, png_info);
153 png_get_IHDR(png, png_info,
154 &res->width, &res->height,
155 &res->depth, &res->colour,
156 NULL, NULL, NULL);
157
158 /* Get the palette if the image has one */
159 if (res->colour == PNG_COLOR_TYPE_PALETTE)
160 {
161 png_colorp pal;
162 int z;
163
164 png_get_PLTE(png, png_info, &pal, &res->pal_size);
165 res->pal = malloc(sizeof(png_color)*res->pal_size);
166
167 for (z=0; z<res->pal_size; z++)
168 {
169 res->pal[z] = pal[z];
170 }
171 }
172
173 /* We want 8-bit RGB data only */
174 if (res->colour == PNG_COLOR_TYPE_GRAY ||
175 res->colour == PNG_COLOR_TYPE_GRAY_ALPHA)
176 png_set_gray_to_rgb(png);
177 if (res->colour != PNG_COLOR_TYPE_PALETTE ||
178 palimg == NULL)
179 {
180 if (res->depth <= 8)
181 png_set_expand(png);
182 if (png_get_valid(png, png_info, PNG_INFO_tRNS))
183 png_set_expand(png);
184 if (res->depth == 16)
185 png_set_strip_16(png);
186 }
187
188 /* Update our information accordingly */
189 png_read_update_info(png, png_info);
190 png_get_IHDR(png, png_info,
191 &res->width, &res->height,
192 &res->depth, &res->colour,
193 NULL, NULL, NULL);
194
195 if (realread)
196 {
197 res->row = malloc(sizeof(png_bytep)*res->height);
198 res->image = malloc(sizeof(png_byte)*png_get_rowbytes(png, png_info)*res->height);
199
200 for (x=0; x<res->height; x++)
201 {
202 res->row[x] = res->image + (x*png_get_rowbytes(png, png_info));
203 }
204
205 png_read_image(png, res->row);
206
207 /* Convert from a paletted image */
208 if (res->colour == PNG_COLOR_TYPE_PALETTE)
209 {
210 unsigned char* realimg;
211 unsigned char* out;
212 int x, y, mask;
213 unsigned char* p;
214 int bit, shift;
215
216 png_bytep trans;
217 int ntrans;
218
219 png_colorp plte;
220
221 int is_trans[256];
222
223 out = realimg = malloc(res->width*res->height*4);
224 plte = res->pal;
225 if (palimg != NULL && palimg->pal != NULL)
226 plte = palimg->pal;
227
228 ntrans = 0;
229 png_get_tRNS(png, png_info, &trans, &ntrans, NULL);
230
231 for (x=0; x<(1<<res->depth); x++)
232 is_trans[x] = 0;
233 for (x=0; x<ntrans; x++)
234 is_trans[trans[x]] = 1;
235
236 shift = 8 - res->depth;
237 mask = (1<<res->depth)-1;
238 mask <<= shift;
239
240 for (y=0; y<res->height; y++)
241 {
242 p = res->row[y];
243 bit = 8;
244 for (x=0; x<res->width; x++)
245 {
246 int pix;
247
248 pix = (*p)&mask;
249 pix >>= shift;
250 (*p) <<= res->depth;
251
252 *(out++) = plte[pix].red;
253 *(out++) = plte[pix].green;
254 *(out++) = plte[pix].blue;
255
256 if (is_trans[pix])
257 *(out++) = 0;
258 else
259 *(out++) = 255;
260
261 bit -= res->depth;
262 if (bit == 0)
263 {
264 p++;
265 bit = 8;
266 }
267 }
268 }
269
270 free(res->image);
271 res->image = realimg;
272
273 for (x=0; x<res->height; x++)
274 {
275 res->row[x] = res->image + (x*res->width*4);
276 }
277 }
278 else if ((res->colour&PNG_COLOR_MASK_ALPHA) == 0)
279 {
280 int old, new;
281
282 /* Add an alpha channel */
283 /* (png_set_filler seems to cause segfaults) */
284 res->image = realloc(res->image,
285 res->width*res->height*4);
286
287 new = res->width*res->height*4;
288 for (old = (res->width*res->height-1)*3; old >= 0; old-=3)
289 {
290 new-=4;
291
292 res->image[new+3] = 255;
293 res->image[new+2] = res->image[old+2];
294 res->image[new+1] = res->image[old+1];
295 res->image[new] = res->image[old];
296 }
297
298 for (x=0; x<res->height; x++)
299 {
300 res->row[x] = res->image + (x*res->width*4);
301 }
302 }
303
304 #if WINDOW_SYSTEM == 3
305 /* Premultiply */
306 for (x=0; x<(res->width*res->height)*4; x += 4)
307 {
308 res->image[x+0] = ((int)res->image[x+0]*(int)res->image[x+3])>>8;
309 res->image[x+1] = ((int)res->image[x+1]*(int)res->image[x+3])>>8;
310 res->image[x+2] = ((int)res->image[x+2]*(int)res->image[x+3])>>8;
311 }
312 #endif
313
314 png_read_end(png, end_info);
315 }
316 else
317 {
318 res->row = NULL;
319 res->image = NULL;
320 }
321
322 png_destroy_read_struct(&png, &png_info, &end_info);
323
324 return res;
325 }
326
image_load(ZFile * file,int offset,int length,image_data * palimg)327 image_data* image_load(ZFile* file, int offset, int length, image_data* palimg)
328 {
329 return iload(NULL, palimg, file, offset, 0);
330 }
331
image_unload(image_data * data)332 void image_unload(image_data* data)
333 {
334 if (data == NULL)
335 return;
336
337 if (data->data != NULL)
338 {
339 (data->data_destruct)(data, data->data);
340 }
341
342 if (data->pal != NULL)
343 free(data->pal);
344
345 if (data->image != NULL)
346 free(data->image);
347 if (data->row != NULL)
348 free(data->row);
349
350 free(data);
351 }
352
image_unload_rgb(image_data * data)353 void image_unload_rgb(image_data* data)
354 {
355 if (data == NULL)
356 return;
357
358 if (data->image != NULL)
359 free(data->image);
360 if (data->row != NULL)
361 free(data->row);
362
363 data->image = NULL;
364 data->row = NULL;
365 }
366
image_width(image_data * data)367 int image_width(image_data* data)
368 {
369 return data->width;
370 }
371
image_height(image_data * data)372 int image_height(image_data* data)
373 {
374 return data->height;
375 }
376
image_rgb(image_data * data)377 unsigned char* image_rgb(image_data* data)
378 {
379 if (data->image == NULL)
380 {
381 if (iload(data, data->pal_image, data->file, data->offset, 1) == NULL)
382 {
383 return NULL;
384 }
385 }
386
387 return data->image;
388 }
389
image_resample(image_data * data,int n,int d)390 void image_resample(image_data* data, int n, int d)
391 {
392 unsigned char* newimage, *ip;
393 int ny;
394
395 int newwidth, newheight;
396
397 #ifdef QUALITY_HIGH
398 int filter[5][5] =
399 { { 1, 2, 3, 2, 1 },
400 { 2, 3, 4, 3, 2 },
401 { 3, 4, 5, 4, 3 },
402 { 2, 3, 4, 3, 2 },
403 { 1, 2, 3, 2, 1 } };
404 #else
405 int filter[3][3] =
406 { { 1, 2, 1 },
407 { 2, 4, 2 },
408 { 1, 2, 1 } };
409 #endif
410
411 if (data->image == NULL)
412 {
413 if (iload(data, data->pal_image, data->file, data->offset, 1) == NULL)
414 {
415 return;
416 }
417 }
418
419 /*
420 * Not a very complicated resampling w/filter routine. We use bresenham
421 * to generate pixels and a 3x3 filter. The results are usually OK.
422 *
423 * The filtering could *definately* be better, and the resampling
424 * sometimes produces the odd pixel error. Ho-hum. The aliasing
425 * is usually unnoticable - dithering is the place where you'll
426 * see it the most, and it's damn hard to resample dithered areas
427 * properly anyway. At least, it is in reasonable time.
428 */
429
430 newwidth = (data->width*n)/d;
431 newheight = (data->height*n)/d;
432
433 ip = newimage = malloc(newwidth*newheight*4);
434
435 n *= MATRIX_SIZE; /* 3x3 filter, y'see */
436
437 if (n >= d) /* Far more likely to happen... */
438 {
439 int dfx, dfy, Ex, NEx, Ey, NEy;
440 unsigned char* xp[MATRIX_SIZE];
441 int yp, dstx, dsty;
442
443 int i;
444
445 if (newwidth < newheight)
446 { n = newwidth*MATRIX_SIZE; d = data->width-1; }
447 else
448 { n = newheight*MATRIX_SIZE; d = data->height-1; }
449
450 /* Set up for bresenham */
451 dfx = 2*(data->width-1)-newwidth*MATRIX_SIZE;
452 dfy = 2*(data->height-1)-newheight*MATRIX_SIZE;
453 Ex = 2*(data->width-1);
454 Ey = 2*(data->height-1);
455 NEx = 2*((data->width-1)-newwidth*MATRIX_SIZE);
456 NEy = 2*((data->height-1)-newheight*MATRIX_SIZE);
457
458 /* Calculate our 3 initial y positions */
459 yp = 0;
460 for (i=0; i<MATRIX_SIZE; i++)
461 {
462 xp[i] = data->row[yp];
463
464 /* Next position */
465 if (dfy <= 0)
466 {
467 dfy += Ey;
468 }
469 else
470 {
471 dfy += NEy;
472 yp++;
473 }
474 }
475
476 ip = newimage; /* Current position */
477
478 for (dsty = 0; dsty<newheight; dsty++)
479 {
480 for (dstx = 0; dstx<newwidth; dstx++)
481 {
482 int rs, gs, bs, as;
483
484 #ifndef QUALITY_REALLYLOW
485 /* Do the sampling */
486 rs = gs = bs = as = 0;
487
488 for (i=0; i<MATRIX_SIZE; i++)
489 {
490 int j;
491
492 for (j=0; j<MATRIX_SIZE; j++)
493 {
494 rs += xp[j][0]*filter[i][j];
495 gs += xp[j][1]*filter[i][j];
496 bs += xp[j][2]*filter[i][j];
497 as += xp[j][3]*filter[i][j];
498 }
499
500 /* Next X */
501 if (dfx <= 0)
502 {
503 dfx += Ex;
504 }
505 else
506 {
507 for (j=0; j<MATRIX_SIZE; j++)
508 xp[j] += 4;
509 dfx += NEx;
510 }
511 }
512
513 /* Scale the sample */
514 # ifdef QUALITY_HIGH
515 rs /= 65; gs /= 65; bs /= 65; as /= 65;
516 # else
517 rs >>= 4; gs >>= 4; bs >>= 4; as >>= 4;
518 # endif
519 #else
520 rs = xp[0][0];
521 gs = xp[0][1];
522 bs = xp[0][2];
523 as = xp[0][3];
524
525 if (dfx <= 0)
526 dfx += Ex;
527 else
528 {
529 xp[0] += 4;
530 dfx += NEx;
531 }
532 #endif
533
534 /* store the sample */
535 (*ip++) = rs;
536 (*ip++) = gs;
537 (*ip++) = bs;
538 (*ip++) = as;
539 }
540
541 /* Next 3 y positions */
542 for (i=0; i<MATRIX_SIZE; i++)
543 {
544 xp[i] = data->row[yp];
545
546 /* Next position */
547 if (dfy <= 0)
548 {
549 dfy += Ey;
550 }
551 else
552 {
553 dfy += NEy;
554 yp++;
555 }
556 }
557
558 dfx = 2*(data->width)-newwidth*MATRIX_SIZE;
559 }
560 }
561 else
562 {
563 /*
564 * Less likely: the new image is less than 1/3 of the size of
565 * the original.
566 */
567 int dfx, dfy, E, NE;
568 unsigned char* xp[MATRIX_SIZE];
569 int yp, dstx, dsty;
570 int subyp;
571
572 int i;
573
574 /*
575 * What's going on here?
576 *
577 * In a word - downscaling. This code won't be executed much...
578 *
579 * Well, bresenham's algorithm only works for slopes <= 1. So, we
580 * consider the mapping this way: Bresenham tells us when we should
581 * move on a pixel in the *smaller* of the two images. Now, matters
582 * are complicated somewhat here, because the 'larger' image we're
583 * considering is in fact 3 times larger than the 'smaller' one
584 * (for the filter).
585 *
586 * So, to find X, the number of pixels to move in the larger image
587 * for one in the smaller, we run bresenham's algorithm until it
588 * moves up a pixel - each iteration indicates to move on a pixel
589 * in the larger image (as above). This event indicates we should
590 * move on a pixel in the smaller image (note: as well as the
591 * larger). A spot of extra code to deal with the whole *3 problem,
592 * and bob's yer uncle.
593 *
594 * Note that, as we aren't considering every pixel in the source image
595 * anymore, we could introduce aliasing. But, the filtering is far
596 * from perfect, so you get some aliasing anyway.
597 */
598
599 /* Minor adjustment (ensures we don't overrun) */
600 if (newwidth < newheight)
601 { n = newwidth*MATRIX_SIZE; d = data->width+1; }
602 else
603 { n = newheight*MATRIX_SIZE; d = data->height+1; }
604
605 /* Set up for bresenham */
606 dfx = dfy = 2*n-d;
607 E = 2*n;
608 NE = 2*(n-d);
609
610 /* Set up initial 3 y positions*/
611 yp = 0;
612 subyp = 0;
613 for (i=0; i<MATRIX_SIZE; i++)
614 {
615 xp[i] = data->row[yp];
616
617 /* Next position */
618 while (dfy > 0)
619 {
620 dfy += NE;
621 subyp++;
622 if (subyp >= MATRIX_SIZE)
623 { subyp = 0; yp++; }
624 }
625 subyp++; if (subyp >= MATRIX_SIZE) { subyp = 0; yp++; }
626
627 dfy += E;
628 }
629
630 ip = newimage; /* Current position */
631
632 for (dsty = 0; dsty<newheight; dsty++)
633 {
634 int subx;
635
636 subx = 0;
637
638 for (dstx = 0; dstx<newwidth; dstx++)
639 {
640 int rs, gs, bs, as;
641
642 #ifndef QUALITY_REALLYLOW
643 /* Do the sampling */
644 rs = gs = bs = as = 0;
645
646 for (i=0; i<MATRIX_SIZE; i++)
647 {
648 int j,k;
649
650 for (j=0; j<MATRIX_SIZE; j++)
651 {
652 rs += xp[j][0]*filter[i][j];
653 gs += xp[j][1]*filter[i][j];
654 bs += xp[j][2]*filter[i][j];
655 as += xp[j][3]*filter[i][j];
656 }
657
658 /* Next X */
659 j = 0;
660 while (dfx > 0)
661 {
662 dfx += NE;
663 subx++;
664 if (subx >= MATRIX_SIZE) { subx = 0; j+=4; }
665 }
666 subx++;
667 if (subx >= MATRIX_SIZE) { subx = 0; j+=4; }
668
669 for (k=0; k<MATRIX_SIZE; k++)
670 xp[k] += j;
671 dfx += E;
672 }
673
674 /* Scale the sample */
675 # ifdef QUALITY_HIGH
676 rs /= 65; gs /= 65; bs /= 65; as /= 65;
677 # else
678 rs >>= 4; gs >>= 4; bs >>= 4; as >>= 4;
679 # endif
680 #else
681 rs = xp[0][0];
682 gs = xp[0][1];
683 bs = xp[0][2];
684 as = xp[0][3];
685
686 i = 0;
687 while (dfx > 0)
688 {
689 dfx += NE;
690 subx++;
691 if (subx >= MATRIX_SIZE) { subx = 0; i+=4; }
692 }
693 subx++;
694 if (subx >= MATRIX_SIZE) { subx = 0; i+=4; }
695
696 xp[0] += i;
697 #endif
698
699 /* store the sample */
700 (*ip++) = rs;
701 (*ip++) = gs;
702 (*ip++) = bs;
703 (*ip++) = as;
704 }
705
706 /* Next 3 y positions */
707 for (i=0; i<MATRIX_SIZE; i++)
708 {
709 xp[i] = data->row[yp];
710
711 /* Next position */
712 while (dfy > 0)
713 {
714 dfy += NE;
715 subyp++;
716 if (subyp >= MATRIX_SIZE)
717 { subyp = 0; yp++; }
718 }
719 subyp++; if (subyp >= MATRIX_SIZE) { subyp=0; yp++; }
720
721 dfy += E;
722 }
723
724 dfx = 2*n-d;
725 }
726 }
727
728 /* Reset the data structures */
729 free(data->image);
730
731 data->image = newimage;
732 data->width = newwidth;
733 data->height = newheight;
734
735 data->row = realloc(data->row, sizeof(png_bytep)*newheight);
736
737 for (ny=0; ny<newheight; ny++)
738 {
739 data->row[ny] = newimage + 4*ny*newwidth;
740 }
741 }
742
image_set_data(image_data * img,void * data,void (* destruct)(image_data *,void *))743 void image_set_data(image_data* img, void* data,
744 void (*destruct)(image_data*, void*))
745 {
746 img->data = data;
747 img->data_destruct = destruct;
748 }
749
image_get_data(image_data * img)750 void* image_get_data(image_data* img)
751 {
752 return img->data;
753 }
754
image_cmp_palette(image_data * img1,image_data * img2)755 int image_cmp_palette(image_data* img1, image_data* img2)
756 {
757 int x;
758
759 if (img1->pal == NULL && img2->pal == NULL)
760 return 1;
761 if (img1->pal == NULL)
762 return 0;
763 if (img2->pal == NULL)
764 return 0;
765
766 if (img1->pal_size != img2->pal_size)
767 return 0;
768
769 for (x=0; x<img1->pal_size; x++)
770 {
771 if (img1->pal[x].red != img2->pal[x].red ||
772 img1->pal[x].green != img2->pal[x].green ||
773 img1->pal[x].blue != img2->pal[x].blue)
774 return 0;
775 }
776
777 return 1;
778 }
779
780 #endif
781