2 * Copyright 2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
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.
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
{
44 typedef struct h2p_parameters
{
51 typedef struct h2p_state
{
55 h2p_hvif_buffer hvif_buffer
;
56 h2p_parameters params
;
61 h2p_fprintsyntax(FILE* stream
)
63 return fprintf(stream
, "syntax: hvif2png -s <size> [-i <input-file>]"
64 " [-o <output-file>]\n");
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
)
80 if (state
->out
!= NULL
) {
81 if (state
->out
!= stdout
)
88 /*! Opens the input and output streams for the conversion.
89 \return false if there was some problem in opening the streams; otherwise
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");
102 if (state
->in
== NULL
) {
103 fprintf(stderr
, "unable to open the input file; '%s'\n",
104 state
->params
.in_filename
);
108 if (state
->params
.out_filename
!= NULL
)
109 state
->out
= fopen(state
->params
.out_filename
, "wb");
113 if (state
->out
== NULL
) {
114 fprintf(stderr
, "unable to open the output file; '%s'\n",
115 state
->params
.out_filename
);
119 stateCloser
.Detach();
126 h2p_write_png(BBitmap
* bitmap
, FILE* out
)
128 png_structp png
= png_create_write_struct(PNG_LIBPNG_VER_STRING
, NULL
, NULL
,
132 fprintf(stderr
, "unable to setup png write data structures\n");
136 png_init_io(png
, out
);
137 png_infop info
= png_create_info_struct(png
);
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
);
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
++) {
159 (png_bytep
)&bitmapData
[i
* bitmapBytesPerRow
]);
162 png_write_end(png
, NULL
);
164 png_free_data(png
, info
, PNG_FREE_ALL
, -1);
168 fprintf(stderr
, "unable to setup png info data structures\n");
170 png_destroy_write_struct(&png
, (png_infopp
)NULL
);
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.
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
;
187 if (result
->buffer
== NULL
) {
188 fprintf(stderr
,"out of memory\n");
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");
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
);
211 fprintf(stderr
, "error reading input; %s\n", strerror(err
));
216 if (result
->used
< 4) {
217 fprintf(stderr
, "the hvif data is too small to visably be valid\n");
221 // hvif files have a magic string of "ncif" so we should check for that as
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],
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
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
);
250 if (strlen(argv
[i
]) != 2) {
251 fprintf(stderr
, "illegal switch; '%s'\n", argv
[i
]);
252 h2p_fprintsyntax(stderr
);
256 switch (argv
[i
][1]) {
259 fprintf(stderr
,"the size has not been specified\n");
260 h2p_fprintsyntax(stderr
);
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
);
278 "the input filename has not been specified\n");
279 h2p_fprintsyntax(stderr
);
283 result
->in_filename
= argv
[i
+ 1];
290 "the output filename has not been specified\n");
291 h2p_fprintsyntax(stderr
);
295 result
->out_filename
= argv
[i
+ 1];
300 fprintf(stderr
, "unrecognized switch; '%s'\n", argv
[i
]);
301 h2p_fprintsyntax(stderr
);
306 if (result
->size
== 0) {
307 fprintf(stderr
, "size has not been specified\n");
308 h2p_fprintsyntax(stderr
);
317 main(int argc
, char* argv
[])
320 h2p_fprintsyntax(stderr
);
325 bzero(&state
, sizeof(state
));
327 if (!h2p_parse_args(&state
.params
, argc
, argv
))
330 if (!h2p_open_streams(&state
))
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
,
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
);
353 // write the bitmap data out again as a PNG.
354 if (h2p_write_png(state
.bitmap
, state
.out
))
360 h2p_close_state(&state
);