From 0998bc7152a52a4b72307c27ca1a430e987b6af1 Mon Sep 17 00:00:00 2001 From: Tom Marshall Date: Tue, 17 Sep 2019 15:04:20 -0700 Subject: [PATCH] scaler: Initial checkin --- Makefile | 39 ++++++++ scaler.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 Makefile create mode 100644 scaler.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5bb9f18 --- /dev/null +++ b/Makefile @@ -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 diff --git a/scaler.c b/scaler.c new file mode 100644 index 0000000..8a86746 --- /dev/null +++ b/scaler.c @@ -0,0 +1,293 @@ +#include +#include +#include +#include +#include +#include + +#include + +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 \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; +}