libretro-prboom/data/rd_graphic.c
2015-08-27 07:36:16 +02:00

165 lines
4.0 KiB
C

// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS)
// Licence: GPLv2 or later (see COPYING)
// Convert portable pixmap to Doom patch format
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "rd_util.h"
#include "rd_palette.h"
#include "rd_graphic.h"
//
// parseppm
//
// try to read a ppm file header and get width/height of image
//
static unsigned char *parseppm(char *ppm, size_t size, const char *file,
int *width, int *height)
{
int maxcol, numpixels;
char *pos = ppm;
if (size < 2 || !(*pos++ == 'P' && *pos++ == '6'))
die("Not a PPM: %s\n", file);
numpixels = *width = strtol(pos, &pos, 0);
numpixels *= *height = strtol(pos, &pos, 0);
maxcol = strtol(pos, &pos, 0);
if (maxcol != 255 || !isspace(*pos++))
die("Invalid PPM header: %s\n", file);
if (!numpixels || numpixels > (size-(ppm-pos))/3)
die("Invalid PPM size: %s\n", file);
return (unsigned char *)pos;
}
//
// column_pixels_to_colours
//
// make an array of colour indices from a column of raw pixel data
// (random access to which is easier while building a patch column)
//
static void pixels_to_colours(int *colours, unsigned char *pixels,
int width, int height, int x)
{
int i = height;
int *colour = colours;
unsigned char *rgb = &pixels[3*x];
while (--i>=0)
{
*colour = palette_getindex(rgb);
colour++;
rgb += 3*width;
}
}
//
// createcolumn
//
// make a doom patch column from an array of colour indices
//
static size_t createcolumn(unsigned char **column, int *colours, int height)
{
size_t size = 256, length = 0;
unsigned char *data = xmalloc(size);
int i, y, top, transparent, opaque;
for (y = 0; y < height; )
{
// we are at the start of a new post
top = y;
// count transparent pixels above post and opaque pixels in it
transparent = opaque = 0;
for (; y < height && colours[y] == -1; y++) transparent++;
for (; y < height && colours[y] >= 0; y++) opaque++;
if (opaque > 0) // this post has pixels
{
while (size < length + opaque + 8)
size *= 2, data = xrealloc(data, size);
data[length++] = top + transparent; // column offset
data[length++] = opaque; // length of column
data[length++] = 0; // padding
for (i = 0; i < opaque; i++)
data[length++] = colours[top + transparent + i];
data[length++] = 0; // more padding
}
}
data[length++] = 0xff; // end of column
*column = data;
return length;
}
//
// ppm_to_patch
//
// convert an 8-bit portable pixmap to doom's patch format
// insert_x and insert_y are the graphic/sprite insertion point
//
size_t ppm_to_patch(void **lumpdata, const char *filename,
int insert_x, int insert_y)
{
void *data;
size_t size = read_or_die(&data, filename);
int i, width, height, *column_colours;
unsigned char *pixels, **columns, *patch;
size_t *columnsizes, totalcolumnsize, offset;
pixels = parseppm(data, size, filename, &width, &height);
columns = xmalloc(width * sizeof(*columns));
columnsizes = xmalloc(width * sizeof(*columnsizes));
column_colours = xmalloc(height * sizeof(*column_colours));
for (totalcolumnsize = i = 0; i < width; i++)
{
pixels_to_colours(column_colours, pixels, width, height, i);
columnsizes[i] = createcolumn(&columns[i], column_colours, height);
totalcolumnsize += columnsizes[i];
}
patch = xmalloc(8+4*width+totalcolumnsize);
((short *)patch)[0] = SHORT(width);
((short *)patch)[1] = SHORT(height);
((short *)patch)[2] = SHORT(insert_x);
((short *)patch)[3] = SHORT(insert_y);
for (offset = 8+4*width, i = 0; i < width; i++)
{
((int *)(patch+8))[i] = LONG(offset);
offset += columnsizes[i];
}
for (offset = 8+4*width, i = 0; i < width; i++)
{
memmove(patch + offset, columns[i], columnsizes[i]);
offset += columnsizes[i];
free(columns[i]);
}
free(column_colours);
free(columnsizes);
free(columns);
free(data);
*lumpdata = patch;
return 8+4*width+totalcolumnsize;
}