BPicture: Fix archive constructor.
[haiku.git] / src / tools / hvif2png / hvif2png.cpp
blob1dca2ece8bde30a99cb76be4b91cd2f5d6e5caa1
1 /*
2 * Copyright 2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Andrew Lindesay
7 */
10 // This command line program was created in order to be able to render
11 // HVIF files and then save the resultant bitmap into a PNG image file.
12 // The tool can be compiled for linux and was initially created for
13 // use with the Haiku Depot Server application server so that it was
14 // able to render HVIFs in the web page.
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
21 #include <png.h>
23 #include <Bitmap.h>
24 #include <IconUtils.h>
25 #include <InterfaceDefs.h>
26 #include <SupportDefs.h>
28 #include <AutoDeleter.h>
31 #define SIZE_HVIF_BUFFER_STEP 1024
34 static const uint8 kHvifMagic[] = { 'n', 'c', 'i', 'f' };
37 typedef struct h2p_hvif_buffer {
38 uint8* buffer;
39 size_t used;
40 size_t allocated;
41 } h2p_hvif_buffer;
44 typedef struct h2p_parameters {
45 int size;
46 char* in_filename;
47 char* out_filename;
48 } h2p_parameters;
51 typedef struct h2p_state {
52 FILE* in;
53 FILE* out;
54 BBitmap* bitmap;
55 h2p_hvif_buffer hvif_buffer;
56 h2p_parameters params;
57 } h2p_state;
60 static int
61 h2p_fprintsyntax(FILE* stream)
63 return fprintf(stream, "syntax: hvif2png -s <size> [-i <input-file>]"
64 " [-o <output-file>]\n");
68 static void
69 h2p_close_state(h2p_state* state)
71 if (state->hvif_buffer.buffer != NULL)
72 free(state->hvif_buffer.buffer);
74 if (state->in != NULL) {
75 if (state->in != stdin)
76 fclose(state->in);
77 state->in = NULL;
80 if (state->out != NULL) {
81 if (state->out != stdout)
82 fclose(state->out);
83 state->out = NULL;
88 /*! Opens the input and output streams for the conversion.
89 \return false if there was some problem in opening the streams; otherwise
90 true.
92 static bool
93 h2p_open_streams(h2p_state* state)
95 CObjectDeleter<h2p_state> stateCloser(state, &h2p_close_state);
97 if (state->params.in_filename != NULL)
98 state->in = fopen(state->params.in_filename, "rb");
99 else
100 state->in = stdin;
102 if (state->in == NULL) {
103 fprintf(stderr, "unable to open the input file; '%s'\n",
104 state->params.in_filename);
105 return false;
108 if (state->params.out_filename != NULL)
109 state->out = fopen(state->params.out_filename, "wb");
110 else
111 state->out = stdout;
113 if (state->out == NULL) {
114 fprintf(stderr, "unable to open the output file; '%s'\n",
115 state->params.out_filename);
116 return false;
119 stateCloser.Detach();
121 return true;
125 static bool
126 h2p_write_png(BBitmap* bitmap, FILE* out)
128 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL,
129 NULL);
131 if (png == NULL) {
132 fprintf(stderr, "unable to setup png write data structures\n");
133 return false;
136 png_init_io(png, out);
137 png_infop info = png_create_info_struct(png);
139 bool result = false;
141 if (info != NULL) {
142 BRect rect = bitmap->Bounds();
143 png_uint_32 width = (png_uint_32)rect.Width() + 1;
144 png_uint_32 height = (png_uint_32)rect.Height() + 1;
146 png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGBA,
147 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
148 PNG_FILTER_TYPE_BASE);
150 png_set_bgr(png);
152 png_write_info(png, info);
154 uint8 *bitmapData = (uint8*)bitmap->Bits();
155 int32 bitmapBytesPerRow = bitmap->BytesPerRow();
157 for (png_uint_32 i = 0; i < height; i++) {
158 png_write_row(png,
159 (png_bytep)&bitmapData[i * bitmapBytesPerRow]);
162 png_write_end(png, NULL);
164 png_free_data(png, info, PNG_FREE_ALL, -1);
166 result = true;
167 } else
168 fprintf(stderr, "unable to setup png info data structures\n");
170 png_destroy_write_struct(&png, (png_infopp)NULL);
172 return result;
176 /*! Reads the HVIF input data from the supplied input file.
177 \return the quantity of bytes that were read from the
178 HVIF file or 0 if there was a problem reading the HVIF data.
180 static size_t
181 h2p_read_hvif_input(h2p_hvif_buffer* result, FILE* in)
183 result->buffer = (uint8*)malloc(SIZE_HVIF_BUFFER_STEP);
184 result->allocated = SIZE_HVIF_BUFFER_STEP;
185 result->used = 0;
187 if (result->buffer == NULL) {
188 fprintf(stderr,"out of memory\n");
189 return 0;
192 while (!feof(in)) {
193 if (result->used == result->allocated) {
194 result->buffer = (uint8 *)realloc(result->buffer,
195 result->allocated + SIZE_HVIF_BUFFER_STEP);
197 if (result->buffer == NULL) {
198 fprintf(stderr,"out of memory\n");
199 return 0;
202 result->allocated += SIZE_HVIF_BUFFER_STEP;
205 result->used += fread(&result->buffer[result->used], sizeof(uint8),
206 result->allocated - result->used, in);
208 int err = ferror(in);
210 if (err != 0) {
211 fprintf(stderr, "error reading input; %s\n", strerror(err));
212 return 0;
216 if (result->used < 4) {
217 fprintf(stderr, "the hvif data is too small to visably be valid\n");
218 return 0;
221 // hvif files have a magic string of "ncif" so we should check for that as
222 // well.
224 if (memcmp(result->buffer, kHvifMagic, 4) != 0) {
225 fprintf(stderr, "the input data does not look like hvif because the"
226 " magic string is not 'ncif'; %d, %d, %d, %d\n",
227 result->buffer[0], result->buffer[1], result->buffer[2],
228 result->buffer[3]);
229 return 0;
232 return result->used;
236 /*! Parse the arguments to the conversion program from the command line.
237 \return false if there was a problem reading the parameters and true
238 otherwise.
240 static bool
241 h2p_parse_args(h2p_parameters* result, int argc, char* argv[])
243 for (int i = 1;i < argc;) {
244 if (argv[i][0] != '-') {
245 fprintf(stderr, "was expecting a switch; found '%s'\n",argv[i]);
246 h2p_fprintsyntax(stderr);
247 return false;
250 if (strlen(argv[i]) != 2) {
251 fprintf(stderr, "illegal switch; '%s'\n", argv[i]);
252 h2p_fprintsyntax(stderr);
253 return false;
256 switch (argv[i][1]) {
257 case 's':
258 if (i == argc - 1) {
259 fprintf(stderr,"the size has not been specified\n");
260 h2p_fprintsyntax(stderr);
261 return false;
264 result->size = atoi(argv[i + 1]);
266 if (result->size <= 0 || result->size > 1024) {
267 fprintf(stderr,"bad size specified; '%s'\n", argv[i]);
268 h2p_fprintsyntax(stderr);
269 return false;
272 i+=2;
273 break;
275 case 'i':
276 if (i == argc - 1) {
277 fprintf(stderr,
278 "the input filename has not been specified\n");
279 h2p_fprintsyntax(stderr);
280 return false;
283 result->in_filename = argv[i + 1];
284 i+=2;
285 break;
287 case 'o':
288 if (i == argc - 1) {
289 fprintf(stderr,
290 "the output filename has not been specified\n");
291 h2p_fprintsyntax(stderr);
292 return false;
295 result->out_filename = argv[i + 1];
296 i += 2;
297 break;
299 default:
300 fprintf(stderr, "unrecognized switch; '%s'\n", argv[i]);
301 h2p_fprintsyntax(stderr);
302 return false;
306 if (result->size == 0) {
307 fprintf(stderr, "size has not been specified\n");
308 h2p_fprintsyntax(stderr);
309 return false;
312 return true;
317 main(int argc, char* argv[])
319 if (argc == 1) {
320 h2p_fprintsyntax(stderr);
321 return 1;
324 h2p_state state;
325 bzero(&state, sizeof(state));
327 if (!h2p_parse_args(&state.params, argc, argv))
328 return 1;
330 if (!h2p_open_streams(&state))
331 return 1;
333 int exitResult = 1;
335 if (h2p_read_hvif_input(&state.hvif_buffer, state.in) > 0) {
336 // create the bitmap and then parse and render the HVIF icon
337 // data into the bitmap.
339 state.bitmap = new BBitmap(
340 BRect(0.0, 0.0, state.params.size - 1,
341 state.params.size - 1),
342 B_RGBA32); // actual storage is BGRA
344 status_t gviStatus = BIconUtils::GetVectorIcon(
345 state.hvif_buffer.buffer,
346 state.hvif_buffer.used,
347 state.bitmap);
349 if (gviStatus != B_OK) {
350 fprintf(stderr, "the hvif data (%zdB) was not able to "
351 "be parsed / rendered\n", state.hvif_buffer.used);
352 } else {
353 // write the bitmap data out again as a PNG.
354 if (h2p_write_png(state.bitmap, state.out))
355 exitResult = 0;
359 // clean up
360 h2p_close_state(&state);
362 return exitResult;