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