Add bmp2png utility.
[platform/upstream/SSAT.git] / util / bmp2png.c
1 // SPDX-License-Identifier: Apache-2.0
2 /**
3  * BMP2PNG Converter with libpng
4  * Copyright (C) 2018 MyungJoo Ham <myungjoo.ham@samsung.com>
5  */
6 /**
7  * @file        bmp2png.c
8  * @date        13 Jul 2018
9  * @brief       Simple bmp2png converter for testcases
10  * @see         http://github.com/nnstreamer/nnstreamer
11  * @author      MyungJoo Ham <myungjoo.ham@samsung.com>
12  * @bug         No known bugs except for NYI items
13  *
14  * This converts bmp files created by gen24bBMP.py.
15  * This won't support general bmp files.
16  *
17  * Adopted code from https://www.lemoda.net/c/write-png/
18  * The author, "Ben Bullock <benkasminbullock@gmail.com>", has authorized
19  * to adopt the code as LGPL-2.1 on 2018-07-13
20  *
21  * Then, migrated code from https://github.com/nnstreamer/nnstreamer
22  * by the author, "MyungJoo Ham <myungjoo.ham@samsung.com>", changing
23  * the licence to Apache 2.0
24  */
25
26 #include <png.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <string.h>
31 #include <glib.h>
32
33 typedef enum
34 {
35   RGB = 0,
36   GRAY8,
37 } colorformat_t;
38
39 typedef union
40 {
41   struct
42   {
43     uint8_t red;
44     uint8_t green;
45     uint8_t blue;
46   };
47   struct
48   {
49     uint8_t gray;
50   };
51 } pixel_t;
52
53 typedef struct
54 {
55   pixel_t *pixels;
56   size_t width;
57   size_t height;
58   colorformat_t color_format;
59 } bitmap_t;
60
61 /**
62  * @brief Given "bitmap", this returns the pixel of bitmap at the point
63  *  ("x", "y").
64  */
65 static pixel_t *
66 pixel_at (bitmap_t * bitmap, int x, int y)
67 {
68   return bitmap->pixels + bitmap->width * y + x;
69 }
70
71 /**
72  * @brief Write "bitmap" to a PNG file specified by "path"
73  * @return 0 on success, non-zero on error.
74  */
75 static int
76 save_png_to_file (bitmap_t * bitmap, const char *path)
77 {
78   FILE *fp;
79   png_structp png_ptr = NULL;
80   png_infop info_ptr = NULL;
81   size_t x, y;
82   png_byte **row_pointers = NULL;
83   /**
84    * "status" contains the return value of this function. At first
85    * it is set to a value which means 'failure'. When the routine
86    * has finished its work, it is set to a value which means
87    * 'success'.
88    */
89   int status = -1;
90   /**
91    * The following number is set by trial and error only. I cannot
92    * see where it it is documented in the libpng manual.
93    */
94   int pixel_size;
95   int depth = 8;
96   int color_type;
97
98   fp = fopen (path, "wb");
99   if (!fp) {
100     goto fopen_failed;
101   }
102
103   png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
104   if (png_ptr == NULL) {
105     goto png_create_write_struct_failed;
106   }
107
108   info_ptr = png_create_info_struct (png_ptr);
109   if (info_ptr == NULL) {
110     goto png_create_info_struct_failed;
111   }
112
113   /** Set up error handling. */
114
115   if (setjmp (png_jmpbuf (png_ptr))) {
116     status = -1;
117     goto png_failure;
118   }
119
120   /** Set image attributes. */
121   if (bitmap->color_format == GRAY8) {
122     pixel_size = 1;
123     color_type = PNG_COLOR_TYPE_GRAY;
124   } else {
125     pixel_size = 3;
126     color_type = PNG_COLOR_TYPE_RGB;
127   }
128
129   png_set_IHDR (png_ptr,
130       info_ptr,
131       bitmap->width,
132       bitmap->height,
133       depth,
134       color_type,
135       PNG_INTERLACE_NONE,
136       PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
137
138   /** Initialize rows of PNG. */
139   row_pointers = png_malloc (png_ptr, bitmap->height * sizeof (png_byte *));
140   g_assert (row_pointers != NULL);
141   for (y = 0; y < bitmap->height; y++) {
142     png_byte *row =
143         png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * pixel_size);
144     g_assert (row != NULL);
145     row_pointers[y] = row;
146     for (x = 0; x < bitmap->width; x++) {
147       pixel_t *pixel = pixel_at (bitmap, x, y);
148       if (bitmap->color_format == GRAY8) {
149         *row++ = pixel->gray;
150       } else {
151         *row++ = pixel->red;
152         *row++ = pixel->green;
153         *row++ = pixel->blue;
154       }
155     }
156   }
157
158   /** Write the image data to "fp". */
159
160   png_init_io (png_ptr, fp);
161   png_set_rows (png_ptr, info_ptr, row_pointers);
162   png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
163
164   /**
165    * The routine has successfully written the file, so we set
166    * "status" to a value which indicates success.
167    */
168
169   status = 0;
170
171   for (y = 0; y < bitmap->height; y++) {
172     png_free (png_ptr, row_pointers[y]);
173   }
174   png_free (png_ptr, row_pointers);
175
176 png_failure:
177 png_create_info_struct_failed:
178   png_destroy_write_struct (&png_ptr, &info_ptr);
179 png_create_write_struct_failed:
180   fclose (fp);
181 fopen_failed:
182   return status;
183 }
184
185 /**
186  * @brief The main function, provide filename of a bmp file as the 1st argument.
187  */
188 int
189 main (int argc, char *argv[])
190 {
191   const char option_gray[] = "--GRAY8";
192   FILE *bmpF;
193   bitmap_t bmp;
194   int x, y;
195   uint16_t width, height, *ptr16;
196   size_t size;
197   char byte;
198   char header[26];              /** gen24bBMP.py gives you 24B headered bmp file */
199   int ret;
200   char *pngfilename;
201   unsigned int strn;
202
203   /** Read the .bmp file (argv[1]) */
204   if (argc < 2 || argc > 3) {
205     printf ("Usage: %s BMPfilename [OPTION:--GRAY8]\n\n", argv[0]);
206     return 1;
207   }
208   strn = strlen (argv[1]);
209   if (strn < 5 || argv[1][strn - 4] != '.' || argv[1][strn - 3] != 'b' ||
210       argv[1][strn - 2] != 'm' || argv[1][strn - 1] != 'p') {
211     printf ("The BMPfilename must be ending with \".bmp\"\n\n");
212     return 1;
213   }
214   /** Check the option, --GRAY8 */
215   strn = strlen (option_gray);
216   if ((argc == 3) && (strlen (argv[2]) == strn)
217       && (!strncmp (option_gray, argv[2], strn))) {
218     bmp.color_format = GRAY8;
219   } else {
220     bmp.color_format = RGB;
221   }
222
223   bmpF = fopen (argv[1], "rb");
224   if (!bmpF) {
225     printf ("Cannot open the file: %s\n", argv[1]);
226     return 2;
227   }
228
229   size = fread (header, 1, 26, bmpF);
230   if (size != 26) {
231     printf ("Cannot read the header from the file: %s\n", argv[1]);
232     fclose (bmpF);
233     return 3;
234   }
235
236   ptr16 = (uint16_t *) & (header[18]);
237   width = *ptr16;
238   ptr16 = (uint16_t *) & (header[20]);
239   height = *ptr16;
240
241   /** Let's not accept BMP files larger than 10000 x 10000 (Fix Covertify Issue #1029514) */
242   if (width > 10000 || height > 10000 || width < 4 || height < 4) {
243     printf
244         ("We do not accept BMP files with height or width larger than 10000.\n");
245     fclose (bmpF);
246     return 100;
247   }
248   bmp.width = width;
249   bmp.height = height;
250
251   bmp.pixels = calloc (bmp.width * bmp.height, sizeof (pixel_t));
252   g_assert (bmp.pixels != NULL);
253
254   for (y = (int) height - 1; y >= 0; y--) {
255     for (x = 0; x < (int) width; x++) {
256       pixel_t *pixel = pixel_at (&bmp, x, y);
257       if (bmp.color_format == GRAY8) {
258         uint8_t gray;
259         size = fread (&gray, 1, 1, bmpF);
260         if (size != 1) {
261           printf ("x = %d / y = %d / (%d,%d) / size = %zu\n", x, y, width,
262               height, size);
263           goto error;
264         }
265         pixel->gray = gray;
266       } else {
267         uint8_t bgr[3];
268         size = fread (bgr, 1, 3, bmpF);
269         if (size != 3) {
270           printf ("x = %d / y = %d / (%d,%d) / size = %zu\n", x, y, width,
271               height, size);
272           goto error;
273         }
274         pixel->red = bgr[2];
275         pixel->green = bgr[1];
276         pixel->blue = bgr[0];
277       }
278     }
279     for (x = 0; x < (width * 3) % 4; x++) {
280       size = fread (&byte, 1, 1, bmpF);
281       /** Go through zero padding */
282       if (size != 1)
283         goto error;
284     }
285   }
286   fclose (bmpF);
287
288   pngfilename = g_strdup (argv[1]);
289
290   /** Assume the last 4 characters are ".bmp" */
291   strncpy (pngfilename + strlen (argv[1]) - 4, ".png", 5);
292   ret = save_png_to_file (&bmp, pngfilename);
293   free (bmp.pixels);
294
295   return ret;
296 error:
297   printf ("File read size error.\n");
298   free (bmp.pixels);
299   fclose (bmpF);
300   return 10;
301 }