/* * CS5600 University of Utah * Charles McGarvey * mcgarvey@eng.utah.edu */ #include #include #include #include #include "raster.hh" struct raster { color_t* pixels; int w, h; int left, right, bottom, top; }; raster_t* raster_alloc(int width, int height, color_t fill) { assert(0 < width && 0 < height && "zero-dimension raster not allowed"); size_t size = width * height; raster_t* p = (raster_t*)mem_alloc(sizeof(raster_t)); p->pixels = (color_t*)mem_alloc(sizeof(color_t) * size); p->w = width; p->h = height; raster_clear(p, fill); raster_viewport(p, 0, 0, width, height); return p; } void raster_destroy(raster_t* p) { mem_free(p->pixels); mem_free(p); } color_t* raster_color(const raster_t* p, int x, int y) { return p->pixels + p->w * y + x; } color_t raster_uv(const raster_t* p, vec_t uv) { uv.x = scal_clamp(uv.x, S(0.0), S(1.0)); uv.y = scal_clamp(uv.y, S(0.0), S(1.0)); uv.y = S(1.0) - uv.y; int x = (int)((scal_t)p->w * uv.x); int y = (int)((scal_t)p->h * uv.y); if (p->w <= x) { x = p->w - 1; } if (p->h <= y) { y = p->h - 1; } return *raster_color(p, x, y); } int raster_width(const raster_t* p) { return p->w; } int raster_height(const raster_t* p) { return p->h; } void* raster_data(const raster_t* p) { size_t size = p->w * p->h; rgba_t* data = (rgba_t*)mem_alloc(size * sizeof(rgba_t)); for (int i = 0; i < size; ++i) { data[i] = rgba_from_color(p->pixels[i]); } return data; } void raster_clear(raster_t* p, color_t fill) { size_t size = p->w * p->h; for (int i = 0; i < size; ++i) { p->pixels[i] = fill; } } void raster_viewport(raster_t* p, int x, int y, int width, int height) { p->left = x; p->right = x + width; p->bottom = y; p->top = y + height; } #define _DO_OR_DIE(X) if ((X) <= 0) goto fail int raster_export_ppm(const raster_t* p, const char* filename) { FILE* file = fopen(filename, "w"); if (file == NULL) { fail: fprintf(stderr, "Cannot write to %s: %s\n", filename, strerror(errno)); return -1; } _DO_OR_DIE(fprintf(file, "P3\n%u %u\n255\n", p->w, p->h)); for (int y = (int)p->h - 1; y >= 0; --y) { for (int x = 0; x < p->w; ++x) { rgbachan_t r, g, b; color_split(p->pixels[y * p->w + x], &r, &g, &b, NULL); _DO_OR_DIE(fprintf(file, "%hhu %hhu %hhu\n", r, g, b)); } } fclose(file); return 0; } int raster_export_bmp(const raster_t* p, const char* filename) { /* * This function was adapted from sample code provided with the assignment * instructions. */ FILE* file = fopen(filename, "wb"); if (file == NULL) { fail: fprintf(stderr, "Cannot write to %s: %s\n", filename, strerror(errno)); return -1; } uint16_t magicNumber = 0x4D42; uint16_t reserved0 = 0;//0x4D41; uint16_t reserved1 = 0;//0x5454; uint32_t dataOffset = 54; uint32_t infoHeaderSize = 40; uint32_t width = p->w; uint32_t height = p->h; uint16_t colorPlanes = 1; uint16_t bitsPerPixel = 32; uint32_t compression = 0; uint32_t dataSize = width * height * bitsPerPixel / 8; uint32_t horizontalResolution = 2835; uint32_t verticalResolution = 2835; uint32_t paletteColorCount = 0; uint32_t importantPaletteColorCount = 0; uint32_t fileSize = 54 + dataSize; /* * Check the return values to avoid loud warnings. */ _DO_OR_DIE(fwrite(&magicNumber, sizeof(magicNumber), 1, file)); _DO_OR_DIE(fwrite(&fileSize, sizeof(fileSize), 1, file)); _DO_OR_DIE(fwrite(&reserved0, sizeof(reserved0), 1, file)); _DO_OR_DIE(fwrite(&reserved1, sizeof(reserved1), 1, file)); _DO_OR_DIE(fwrite(&dataOffset, sizeof(dataOffset), 1, file)); _DO_OR_DIE(fwrite(&infoHeaderSize, sizeof(infoHeaderSize), 1, file)); _DO_OR_DIE(fwrite(&width, sizeof(width), 1, file)); _DO_OR_DIE(fwrite(&height, sizeof(height), 1, file)); _DO_OR_DIE(fwrite(&colorPlanes, sizeof(colorPlanes), 1, file)); _DO_OR_DIE(fwrite(&bitsPerPixel, sizeof(bitsPerPixel), 1, file)); _DO_OR_DIE(fwrite(&compression, sizeof(compression), 1, file)); _DO_OR_DIE(fwrite(&dataSize, sizeof(dataSize), 1, file)); _DO_OR_DIE(fwrite(&horizontalResolution, sizeof(horizontalResolution), 1, file)); _DO_OR_DIE(fwrite(&verticalResolution, sizeof(verticalResolution), 1, file)); _DO_OR_DIE(fwrite(&paletteColorCount, sizeof(paletteColorCount), 1, file)); _DO_OR_DIE(fwrite(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file)); size_t size = width * height; for (int i = 0; i < size; ++i) { struct { rgbachan_t b, g, r, a; } argb; color_split(p->pixels[i], &argb.r, &argb.g, &argb.b, &argb.a); _DO_OR_DIE(fwrite(&argb, sizeof(argb), 1, file)); } fclose(file); return 0; } raster_t* raster_import(const char* filename) { int type = 0; const char* ext = strrchr(filename, '.'); if (ext == NULL) { goto fail; } ++ext; if (strcmp(ext, "bmp") == 0) { return raster_import_bmp(filename); } if (strcmp(ext, "ppm") == 0) { return raster_import_ppm(filename); } fail: fprintf(stderr, "Unknown file type: %s", filename); return NULL; } raster_t* raster_import_ppm(const char* filename) { FILE* file = fopen(filename, "r"); if (file == NULL) { fprintf(stderr, "Cannot read from %s: %s\n", filename, strerror(errno)); return NULL; } int w, h; if (fscanf(file, "P3 %d %d 255 ", &w, &h) != 2) { fprintf(stderr, "Cannot read header from %s: %s\n", filename, strerror(errno)); return NULL; } raster_t* p = raster_alloc(w, h, COLOR_WHITE); for (int y = h - 1; y >= 0; --y) { for (int x = 0; x < w; ++x) { uint16_t r, g, b; /* mingw32 does not like %hhu conversion type */ if (fscanf(file, "%hu %hu %hu ", &r, &g, &b) != 3) { fprintf(stderr, "Failed reading color values from %s: %s\n", filename, strerror(errno)); return NULL; } union { rgba_t rgba; struct { rgbachan_t r, g, b, a; } chan; } u; u.chan.r = (rgbachan_t)r; u.chan.g = (rgbachan_t)g; u.chan.b = (rgbachan_t)b; u.chan.a = 255; p->pixels[y * w + x] = color_from_rgba(u.rgba); } } fclose(file); return p; fail: raster_destroy(p); fclose(file); fprintf(stderr, "Unexpected file format in %s: %s\n", filename, strerror(errno)); return NULL; } raster_t* raster_import_bmp(const char* filename) { FILE* file = fopen(filename, "rb"); if (file == NULL) { fprintf(stderr, "Cannot read from %s: %s\n", filename, strerror(errno)); return NULL; } size_t size; uint16_t magicNumber; uint16_t reserved0;//0x4D41; uint16_t reserved1;//0x5454; uint32_t dataOffset; uint32_t infoHeaderSize; uint32_t width; uint32_t height; uint16_t colorPlanes; uint16_t bitsPerPixel; uint32_t compression; uint32_t dataSize; uint32_t horizontalResolution; uint32_t verticalResolution; uint32_t paletteColorCount; uint32_t importantPaletteColorCount; uint32_t fileSize; raster_t* p = NULL; _DO_OR_DIE(fread(&magicNumber, sizeof(magicNumber), 1, file)); _DO_OR_DIE(fread(&fileSize, sizeof(fileSize), 1, file)); _DO_OR_DIE(fread(&reserved0, sizeof(reserved0), 1, file)); _DO_OR_DIE(fread(&reserved1, sizeof(reserved1), 1, file)); _DO_OR_DIE(fread(&dataOffset, sizeof(dataOffset), 1, file)); _DO_OR_DIE(fread(&infoHeaderSize, sizeof(infoHeaderSize), 1, file)); _DO_OR_DIE(fread(&width, sizeof(width), 1, file)); _DO_OR_DIE(fread(&height, sizeof(height), 1, file)); _DO_OR_DIE(fread(&colorPlanes, sizeof(colorPlanes), 1, file)); _DO_OR_DIE(fread(&bitsPerPixel, sizeof(bitsPerPixel), 1, file)); _DO_OR_DIE(fread(&compression, sizeof(compression), 1, file)); _DO_OR_DIE(fread(&dataSize, sizeof(dataSize), 1, file)); _DO_OR_DIE(fread(&horizontalResolution, sizeof(horizontalResolution), 1, file)); _DO_OR_DIE(fread(&verticalResolution, sizeof(verticalResolution), 1, file)); _DO_OR_DIE(fread(&paletteColorCount, sizeof(paletteColorCount), 1, file)); _DO_OR_DIE(fread(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file)); if ((int)width < 0 || (int)height < 0) { fprintf(stderr, "Unexpected file format in %s: Try a ppm file instead\n", filename); fclose(file); return NULL; } p = raster_alloc((int)width, (int)height, COLOR_WHITE); size = width * height; for (int i = 0; i < size; ++i) { union { rgba_t rgba; struct { rgbachan_t r, g, b, a; } chan; } u; _DO_OR_DIE(fread(&u, sizeof(u), 1, file)); rgbachan_t t = u.chan.r; u.chan.r = u.chan.a; u.chan.a = t; t = u.chan.g; u.chan.g = u.chan.b; u.chan.b = t; t = u.chan.r; u.chan.r = u.chan.g; u.chan.g = u.chan.b; u.chan.b = u.chan.a; u.chan.a = t; p->pixels[i] = color_from_rgba(u.rgba); } fclose(file); return p; fail: if (p) { raster_destroy(p); } fclose(file); fprintf(stderr, "Unexpected file format in %s: %s\n", filename, strerror(errno)); return NULL; } #undef _DO_OR_DIE