scaler: Initial checkin

This commit is contained in:
Tom Marshall 2019-09-17 15:04:20 -07:00
parent 20ea53892a
commit 0998bc7152
2 changed files with 332 additions and 0 deletions

39
Makefile Normal file
View File

@ -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

293
scaler.c Normal file
View File

@ -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;
}