/*============================================================================= framebuffer.c =============================================================================== Frame buffer functions Every drawing operation is applied on an internal "frame buffer", which is simply an "image buffer" which represents the picture currently being drawn, along with a "Z-Buffer" which contains the depth values for every pixel in the image buffer. Once all desired drawing operations for a particular picture are effected, a function is provided to print the current contents of the image buffer as a PAM image on standard output. Another function is provided to clear the contents of the frame buffer (i. e. set all image samples and Z-Buffer entries to 0), with the option of only clearing either the image buffer or the Z-Buffer individually. The Z-Buffer works as follows: Every pixel in the image buffer has a corresponding entry in the Z-Buffer. Initially, every entry in the Z-Buffer is set to 0. Every time we desire to plot a pixel at some particular position in the frame buffer, the current value of the corresponding entry in the Z-Buffer is compared against the the Z component of the incoming pixel. If MAX_Z minus the value of the Z component of the incoming pixel is equal to or greater than the current value of the corresponding entry in the Z-Buffer, the frame buffer is changed as follows: 1. All the samples but the last of the corresponding position in the image buffer are set to equal those of the incoming pixel. 2. The last sample, that is, the A-component of the corresponding position in the image buffer is set to equal the maxval. 3. The corresponding entry in the Z-Buffer is set to equal MAX_Z minus the value of the Z component of the incoming pixel. Otherwise, no changes are made on the frame buffer. =============================================================================*/ #include #include #include #include #include #include "utils.h" #include "varying.h" #include "limits_pamtris.h" #include "framebuffer.h" int set_tupletype(const char * const str, char * const tupletype) { /*---------------------------------------------------------------------------- Set the tuple type for the output PAM images given a string ("str") of 255 characters or less. If the string has more than 255 characters, the function returns 0. Otherwise, it returns 1. If NULL is given for the "str" argument, the tuple type is set to a null string. This function is called during program initialization and whenever a "r" command is executed. The second argument must point to the tuple_type member of the "outpam" field in the framebuffer_info struct. -----------------------------------------------------------------------------*/ if (str == NULL) { memset(tupletype, 0, 256); } else { size_t len; len = strlen(str); /* initial value */ if (len > 255) { return 0; } if (len > 0) { memcpy(tupletype, str, len); } tupletype[len--] = '\0'; while(len > 0 && isspace(tupletype[len])) { tupletype[len--] = '\0'; } } return 1; } int init_framebuffer(framebuffer_info * const fbi) { uint8_t const num_planes = fbi->num_attribs + 1; uint32_t const elements = fbi->width * fbi->height; fbi->img.bytes = elements * (num_planes * sizeof(uint16_t)); fbi->z.bytes = elements * sizeof(uint32_t); fbi->img.buffer = calloc(fbi->img.bytes / sizeof(uint16_t), sizeof(uint16_t)); fbi->z.buffer = calloc(fbi->z.bytes / sizeof(uint32_t), sizeof(uint32_t)); if(fbi->img.buffer == NULL || fbi->z.buffer == NULL) { free(fbi->img.buffer); free(fbi->z.buffer); return 0; } fbi->outpam.size = sizeof(struct pam); fbi->outpam.len = sizeof(struct pam); fbi->outpam.file = stdout; fbi->outpam.format = PAM_FORMAT; fbi->outpam.plainformat = 0; fbi->outpam.height = fbi->height; fbi->outpam.width = fbi->width; fbi->outpam.depth = num_planes; fbi->outpam.maxval = fbi->maxval; fbi->outpam.allocation_depth = 0; fbi->outpam.comment_p = NULL; fbi->pamrow = NULL; fbi->pamrow = pnm_allocpamrow(&fbi->outpam); if (fbi->pamrow == NULL) { free(fbi->img.buffer); free(fbi->z.buffer); return 0; } return 1; } void free_framebuffer(framebuffer_info * const fbi) { free(fbi->img.buffer); free(fbi->z.buffer); pnm_freepamrow(fbi->pamrow); } int realloc_image_buffer(int32_t const new_maxval, int32_t const new_num_attribs, framebuffer_info * const fbi) { /*---------------------------------------------------------------------------- Reallocate the image buffer with a new maxval and depth, given the struct with information about the framebuffer. The fields variables "maxval" and "num_attribs". From the point this function is called onwards, new PAM images printed on standard output will have the new maxval for the maxval and num_attribs + 1 for the depth. This function does *not* check whether the new maxval and num_attribs are within the proper allowed limits. That is done inside the input processing function "process_next_command", which is the only function that calls this one. If the function succeeds, the image buffer is left in cleared state. The Z-Buffer, however, is not touched at all. If the new depth is equal to the previous one, no actual reallocation is performed: only the global variable "maxval" is changed. But the image buffer is nonetheless left in cleared state regardless. -----------------------------------------------------------------------------*/ uint8_t num_planes; pnm_freepamrow(fbi->pamrow); fbi->pamrow = NULL; num_planes = fbi->num_attribs + 1; /* initial value */ if (new_num_attribs != fbi->num_attribs) { fbi->num_attribs = new_num_attribs; num_planes = fbi->num_attribs + 1; fbi->img.bytes = fbi->width * fbi->height * (num_planes * sizeof(uint16_t)); { uint16_t * const new_ptr = realloc(fbi->img.buffer, fbi->img.bytes); if (new_ptr == NULL) { free(fbi->img.buffer); fbi->img.buffer = NULL; return 0; } fbi->img.buffer = new_ptr; } } fbi->maxval = new_maxval; fbi->outpam.size = sizeof(struct pam); fbi->outpam.len = sizeof(struct pam); fbi->outpam.file = stdout; fbi->outpam.format = PAM_FORMAT; fbi->outpam.plainformat = 0; fbi->outpam.height = fbi->height; fbi->outpam.width = fbi->width; fbi->outpam.depth = num_planes; fbi->outpam.maxval = fbi->maxval; fbi->outpam.allocation_depth = 0; fbi->outpam.comment_p = NULL; fbi->pamrow = pnm_allocpamrow(&fbi->outpam); if (fbi->pamrow == NULL) { free(fbi->img.buffer); fbi->img.buffer = NULL; return 0; } memset(fbi->img.buffer, 0, fbi->img.bytes); return 1; } void print_framebuffer(framebuffer_info * const fbi) { uint8_t const num_planes = fbi->num_attribs + 1; uint32_t const end = fbi->width * fbi->height; uint32_t i; pnm_writepaminit(&fbi->outpam); for (i = 0; i != end; ) { int j; for (j = 0; j < fbi->width; j++) { uint32_t const k = (i + j) * num_planes; unsigned int l; for (l = 0; l < num_planes; l++) { fbi->pamrow[j][l] = fbi->img.buffer[k + l]; } } pnm_writepamrow(&fbi->outpam, fbi->pamrow); i += fbi->width; } } void clear_framebuffer(bool const clear_image_buffer, bool const clear_z_buffer, framebuffer_info * const fbi) { if (clear_image_buffer) { memset(fbi->img.buffer, 0, fbi->img.bytes); } if (clear_z_buffer) { memset(fbi->z.buffer, 0, fbi->z.bytes); } } void draw_span(uint32_t const base, uint16_t const length, varying * const attribs, framebuffer_info * const fbi) { /*---------------------------------------------------------------------------- Draw a horizontal span of "length" pixels into the frame buffer, performing the appropriate depth tests. "base" must equal the row of the frame buffer where one desires to draw the span *times* the image width, plus the column of the first pixel in the span. This function does not perform any kind of bounds checking. -----------------------------------------------------------------------------*/ static double const depth_range = MAX_Z; uint16_t const maxval = fbi->maxval; uint8_t const z = fbi->num_attribs; uint8_t const w = z + 1; uint8_t const n = w + 1; uint8_t const num_planes = w; unsigned int i; /* Process each pixel in the span: */ for (i = 0; i < length; i++) { int32_t const d = round(depth_range * attribs[z].v); uint32_t const d_mask = geq_mask64(d, fbi->z.buffer[base + i]); uint32_t const j = base + i; uint32_t const k = j * num_planes; varying const inverse_w = inverse_varying(attribs[w]); unsigned int l; /* The following statements will only have any effect if the depth test, performed above, has succeeded. I. e. if the depth test fails, no changes will be made on the frame buffer; otherwise, the frame buffer will be updated with the new values. */ fbi->z.buffer[j] = (fbi->z.buffer[j] & ~d_mask) | (d & d_mask); for (l = 0; l < z; l++) { varying const newval = multiply_varyings(attribs[l], inverse_w); fbi->img.buffer[k + l] = (fbi->img.buffer[k + l] & ~d_mask) | (round_varying(newval) & d_mask); } fbi->img.buffer[k + z] |= (maxval & d_mask); /* Compute the attribute values for the next pixel: */ step_up(attribs, n); } }