scaler: Initial checkin
This commit is contained in:
parent
20ea53892a
commit
0998bc7152
|
@ -0,0 +1,39 @@
|
||||||
|
# Makefile
|
||||||
|
|
||||||
|
TARGET := scaler
|
||||||
|
SRCS := scaler.c
|
||||||
|
OBJS := $(SRCS:.c=.o)
|
||||||
|
|
||||||
|
define lib-paths
|
||||||
|
$(strip \
|
||||||
|
$(shell $(CC) -print-search-dirs | \
|
||||||
|
grep "^libraries" | \
|
||||||
|
sed 's/^libraries *: *=//' | \
|
||||||
|
sed 's/:/ /g') \
|
||||||
|
)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define lib-for
|
||||||
|
$(strip \
|
||||||
|
$(eval _d := $(lastword $(wildcard $(patsubst %,%lib$(1).a,$(call lib-paths))))) \
|
||||||
|
$(if $(_d),$(_d),$(error lib-for $(1) not found)) \
|
||||||
|
)
|
||||||
|
endef
|
||||||
|
|
||||||
|
LIBS := \
|
||||||
|
$(call lib-for,png) \
|
||||||
|
$(call lib-for,z) \
|
||||||
|
$(call lib-for,m)
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJS)
|
||||||
|
$(CC) -static -o $@ $^ $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -Wall -c -o $@ $<
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -f scaler scaler.o
|
|
@ -0,0 +1,293 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include <png.h>
|
||||||
|
|
||||||
|
typedef unsigned char byte;
|
||||||
|
|
||||||
|
static void
|
||||||
|
abort_(const char *s, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, s);
|
||||||
|
vfprintf(stderr, s, args);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
va_end(args);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct image_info {
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
byte color_type;
|
||||||
|
byte bit_depth;
|
||||||
|
byte** data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
image_destroy(struct image_info* info)
|
||||||
|
{
|
||||||
|
uint32_t y;
|
||||||
|
|
||||||
|
for (y = 0; y < info->height; y++) {
|
||||||
|
free(info->data[y]);
|
||||||
|
}
|
||||||
|
free(info->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct image_info
|
||||||
|
read_png_file(const char* file_name)
|
||||||
|
{
|
||||||
|
struct image_info info;
|
||||||
|
FILE* fp;
|
||||||
|
|
||||||
|
png_byte header[8];
|
||||||
|
png_structp png_ptr;
|
||||||
|
png_infop info_ptr;
|
||||||
|
size_t rowbytes;
|
||||||
|
uint32_t y;
|
||||||
|
|
||||||
|
/* open file and test for it being a png */
|
||||||
|
fp = fopen(file_name, "rb");
|
||||||
|
if (!fp) {
|
||||||
|
abort_("[read_png_file] File %s could not be opened for reading", file_name);
|
||||||
|
}
|
||||||
|
fread(header, 1, sizeof(header), fp);
|
||||||
|
if (png_sig_cmp(header, 0, sizeof(header)))
|
||||||
|
{
|
||||||
|
abort_("[read_png_file] File %s is not recognized as a PNG file", file_name);
|
||||||
|
}
|
||||||
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||||
|
if (!png_ptr) {
|
||||||
|
abort_("[read_png_file] png_create_read_struct failed");
|
||||||
|
}
|
||||||
|
info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
if (!info_ptr) {
|
||||||
|
abort_("[read_png_file] png_create_info_struct failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
png_init_io(png_ptr, fp);
|
||||||
|
png_set_sig_bytes(png_ptr, 8);
|
||||||
|
|
||||||
|
png_read_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
info.width = png_get_image_width(png_ptr, info_ptr);
|
||||||
|
info.height = png_get_image_height(png_ptr, info_ptr);
|
||||||
|
info.color_type = png_get_color_type(png_ptr, info_ptr);
|
||||||
|
info.bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
(void)png_set_interlace_handling(png_ptr);
|
||||||
|
png_read_update_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
info.data = (byte**)malloc(sizeof(byte*) * info.height);
|
||||||
|
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
|
||||||
|
for (y = 0; y < info.height; y++) {
|
||||||
|
info.data[y] = (byte*)malloc(rowbytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
png_read_image(png_ptr, info.data);
|
||||||
|
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_png_file(const char* file_name, const struct image_info* info)
|
||||||
|
{
|
||||||
|
FILE* fp;
|
||||||
|
|
||||||
|
png_structp png_ptr;
|
||||||
|
png_infop info_ptr;
|
||||||
|
|
||||||
|
fp = fopen(file_name, "wb");
|
||||||
|
if (!fp) {
|
||||||
|
abort_("[write_png_file] File %s could not be opened for writing", file_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||||
|
if (!png_ptr) {
|
||||||
|
abort_("[write_png_file] png_create_write_struct failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
if (!info_ptr) {
|
||||||
|
abort_("[write_png_file] png_create_info_struct failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
png_init_io(png_ptr, fp);
|
||||||
|
|
||||||
|
png_set_IHDR(png_ptr, info_ptr, info->width, info->height,
|
||||||
|
info->bit_depth, info->color_type, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||||
|
|
||||||
|
png_write_info(png_ptr, info_ptr);
|
||||||
|
png_write_image(png_ptr, info->data);
|
||||||
|
png_write_end(png_ptr, NULL);
|
||||||
|
|
||||||
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
getpixel(const struct image_info* info, uint32_t x, uint32_t y)
|
||||||
|
{
|
||||||
|
uint32_t pixel;
|
||||||
|
uint32_t stride;
|
||||||
|
byte* row = info->data[y];
|
||||||
|
|
||||||
|
switch (info->color_type) {
|
||||||
|
case PNG_COLOR_TYPE_RGB:
|
||||||
|
stride = 3;
|
||||||
|
break;
|
||||||
|
case PNG_COLOR_TYPE_RGBA:
|
||||||
|
stride = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort_("Unknown color type");
|
||||||
|
}
|
||||||
|
|
||||||
|
pixel = ((uint32_t)row[x * stride + 0] << 16) |
|
||||||
|
((uint32_t)row[x * stride + 1] << 8) |
|
||||||
|
((uint32_t)row[x * stride + 2]);
|
||||||
|
|
||||||
|
return pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
putpixel(struct image_info* info, uint32_t x, uint32_t y, uint32_t color)
|
||||||
|
{
|
||||||
|
uint32_t stride;
|
||||||
|
byte* row = info->data[y];
|
||||||
|
|
||||||
|
switch (info->color_type) {
|
||||||
|
case PNG_COLOR_TYPE_RGB:
|
||||||
|
stride = 3;
|
||||||
|
break;
|
||||||
|
case PNG_COLOR_TYPE_RGBA:
|
||||||
|
stride = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort_("Unknown color type");
|
||||||
|
}
|
||||||
|
|
||||||
|
row[x * stride + 0] = (color >> 16) & 0xff;
|
||||||
|
row[x * stride + 1] = (color >> 8) & 0xff;
|
||||||
|
row[x * stride + 2] = (color) & 0xff;
|
||||||
|
if (stride == 4) {
|
||||||
|
row[x * stride + 4] = 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static float
|
||||||
|
lerp(float s, float e, float t)
|
||||||
|
{
|
||||||
|
return s + (e - s) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float
|
||||||
|
blerp(float c00, float c10, float c01, float c11, float tx, float ty)
|
||||||
|
{
|
||||||
|
return lerp(lerp(c00, c10, tx), lerp(c01, c11, tx), ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t
|
||||||
|
getbyte(uint32_t value, int n)
|
||||||
|
{
|
||||||
|
return (value >> (n * 8)) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct image_info
|
||||||
|
scale_image(const struct image_info* src, uint32_t new_w, uint32_t new_h)
|
||||||
|
{
|
||||||
|
png_structp png_ptr;
|
||||||
|
png_infop info_ptr;
|
||||||
|
|
||||||
|
struct image_info scaled;
|
||||||
|
uint32_t x, y;
|
||||||
|
|
||||||
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||||
|
if (!png_ptr) {
|
||||||
|
abort_("[scale_image] png_create_write_struct failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
if (!info_ptr) {
|
||||||
|
abort_("[scale_image] png_create_info_struct failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
png_set_IHDR(png_ptr, info_ptr, new_w, new_h,
|
||||||
|
src->bit_depth, src->color_type, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||||
|
|
||||||
|
scaled.width = new_w;
|
||||||
|
scaled.height = new_h;
|
||||||
|
scaled.color_type = src->color_type;
|
||||||
|
scaled.bit_depth = src->bit_depth;
|
||||||
|
scaled.data = (byte**)malloc(sizeof(byte*) * new_h);
|
||||||
|
for (y = 0; y < new_h; y++) {
|
||||||
|
scaled.data[y] = (byte*)malloc(png_get_rowbytes(png_ptr, info_ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (y = 0; y < new_h; ++y) {
|
||||||
|
for (x = 0; x < new_w; ++x) {
|
||||||
|
float gx = x / (float)new_w * (src->width - 1);
|
||||||
|
float gy = y / (float)new_h * (src->height - 1);
|
||||||
|
int gxi = (int)gx;
|
||||||
|
int gyi = (int)gy;
|
||||||
|
uint32_t result = 0;
|
||||||
|
uint32_t c00 = getpixel(src, gxi, gyi);
|
||||||
|
uint32_t c10 = getpixel(src, gxi + 1, gyi);
|
||||||
|
uint32_t c01 = getpixel(src, gxi, gyi + 1);
|
||||||
|
uint32_t c11 = getpixel(src, gxi + 1, gyi + 1);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 3; ++i) {
|
||||||
|
result |= (uint8_t)blerp(getbyte(c00, i),
|
||||||
|
getbyte(c10, i),
|
||||||
|
getbyte(c01, i),
|
||||||
|
getbyte(c11, i),
|
||||||
|
gx - gxi, gy - gyi) << (8 * i);
|
||||||
|
}
|
||||||
|
putpixel(&scaled, x, y, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||||
|
|
||||||
|
return scaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage(const char* argv0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s WxH <file>\n", argv0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
uint32_t w, h;
|
||||||
|
struct image_info orig;
|
||||||
|
struct image_info scaled;
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
if (sscanf(argv[1], "%ux%u", &w, &h) != 2) {
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
orig = read_png_file(argv[2]);
|
||||||
|
scaled = scale_image(&orig, w, h);
|
||||||
|
write_png_file(argv[2], &scaled);
|
||||||
|
image_destroy(&scaled);
|
||||||
|
image_destroy(&orig);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue