initial commit
[img2tiles] / src / image.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <assert.h>
6 #include <png.h>
7 #include "image.h"
8
9 int alloc_image(struct image *img, int x, int y, int bpp)
10 {
11         memset(img, 0, sizeof *img);
12         img->width = x;
13         img->height = y;
14         img->bpp = bpp;
15         img->scansz = img->pitch = x * bpp / 8;
16
17         if(!(img->pixels = malloc(y * img->scansz))) {
18                 fprintf(stderr, "failed to allocate %dx%d (%dbpp) pixel buffer\n", x, y, bpp);
19                 return -1;
20         }
21
22         /* just a guess, assume the user will fill the details, but set up reasonable
23          * defaults just in case...
24          */
25         if(bpp <= 8) {
26                 img->nchan = 1;
27                 img->cmap_ncolors = 1 << bpp;
28         } else if(bpp <= 24) {
29                 img->nchan = 3;
30         } else {
31                 img->nchan = 4;
32         }
33         return 0;
34 }
35
36 int load_image(struct image *img, const char *fname)
37 {
38         int i;
39         FILE *fp;
40         png_struct *png;
41         png_info *info;
42         int chan_bits, color_type;
43         png_uint_32 xsz, ysz;
44         png_color *palette;
45         unsigned char **scanline;
46         unsigned char *dptr;
47
48         if(!(fp = fopen(fname, "rb"))) {
49                 fprintf(stderr, "failed to open: %s: %s\n", fname, strerror(errno));
50                 return -1;
51         }
52
53         if(!(png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
54                 fclose(fp);
55                 return -1;
56         }
57         if(!(info = png_create_info_struct(png))) {
58                 fclose(fp);
59                 png_destroy_read_struct(&png, 0, 0);
60                 return -1;
61         }
62         if(setjmp(png_jmpbuf(png))) {
63                 fclose(fp);
64                 png_destroy_read_struct(&png, &info, 0);
65                 return -1;
66         }
67
68         png_init_io(png, fp);
69         png_read_png(png, info, 0, 0);
70
71         png_get_IHDR(png, info, &xsz, &ysz, &chan_bits, &color_type, 0, 0, 0);
72         img->width = xsz;
73         img->height = ysz;
74         img->nchan = png_get_channels(png, info);
75         img->bpp = img->nchan * chan_bits;
76         img->scansz = img->pitch = xsz * img->bpp / 8;
77         img->cmap_ncolors = 0;
78
79         if(color_type == PNG_COLOR_TYPE_PALETTE) {
80                 png_get_PLTE(png, info, &palette, &img->cmap_ncolors);
81                 memcpy(img->cmap, palette, img->cmap_ncolors * sizeof *img->cmap);
82         }
83
84         if(!(img->pixels = malloc(ysz * img->scansz))) {
85                 perror("failed to allocate pixel buffer");
86                 fclose(fp);
87                 png_destroy_read_struct(&png, &info, 0);
88                 return -1;
89         }
90         dptr = img->pixels;
91
92         scanline = (unsigned char**)png_get_rows(png, info);
93         for(i=0; i<ysz; i++) {
94                 memcpy(dptr, scanline[i], img->scansz);
95                 dptr += img->pitch;
96         }
97
98         fclose(fp);
99         png_destroy_read_struct(&png, &info, 0);
100         return 0;
101 }
102
103 int save_image(struct image *img, const char *fname)
104 {
105         int i, chan_bits, coltype;
106         FILE *fp;
107         png_struct *png;
108         png_info *info;
109         png_text txt;
110         unsigned char **scanline = 0;
111         unsigned char *pptr;
112
113         if(!(fp = fopen(fname, "wb"))) {
114                 fprintf(stderr, "save_image: failed to open: %s: %s\n", fname, strerror(errno));
115                 return -1;
116         }
117
118         if(!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
119                 fclose(fp);
120                 return -1;
121         }
122         if(!(info = png_create_info_struct(png))) {
123                 png_destroy_write_struct(&png, 0);
124                 fclose(fp);
125                 return -1;
126         }
127
128         txt.compression = PNG_TEXT_COMPRESSION_NONE;
129         txt.key = "Software";
130         txt.text = "img2tiles";
131         txt.text_length = 0;
132
133         if(setjmp(png_jmpbuf(png))) {
134                 png_destroy_write_struct(&png, &info);
135                 free(scanline);
136                 fclose(fp);
137                 return -1;
138         }
139
140         switch(img->nchan) {
141         case 1:
142                 if(img->cmap_ncolors > 0) {
143                         coltype = PNG_COLOR_TYPE_PALETTE;
144                 } else {
145                         coltype = PNG_COLOR_TYPE_GRAY;
146                 }
147                 break;
148         case 2:
149                 coltype = PNG_COLOR_TYPE_GRAY_ALPHA;
150                 break;
151         case 3:
152                 coltype = PNG_COLOR_TYPE_RGB;
153                 break;
154         case 4:
155                 coltype = PNG_COLOR_TYPE_RGB_ALPHA;
156                 break;
157         }
158
159         chan_bits = img->bpp / img->nchan;
160         png_set_IHDR(png, info, img->width, img->height, chan_bits, coltype, PNG_INTERLACE_NONE,
161                         PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
162         png_set_text(png, info, &txt, 1);
163
164         if(img->cmap_ncolors > 0) {
165                 png_set_PLTE(png, info, (png_color*)img->cmap, img->cmap_ncolors);
166         }
167
168         if(!(scanline = malloc(img->height * sizeof *scanline))) {
169                 png_destroy_write_struct(&png, &info);
170                 fclose(fp);
171                 return -1;
172         }
173
174         pptr = img->pixels;
175         for(i=0; i<img->height; i++) {
176                 scanline[i] = pptr;
177                 pptr += img->pitch;
178         }
179         png_set_rows(png, info, scanline);
180
181         png_init_io(png, fp);
182         png_write_png(png, info, 0, 0);
183         png_destroy_write_struct(&png, &info);
184         free(scanline);
185         fclose(fp);
186         return 0;
187 }
188
189
190 int cmp_image(struct image *a, struct image *b)
191 {
192         int i;
193         unsigned char *aptr = a->pixels;
194         unsigned char *bptr = b->pixels;
195
196         if(a->width != b->width || a->height != b->height || a->bpp != b->bpp || a->nchan != b->nchan) {
197                 return -1;
198         }
199
200         for(i=0; i<a->height; i++) {
201                 if(memcmp(aptr, bptr, a->scansz) != 0) {
202                         return -1;
203                 }
204                 aptr += a->pitch;
205                 bptr += b->pitch;
206         }
207         return 0;
208 }
209
210 void blit(struct image *src, int sx, int sy, int w, int h, struct image *dst, int dx, int dy)
211 {
212         int i;
213         unsigned char *sptr, *dptr;
214
215         assert(src->bpp == dst->bpp);
216         assert(src->nchan == dst->nchan);
217
218         if(sx < 0) { w += sx; sx = 0; }
219         if(sy < 0) { h += sy; sy = 0; }
220         if(dx < 0) { w += dx; sx -= dx; dx = 0; }
221         if(dy < 0) { h += dy; sy -= dy; dy = 0; }
222         if(sx + w >= src->width) w = src->width - sx;
223         if(sy + h >= src->height) h = src->height - sy;
224         if(dx + w >= dst->width) w = dst->width - dx;
225         if(dy + h >= dst->height) h = dst->height - dy;
226
227         if(w <= 0 || h <= 0) return;
228
229         sptr = src->pixels + sy * src->pitch + sx * src->bpp / 8;
230         dptr = dst->pixels + dy * dst->pitch + dx * dst->bpp / 8;
231
232         for(i=0; i<h; i++) {
233                 memcpy(dptr, sptr, w * dst->bpp / 8);
234                 dptr += dst->pitch;
235                 sptr += src->pitch;
236         }
237 }