1 /*
2  * Copyright 2008 Sean Fox <dyntryx@gmail.com>
3  * Copyright 2008 James Bursa <james@netsurf-browser.org>
4  *
5  * This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/
6  * Licenced under the MIT License,
7  *                http://www.opensource.org/licenses/mit-license.php
8  */
9 
10 /**
11  * \file
12  * Use libnsbmp to decode icons into ppm or pam files for testing
13  */
14 
15 #include <assert.h>
16 #include <errno.h>
17 #include <stdbool.h>
18 #include <stdlib.h>
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include "../include/libnsbmp.h"
24 
25 /* Currently the library returns the data in RGBA format,
26  * so there are 4 bytes per pixel */
27 #define BYTES_PER_PIXEL 4
28 
29 /* White with alpha masking. */
30 #define TRANSPARENT_COLOR 0xffffffff
31 
bitmap_create(int width,int height,unsigned int state)32 static void *bitmap_create(int width, int height, unsigned int state)
33 {
34         (void) state;  /* unused */
35         return calloc(width * height, BYTES_PER_PIXEL);
36 }
37 
38 
bitmap_get_buffer(void * bitmap)39 static unsigned char *bitmap_get_buffer(void *bitmap)
40 {
41         assert(bitmap);
42         return bitmap;
43 }
44 
45 
bitmap_get_bpp(void * bitmap)46 static size_t bitmap_get_bpp(void *bitmap)
47 {
48         (void) bitmap;  /* unused */
49         return BYTES_PER_PIXEL;
50 }
51 
52 
bitmap_destroy(void * bitmap)53 static void bitmap_destroy(void *bitmap)
54 {
55         assert(bitmap);
56         free(bitmap);
57 }
58 
load_file(const char * path,size_t * data_size)59 static unsigned char *load_file(const char *path, size_t *data_size)
60 {
61         FILE *fd;
62         struct stat sb;
63         unsigned char *buffer;
64         size_t size;
65         size_t n;
66 
67         fd = fopen(path, "rb");
68         if (!fd) {
69                 perror(path);
70                 exit(EXIT_FAILURE);
71         }
72 
73         if (stat(path, &sb)) {
74                 perror(path);
75                 exit(EXIT_FAILURE);
76         }
77         size = sb.st_size;
78 
79         buffer = malloc(size);
80         if (!buffer) {
81                 fprintf(stderr, "Unable to allocate %lld bytes\n",
82                         (long long) size);
83                 exit(EXIT_FAILURE);
84         }
85 
86         n = fread(buffer, 1, size, fd);
87         if (n != size) {
88                 perror(path);
89                 exit(EXIT_FAILURE);
90         }
91 
92         fclose(fd);
93 
94         *data_size = size;
95         return buffer;
96 }
97 
98 
warning(const char * context,bmp_result code)99 static void warning(const char *context, bmp_result code)
100 {
101         fprintf(stderr, "%s failed: ", context);
102         switch (code) {
103         case BMP_INSUFFICIENT_MEMORY:
104                 fprintf(stderr, "BMP_INSUFFICIENT_MEMORY");
105                 break;
106         case BMP_INSUFFICIENT_DATA:
107                 fprintf(stderr, "BMP_INSUFFICIENT_DATA");
108                 break;
109         case BMP_DATA_ERROR:
110                 fprintf(stderr, "BMP_DATA_ERROR");
111                 break;
112         default:
113                 fprintf(stderr, "unknown code %i", code);
114                 break;
115         }
116         fprintf(stderr, "\n");
117 }
118 
119 
write_pam(FILE * fh,const char * name,struct bmp_image * bmp)120 static void write_pam(FILE* fh, const char *name, struct bmp_image *bmp)
121 {
122         uint16_t row, col;
123         uint8_t *image;
124 
125         fprintf(fh, "P7\n");
126         fprintf(fh, "# %s\n", name);
127         fprintf(fh, "WIDTH %u\n", bmp->width);
128         fprintf(fh, "HEIGHT %u\n", bmp->height);
129         fprintf(fh, "DEPTH 4\n");
130         fprintf(fh, "MAXVAL 255\n");
131         fprintf(fh, "TUPLTYPE RGB_ALPHA\n");
132         fprintf(fh, "ENDHDR\n");
133 
134 
135         image = (uint8_t *) bmp->bitmap;
136         for (row = 0; row != bmp->height; row++) {
137                 for (col = 0; col != bmp->width; col++) {
138                         size_t z = (row * bmp->width + col) * BYTES_PER_PIXEL;
139                         putc(image[z], fh);
140                         putc(image[z + 1], fh);
141                         putc(image[z + 2], fh);
142                         putc(image[z + 3], fh);
143                 }
144         }
145 
146 }
147 
write_ppm(FILE * fh,const char * name,struct bmp_image * bmp)148 static void write_ppm(FILE* fh, const char *name, struct bmp_image *bmp)
149 {
150         uint16_t row, col;
151         uint8_t *image;
152 
153         fprintf(fh, "P3\n");
154         fprintf(fh, "# %s\n", name);
155         fprintf(fh, "# width                %u \n", bmp->width);
156         fprintf(fh, "# height               %u \n", bmp->height);
157         fprintf(fh, "%u %u 256\n", bmp->width, bmp->height);
158 
159         image = (uint8_t *) bmp->bitmap;
160         for (row = 0; row != bmp->height; row++) {
161                 for (col = 0; col != bmp->width; col++) {
162                         size_t z = (row * bmp->width + col) * BYTES_PER_PIXEL;
163                         fprintf(fh, "%u %u %u ",
164                                 image[z],
165                                 image[z + 1],
166                                 image[z + 2]);
167                 }
168                 fprintf(fh, "\n");
169         }
170 }
171 
main(int argc,char * argv[])172 int main(int argc, char *argv[])
173 {
174         bmp_bitmap_callback_vt bitmap_callbacks = {
175                 bitmap_create,
176                 bitmap_destroy,
177                 bitmap_get_buffer,
178                 bitmap_get_bpp
179         };
180         uint16_t width, height;
181         ico_collection ico;
182         bmp_result code;
183         struct bmp_image *bmp;
184         size_t size;
185         unsigned short res = 0;
186         unsigned char *data;
187         FILE *outf = stdout;
188 
189         if ((argc < 2) || (argc > 5)) {
190                 fprintf(stderr, "Usage: %s collection.ico [width=255] [height=255] [outfile]\n", argv[0]);
191                 return 1;
192         }
193         width = (argc >= 3) ?  atoi(argv[2]) : 255;
194         height = (argc >= 4) ? atoi(argv[3]) : 255;
195 
196         if (argc >= 5) {
197                 outf = fopen(argv[4], "w+");
198                 if (outf == NULL) {
199                         fprintf(stderr, "Unable to open %s for writing\n", argv[2]);
200                         return 2;
201                 }
202         }
203 
204         /* create our bmp image */
205         ico_collection_create(&ico, &bitmap_callbacks);
206 
207         /* load file into memory */
208         data = load_file(argv[1], &size);
209 
210         /* analyse the BMP */
211         code = ico_analyse(&ico, size, data);
212         if (code != BMP_OK) {
213                 warning("ico_analyse", code);
214                 res = 3;
215                 goto cleanup;
216         }
217 
218         /* decode the image */
219         bmp = ico_find(&ico, width, height);
220         assert(bmp);
221 
222         code = bmp_decode(bmp);
223         /* code = bmp_decode_trans(bmp, TRANSPARENT_COLOR); */
224         if (code != BMP_OK) {
225                 warning("bmp_decode", code);
226                 /* allow partially decoded images */
227                 if ((code != BMP_INSUFFICIENT_DATA) &&
228                     (code != BMP_DATA_ERROR)) {
229                         res = 1;
230                         goto cleanup;
231                 }
232 
233                 /* skip if the decoded image would be ridiculously large */
234                 if ((bmp->width * bmp->height) > 200000) {
235                         res = 1;
236                         goto cleanup;
237                 }
238         }
239 
240         if (bmp->opaque) {
241                 write_ppm(outf, argv[1], bmp);
242         } else {
243                 write_pam(outf, argv[1], bmp);
244         }
245 
246         if (argc >= 5) {
247                 fclose(outf);
248         }
249 
250 cleanup:
251         /* clean up */
252         ico_finalise(&ico);
253         free(data);
254 
255         return res;
256 }
257