问题
I want to say, I have searched in the internet about this topic, but it doesn't apply to my situation.
I was doing modifications for a game that uses C and I'm editing the images for use for the game, but the images needs to be converted to c headers to make them work. The game is multi-platform, with builds for Windows and Android via NDK.
I've actually accomplished some of the editing with "Using .c/.h images exported with gimp in SDL" as my basis for my solution, using mbin2h, which converts any binary file into something that's fit for a C header. It worked for the Android build, but not for the main cross platform C build.
Here's an example code:
This is what mbin2h outputs usually:
0x89, 'P', 'N', 'G', 0xD, 0xA, 0x1A, 0xA, 0x0, 0x0, 0x0, 0xD, 'I', 'H', 'D', 'R'
Here's from the original source:
"\x89""PNG""\15\12\32\12\0\0\0\15""IHDR"
I don't know what program they used to convert the PNG to the header file. As some of you pointed out, it's a combination of hex, ASCII characters and octal values.
The question is how to convert the png to a header file similar to the original code? To make it easy for others to figure out, I've placed in the zip file the original image, the original header and the header generated by mbin2h: image problem.zip - 319.73
This is actually for OpenBOR and I only wanted to modify the menu image, but because I don't know how to program that well, so I'm going to need some help, sorry about that.
EDIT: I didn't see that there's an answer button, I'll put the answer up. Sorry guys.
回答1:
Converting binary to C-compatible data is not that difficult, although I do wonder why the created structure is needlessly complex. Then again, if the rest of the source code expects this particular format, it's just a case of copying the original structure and filling in the right values.
.. it's a combination of hex, ASCII characters and octal values ..
That would only be to make the source file as short as possible. There is no further reason; the entire image could be written out as \x..
hex codes as well, or even decimals for that matter.
It was a nice additional challenge to make the header file as small as possible. Other than in the original, I made sure no single line exceeds 100 characters; still, by using more 'shortest alternatives', for the test image my code only produces 1,626 lines where the original has 1,921; 18% less!
Usage: programName input.png
It will replace the .png
part of the input file with .h
and write this output to the current folder. Works on OSX, and I guess it should compile on other *nix-like systems as well, as I tried to limit myself to Posix file I/O. Whether this does or does not include Windows is left as "exercise for the reader".
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#define MAX_LINE_LENGTH 100
int main (int argc, char **argv)
{
FILE *f_in, *f_out;
char *name_ptr,*ptr;
off_t i;
struct stat st;
char out_name[FILENAME_MAX];
unsigned char png_byte, last_byte_octal, output_length;
if (argc != 2)
{
fprintf (stderr, "this requires an input file\n");
return -1;
}
f_in = fopen (argv[1], "rb");
if (!f_in)
{
fprintf (stderr, "this requires the input file to exist\n");
return -1;
}
if (stat(argv[1], &st))
{
fprintf (stderr, "this requires the input file to have no problems\n");
return -1;
}
name_ptr = strrchr (argv[1], '/'); // *nix path
if (!name_ptr)
name_ptr = strrchr (argv[1], '\\'); // Wind*ws path
if (name_ptr)
name_ptr++;
else
name_ptr = argv[1];
if (!*name_ptr || strlen(name_ptr) >= FILENAME_MAX)
{
fprintf (stderr, "this requires a reasonable length for the file name\n");
return -1;
}
strcpy (out_name, name_ptr);
ptr = out_name;
while (*ptr)
{
*ptr = tolower(*ptr);
ptr++;
}
ptr = strrchr (out_name, '.');
if (*ptr)
strcpy (ptr, ".h");
else
strcat (out_name, ".h");
f_out = fopen (out_name, "w");
if (!f_out)
{
fprintf (stderr, "this requires the output file to be created\n");
return -1;
}
fprintf (stderr, "creating %s, please hold on.\n", out_name);
ptr = out_name;
while (*ptr)
{
if (*ptr == '.') *ptr = '_';
ptr++;
}
for (i=0; i<2; i++)
{
fprintf (f_out, "#%s", !i ? "ifndef" : "define");
fprintf (f_out, " _");
ptr = out_name;
while (*ptr)
{
fprintf (f_out, "%c", toupper(*ptr));
ptr++;
}
fprintf (f_out, "_\n");
}
fprintf (f_out, "\n"
"static const struct {\n"
"\tsize_t size;\n"
"\tunsigned char data [%lu];\n"
"} ", (unsigned long)st.st_size);
ptr = name_ptr;
while (*ptr)
{
if (*ptr == '.')
fprintf (f_out, "_");
else
fprintf (f_out, "%c", tolower(*ptr));
ptr++;
}
fprintf (f_out, " = {\n"
"\t%lu,\n\"", (unsigned long)st.st_size);
last_byte_octal = 0;
output_length = 1;
for (i=0; i<st.st_size; i++)
{
png_byte = fgetc (f_in);
if (png_byte == '\\')
{
output_length += 2;
if (output_length >= MAX_LINE_LENGTH)
{
fprintf (f_out, "\"\n\"");
output_length = 3;
}
fprintf (f_out, "\\\\");
last_byte_octal = 0;
}
else if (png_byte == 9)
{
output_length += 2;
if (output_length >= MAX_LINE_LENGTH)
{
fprintf (f_out, "\"\n\"");
output_length = 3;
}
fprintf (f_out, "\\t");
last_byte_octal = 0;
} else if (png_byte < ' ' || png_byte == '\"')
{
output_length += (png_byte < 8) ? 2 : 3;
last_byte_octal = 1;
if (output_length >= MAX_LINE_LENGTH)
{
fprintf (f_out, "\"\n\"");
output_length = (png_byte < 8) ? 3 : 4;
}
fprintf (f_out, "\\%o", png_byte);
} else if (png_byte > '~')
{
output_length += 4;
if (output_length >= MAX_LINE_LENGTH)
{
fprintf (f_out, "\"\n\"");
output_length = 5;
}
fprintf (f_out, "\\x%X", png_byte);
last_byte_octal = 1;
} else
{
output_length += (last_byte_octal && isxdigit(png_byte)) ? 3 : 1;
if (output_length >= MAX_LINE_LENGTH)
{
fprintf (f_out, "\"\n\"");
output_length = 2;
last_byte_octal = 0;
}
if (last_byte_octal && isxdigit(png_byte))
fprintf (f_out, "\"\"");
fprintf (f_out, "%c", png_byte);
last_byte_octal = 0;
}
}
fprintf (f_out, "\"\n};\n\n#endif\n");
fclose (f_in);
fclose (f_out);
fprintf (stderr, "done.\n");
return 0;
}
回答2:
I don't know what mbin2h
is (even if I guess what it does). I suggest you to modify your build (e.g. your Makefile
) to generate some image.cdata
file with a command like perhaps
mbin2h image.png > image.cdata
then have a C file containing (near its beginning, and with some other functions later)
const char image_data[] = {
#include "image.cdata"
};
Then call appropriate routines using image_data
and probably sizeof(image_data)
; probably according to this answer something similar to
SDL_RWops * z = SDL_RWFromMem(image_data,sizeof(image_data));
Of course that code should appear in the same C file as the const char image_data[]
definition above (and after that definition).
PS. As 5gon12eder answered you should prefer hexdump
to your mbin2h
回答3:
In addition to @BasileStarynkevitch's answer and in reply to your comment asking for “a script”: You can generate said include file image.cdata
eg using the Posix hexdump
utility. In your makefile:
image.cdata: image.png
hexdump --format='8/1 " 0x%02x," "\n"' $< > $@
The hex format is certainly not the most space efficient one. Read man hexdump if you feel an urge to improve this.
回答4:
I solved it at last! As suggested by @Basile and @5gon12eder, I used what I did similarly in the android build:
char openbor_menu_480x272_png[32851]={
...
};
I declared it as char and added the array size to it. The ...
is the output of mbin2h. The hexdump method also works too, but you may need to calculate the array size manually.
I then found the code used to decode the header to image in the OpenBOR code and did so:
// Read Logo or Menu from Array.
if(!type)
bgscreen = pngToScreen(isWide ? (void*) openbor_logo_480x272_png.data : (void*) openbor_logo_320x240_png.data);
else
//CRxTRDude - Removed '.data' since it doesn't use it anyway.
bgscreen = pngToScreen(isWide ? (void*) openbor_menu_480x272_png : (void*) openbor_menu_320x240_png);
I didn't edit the logo picture, so it stays as such.
Compiled it and it works! Apparently, since the android and the cross-platform builds all use the same PNG decoder, it actually did the job too.
But it just so happened that @Jongware actually made a C script that actually looked similar to the c header of the original and works! He also said that the reason that it's like that is for the sake of size, this gives off a smaller output than what mbin2h actually does.
"Other than in the original, I made sure no single line exceeds 100 characters; still, by using more 'shortest alternatives', for the test image my code only produces 1,626 lines where the original has 1,921; 18% less"
He was true though for one thing, the size of the header did reduce to 18%, but when it compiled though, the apk size didn't change though, but it's nice to note that this actually did a good job as well to replicate the original code and made it fractionally smaller.
Anyways, thanks guys for answering and I'm sorry if I'm misunderstood that much.
来源:https://stackoverflow.com/questions/25707030/converting-png-to-c-header-for-sdl