mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-25 22:18:58 +00:00
396 lines
10 KiB
C
396 lines
10 KiB
C
/*
|
|
Copyright (C) 2009 Red Hat, Inc.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <getopt.h>
|
|
#include <spice/macros.h>
|
|
|
|
|
|
#define TAB " "
|
|
|
|
#define ERROR(str) printf("%s: error: %s\n", prog_name, str); exit(-1);
|
|
|
|
static char *prog_name = NULL;
|
|
|
|
static size_t read_input(const char *file_name, uint8_t** out_buf)
|
|
{
|
|
int fd = open(file_name, O_RDONLY);
|
|
if (fd == -1) {
|
|
ERROR("open source file failed");
|
|
return 0;
|
|
}
|
|
struct stat file_stat;
|
|
if (fstat(fd, &file_stat) == -1) {
|
|
ERROR("fstat on source file failed");
|
|
return 0;
|
|
}
|
|
|
|
uint8_t *buf = malloc(file_stat.st_size);
|
|
|
|
if (!buf) {
|
|
close(fd);
|
|
ERROR("alloc mem failed");
|
|
return 0;
|
|
}
|
|
|
|
size_t to_read = file_stat.st_size;
|
|
uint8_t *buf_pos = buf;
|
|
while (to_read) {
|
|
int n = read(fd, buf_pos, to_read);
|
|
if (n <= 0) {
|
|
ERROR("read from source file failed");
|
|
close(fd);
|
|
free(buf);
|
|
return 0;
|
|
}
|
|
to_read -= n;
|
|
buf_pos += n;
|
|
}
|
|
close(fd);
|
|
*out_buf = buf;
|
|
return file_stat.st_size;
|
|
}
|
|
|
|
typedef struct __attribute__ ((__packed__)) BitmpaHeader {
|
|
uint32_t header_size;
|
|
uint32_t width;
|
|
uint32_t height;
|
|
uint16_t plans;
|
|
uint16_t bpp;
|
|
uint32_t compression;
|
|
uint32_t image_size;
|
|
uint32_t horizontal_resolution;
|
|
uint32_t vertical_resolution;
|
|
uint32_t num_colors;
|
|
uint32_t important_colors;
|
|
} BitmpaHeader;
|
|
|
|
typedef struct __attribute__ ((__packed__)) BMPFileHeader {
|
|
uint16_t magic;
|
|
uint32_t file_size;
|
|
uint32_t reserved;
|
|
uint32_t data_offset;
|
|
BitmpaHeader header;
|
|
} BMPFileHeader;
|
|
|
|
#define BI_RGB 0
|
|
|
|
typedef struct Pixmap {
|
|
uint32_t width;
|
|
uint32_t height;
|
|
uint32_t stride;
|
|
uint32_t bpp;
|
|
uint8_t* data;
|
|
} Pixmap;
|
|
|
|
static Pixmap *init_bitmap(size_t input_size, uint8_t *buf)
|
|
{
|
|
BMPFileHeader *file_header;
|
|
uint8_t *pixels;
|
|
Pixmap *pixmap;
|
|
uint32_t stride;
|
|
|
|
|
|
if (input_size < sizeof(BMPFileHeader)) {
|
|
ERROR("invalid source file");
|
|
return NULL;
|
|
}
|
|
|
|
file_header = (BMPFileHeader *)buf;
|
|
|
|
if (file_header->magic != 0x4d42) {
|
|
ERROR("bad bitmap magic");
|
|
return NULL;
|
|
}
|
|
|
|
if (file_header->file_size != input_size) {
|
|
ERROR("invalid source file");
|
|
return NULL;
|
|
}
|
|
|
|
if (file_header->header.header_size != 40 || file_header->header.plans != 1 ||
|
|
file_header->header.compression != BI_RGB ||
|
|
!file_header->header.width ||
|
|
!file_header->header.height) {
|
|
ERROR("invalid bitmap header");
|
|
return NULL;
|
|
}
|
|
|
|
if (file_header->header.bpp == 32) {
|
|
stride = file_header->header.width * sizeof(uint32_t);
|
|
} else if (file_header->header.bpp == 24) {
|
|
stride = SPICE_ALIGN(file_header->header.width * 3, 4);
|
|
} else {
|
|
ERROR("unsupported bpp");
|
|
return NULL;
|
|
}
|
|
|
|
if (file_header->header.height * stride > file_header->header.image_size) {
|
|
ERROR("image size is to small");
|
|
return NULL;
|
|
}
|
|
pixels = buf + file_header->data_offset;
|
|
if (pixels < (uint8_t *)(file_header + 1) ||
|
|
pixels + file_header->header.image_size > buf + input_size) {
|
|
ERROR("bad data offset");
|
|
return NULL;
|
|
}
|
|
|
|
if (!(pixmap = (Pixmap *)malloc(sizeof(*pixmap)))) {
|
|
ERROR("alloc mem failed");
|
|
return NULL;
|
|
}
|
|
|
|
pixmap->width = file_header->header.width;
|
|
pixmap->height = file_header->header.height;
|
|
pixmap->stride = stride;
|
|
pixmap->bpp = file_header->header.bpp;
|
|
pixmap->data = pixels;
|
|
return pixmap;
|
|
}
|
|
|
|
static inline void put_char(FILE* f, uint8_t val)
|
|
{
|
|
fprintf(f, "0x%.2x,", val);
|
|
}
|
|
|
|
static void do_dump_with_alpha_conversion(const Pixmap *pixmap, FILE *f)
|
|
{
|
|
uint8_t *line = (uint8_t *)(pixmap->data + ((pixmap->height - 1) * pixmap->stride));
|
|
int line_size = 0;
|
|
int i, j;
|
|
|
|
for (i = 0; i < pixmap->height; i++) {
|
|
uint8_t *now = line;
|
|
for (j = 0; j < pixmap->width; j++, now += 4) {
|
|
if ((line_size++ % 4) == 0) {
|
|
fprintf(f, "\n" TAB);
|
|
}
|
|
double alpha = (double)now[3] / 0xff;
|
|
put_char(f, alpha * now[0]);
|
|
put_char(f, alpha * now[1]);
|
|
put_char(f, alpha * now[2]);
|
|
put_char(f, now[3]);
|
|
}
|
|
line -= pixmap->stride;
|
|
}
|
|
}
|
|
|
|
static void do_dump_32bpp(const Pixmap *pixmap, FILE *f)
|
|
{
|
|
uint8_t *line = (uint8_t *)(pixmap->data + ((pixmap->height - 1) * pixmap->stride));
|
|
int line_size = 0;
|
|
int i, j;
|
|
|
|
for (i = 0; i < pixmap->height; i++) {
|
|
uint8_t *now = line;
|
|
for (j = 0; j < pixmap->width; j++, now += 4) {
|
|
if ((line_size++ % 4) == 0) {
|
|
fprintf(f, "\n" TAB);
|
|
}
|
|
put_char(f, now[0]);
|
|
put_char(f, now[1]);
|
|
put_char(f, now[2]);
|
|
put_char(f, now[3]);
|
|
}
|
|
line -= pixmap->stride;
|
|
}
|
|
}
|
|
|
|
static void do_dump_24bpp(const Pixmap *pixmap, FILE *f)
|
|
{
|
|
uint8_t *line = (uint8_t *)(pixmap->data + ((pixmap->height - 1) * pixmap->stride));
|
|
int line_size = 0;
|
|
int i, j;
|
|
|
|
for (i = 0; i < pixmap->height; i++) {
|
|
uint8_t *now = line;
|
|
for (j = 0; j < pixmap->width; j++, now += 3) {
|
|
if ((line_size++ % 4) == 0) {
|
|
fprintf(f, "\n" TAB);
|
|
}
|
|
put_char(f, now[0]);
|
|
put_char(f, now[1]);
|
|
put_char(f, now[2]);
|
|
put_char(f, 0);
|
|
}
|
|
line -= pixmap->stride;
|
|
}
|
|
}
|
|
|
|
static int pixmap_to_c_struct(const Pixmap *pixmap, const char *dest_file, const char *image_name,
|
|
int alpha_convertion)
|
|
{
|
|
int fd;
|
|
FILE *f;
|
|
|
|
if ((fd = open(dest_file, O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) {
|
|
ERROR("open dest file failed");
|
|
return -1;
|
|
}
|
|
|
|
if (!(f = fdopen(fd, "w"))) {
|
|
ERROR("fdopen failed");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
uint32_t data_size = pixmap->width * sizeof(uint32_t) * pixmap->height;
|
|
fprintf(f, "static const struct {\n"
|
|
TAB "uint32_t width;\n"
|
|
TAB "uint32_t height;\n"
|
|
TAB "uint8_t pixel_data[%u];\n"
|
|
"} %s = { %u, %u, {",
|
|
data_size, image_name, pixmap->width, pixmap->height);
|
|
|
|
if (alpha_convertion) {
|
|
if (pixmap->bpp != 32) {
|
|
ERROR("32 bpp is requred for alpha option")
|
|
}
|
|
do_dump_with_alpha_conversion(pixmap, f);
|
|
} else if (pixmap->bpp == 32) {
|
|
do_dump_32bpp(pixmap, f);
|
|
} else {
|
|
do_dump_24bpp(pixmap, f);
|
|
}
|
|
|
|
fseek(f, -1, SEEK_CUR);
|
|
fprintf(f, "}\n};\n");
|
|
fclose(f);
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
enum {
|
|
ALPHA_OPT = 'a',
|
|
HELP_OPT = 'h',
|
|
NAME_OPT = 'n',
|
|
};
|
|
|
|
static void usage()
|
|
{
|
|
printf("usage: %s [--alpha] [--name <struct name>] SOURCE [DEST]\n", prog_name);
|
|
printf(" %s --help\n", prog_name);
|
|
}
|
|
|
|
const struct option longopts[] = {
|
|
{"help", no_argument, NULL, HELP_OPT},
|
|
{"alpha", no_argument, NULL, ALPHA_OPT},
|
|
{"name", required_argument, NULL, NAME_OPT},
|
|
{NULL, 0, NULL, 0},
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
size_t input_size;
|
|
uint8_t* buf;
|
|
Pixmap *pixmap;
|
|
int opt;
|
|
int alpha_convertion = FALSE;
|
|
char *struct_name = NULL;
|
|
char *src = NULL;
|
|
char *dest = NULL;
|
|
|
|
|
|
if (!(prog_name = strrchr(argv[0], '/'))) {
|
|
prog_name = argv[0];
|
|
} else {
|
|
prog_name++;
|
|
}
|
|
|
|
|
|
while ((opt = getopt_long(argc, argv, "ah", longopts, NULL)) != -1) {
|
|
switch (opt) {
|
|
case 0:
|
|
case '?':
|
|
usage();
|
|
exit(-1);
|
|
case ALPHA_OPT:
|
|
alpha_convertion = TRUE;
|
|
break;
|
|
case HELP_OPT:
|
|
usage();
|
|
exit(0);
|
|
case NAME_OPT:
|
|
struct_name = optarg;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int more = argc - optind;
|
|
switch (more) {
|
|
case 1: {
|
|
char *slash;
|
|
char *dot;
|
|
|
|
dest = malloc(strlen(argv[optind]) + 3);
|
|
strcpy(dest, argv[optind]);
|
|
dot = strrchr(dest, '.');
|
|
slash = strrchr(dest, '/');
|
|
if (!dot || (slash && slash > dot)) {
|
|
strcat(dest, ".c");
|
|
} else {
|
|
strcpy(dot, ".c");
|
|
}
|
|
break;
|
|
}
|
|
case 2:
|
|
dest = argv[optind + 1];
|
|
//todo: if dir strcat src name
|
|
break;
|
|
default:
|
|
usage();
|
|
exit(-1);
|
|
}
|
|
|
|
src = argv[optind];
|
|
|
|
if (!struct_name) {
|
|
char *struct_name_src;
|
|
char *dot;
|
|
|
|
struct_name_src = strrchr(dest, '/');
|
|
if (!struct_name_src) {
|
|
struct_name_src = dest;
|
|
} else {
|
|
++struct_name_src;
|
|
}
|
|
struct_name = malloc(strlen(struct_name_src) + 1);
|
|
strcpy(struct_name, struct_name_src);
|
|
if ((dot = strchr(struct_name, '.'))) {
|
|
*dot = 0;
|
|
}
|
|
}
|
|
|
|
if (!(input_size = read_input(src, &buf))) {
|
|
return -1;
|
|
}
|
|
|
|
if (!(pixmap = init_bitmap(input_size, buf))) {
|
|
return -1;
|
|
}
|
|
return pixmap_to_c_struct(pixmap, dest, struct_name, alpha_convertion);
|
|
}
|