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