/* * CS5600 University of Utah * Charles McGarvey * mcgarvey@eng.utah.edu */ #include #include #include #include #include "pixmap.h" struct pixmap { color_t* pixels; int w; int h; int left, right, bottom, top; mat_t modelview; mat_t projection; mat_t final; }; pixmap_t* pixmap_alloc(int width, int height, color_t fill) { assert(0 < width && 0 < height && "zero-dimension pixmap not allowed"); size_t size = width * height; pixmap_t* p = (pixmap_t*)mem_alloc(sizeof(pixmap_t)); p->pixels = (color_t*)mem_alloc(sizeof(color_t) * size); p->w = width; p->h = height; p->left = p->bottom = 0; p->right = width; p->top = height; pixmap_clear(p, fill); return p; } void pixmap_destroy(pixmap_t* p) { mem_free(p->pixels); mem_free(p); } void pixmap_clear(pixmap_t* p, color_t fill) { size_t size = p->w * p->h; for (int i = 0; i < size; ++i) { p->pixels[i] = fill; } } static void _pixmap_set_transformation(pixmap_t* p) { /* * Just including a viewport transformation in the final transformation. * This could probably be faster by separating this out. */ mat_t viewport = MAT_VIEWPORT(p->left, p->bottom, p->right - p->left, p->top - p->bottom); p->final = mat_mult(p->projection, p->modelview); p->final = mat_mult(viewport, p->final); } void pixmap_viewport(pixmap_t* p, int x, int y, int width, int height) { p->left = x; p->right = x + width; p->bottom = y; p->top = y + height; _pixmap_set_transformation(p); } void pixmap_modelview(pixmap_t* p, const mat_t* transform) { p->modelview = *transform; _pixmap_set_transformation(p); } void pixmap_projection(pixmap_t* p, const mat_t* transform) { p->projection = *transform; _pixmap_set_transformation(p); } #define _CHECK_WRITE(X) if ((X) <= 0) goto fail int pixmap_export_ppm(const pixmap_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; } _CHECK_WRITE(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); _CHECK_WRITE(fprintf(file, "%hhu %hhu %hhu\n", r, g, b)); } } fclose(file); return 0; } int pixmap_export_bmp(const pixmap_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. */ _CHECK_WRITE(fwrite(&magicNumber, sizeof(magicNumber), 1, file)); _CHECK_WRITE(fwrite(&fileSize, sizeof(fileSize), 1, file)); _CHECK_WRITE(fwrite(&reserved0, sizeof(reserved0), 1, file)); _CHECK_WRITE(fwrite(&reserved1, sizeof(reserved1), 1, file)); _CHECK_WRITE(fwrite(&dataOffset, sizeof(dataOffset), 1, file)); _CHECK_WRITE(fwrite(&infoHeaderSize, sizeof(infoHeaderSize), 1, file)); _CHECK_WRITE(fwrite(&width, sizeof(width), 1, file)); _CHECK_WRITE(fwrite(&height, sizeof(height), 1, file)); _CHECK_WRITE(fwrite(&colorPlanes, sizeof(colorPlanes), 1, file)); _CHECK_WRITE(fwrite(&bitsPerPixel, sizeof(bitsPerPixel), 1, file)); _CHECK_WRITE(fwrite(&compression, sizeof(compression), 1, file)); _CHECK_WRITE(fwrite(&dataSize, sizeof(dataSize), 1, file)); _CHECK_WRITE(fwrite(&horizontalResolution, sizeof(horizontalResolution), 1, file)); _CHECK_WRITE(fwrite(&verticalResolution, sizeof(verticalResolution), 1, file)); _CHECK_WRITE(fwrite(&paletteColorCount, sizeof(paletteColorCount), 1, file)); _CHECK_WRITE(fwrite(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file)); size_t size = width * height; for (int i = 0; i < size; ++i) { rgbachan_t a, r, g, b; color_split(p->pixels[i], &r, &g, &b, &a); uint32_t argb = PACK(argb, 3, a); argb = PACK(argb, 2, r); argb = PACK(argb, 1, g); argb = PACK(argb, 0, b); _CHECK_WRITE(fwrite(&argb, sizeof(argb), 1, file)); } fclose(file); return 0; } void pixmap_draw_tri(pixmap_t* p, const tri_t* triangle) { tri_t t = tri_transform(*triangle, p->final); for (int y = p->bottom; y < p->top; ++y) { for (int x = p->left; x < p->right; ++x) { vec_t pt = vec_new((scal_t)x, (scal_t)y, S(0.0)); vec_t b = tri_barycentric(&t, pt); if (vec_is_barycentric(b)) { color_t* c = p->pixels + y * p->w + x; color_t color = color_new( t.a.c.r * b.x + t.b.c.r * b.y + t.c.c.r * b.z, t.a.c.g * b.x + t.b.c.g * b.y + t.c.c.g * b.z, t.a.c.b * b.x + t.b.c.b * b.y + t.c.c.b * b.z, S(1.0) ); *c = color; } } } }