diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2018-09-29 03:18:57 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2018-09-29 03:18:57 +0000 |
commit | e227331a6b5626e100705fcff3269fd8479ea988 (patch) | |
tree | fd51fff8ec8dc90185ce5ae457805a0f25a0ded6 /generator | |
parent | cdd64d00181d5e5a6c8142cdb59f2215014ca82d (diff) | |
download | netpbm-mirror-e227331a6b5626e100705fcff3269fd8479ea988.tar.gz netpbm-mirror-e227331a6b5626e100705fcff3269fd8479ea988.tar.xz netpbm-mirror-e227331a6b5626e100705fcff3269fd8479ea988.zip |
Promote current Development release as Advanced
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@3352 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'generator')
-rw-r--r-- | generator/Makefile | 4 | ||||
-rw-r--r-- | generator/pamtris/Makefile | 27 | ||||
-rw-r--r-- | generator/pamtris/boundaries.c | 334 | ||||
-rw-r--r-- | generator/pamtris/boundaries.h | 71 | ||||
-rw-r--r-- | generator/pamtris/fract.h | 54 | ||||
-rw-r--r-- | generator/pamtris/framebuffer.c | 334 | ||||
-rw-r--r-- | generator/pamtris/framebuffer.h | 75 | ||||
-rw-r--r-- | generator/pamtris/input.c | 641 | ||||
-rw-r--r-- | generator/pamtris/input.h | 31 | ||||
-rw-r--r-- | generator/pamtris/limits_pamtris.h | 7 | ||||
-rw-r--r-- | generator/pamtris/pamtris.c | 140 | ||||
-rw-r--r-- | generator/pamtris/triangle.c | 427 | ||||
-rw-r--r-- | generator/pamtris/triangle.h | 25 | ||||
-rw-r--r-- | generator/pamtris/utils.c | 256 | ||||
-rw-r--r-- | generator/pamtris/utils.h | 41 | ||||
-rw-r--r-- | generator/pbmtext.c | 85 | ||||
-rw-r--r-- | generator/ppmcie.c | 38 |
17 files changed, 2508 insertions, 82 deletions
diff --git a/generator/Makefile b/generator/Makefile index 5120008c..d54a6cc5 100644 --- a/generator/Makefile +++ b/generator/Makefile @@ -7,6 +7,8 @@ VPATH=.:$(SRCDIR)/$(SUBDIR) include $(BUILDDIR)/config.mk +SUBDIRS = pamtris + # We tend to separate out the build targets so that we don't have # any more dependencies for a given target than it really needs. # That way, if there is a problem with a dependency, we can still @@ -39,6 +41,6 @@ OBJECTS = $(BINARIES:%=%.o) MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2) .PHONY: all -all: $(BINARIES) +all: $(BINARIES) $(SUBDIRS:%=%/all) include $(SRCDIR)/common.mk diff --git a/generator/pamtris/Makefile b/generator/pamtris/Makefile new file mode 100644 index 00000000..d27606e3 --- /dev/null +++ b/generator/pamtris/Makefile @@ -0,0 +1,27 @@ +ifeq ($(SRCDIR)x,x) + SRCDIR = $(CURDIR)/../.. + BUILDDIR = $(SRCDIR) +endif +SUBDIR = generator/pamtris +VPATH=.:$(SRCDIR)/$(SUBDIR) + +include $(BUILDDIR)/config.mk + +PORTBINARIES = pamtris + +MERGEBINARIES = $(PORTBINARIES) + +BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES) + +ADDL_OBJECTS = boundaries.o framebuffer.o input.o triangle.o utils.o + +OBJECTS = pamtris.o $(ADDL_OBJECTS) + +MERGE_OBJECTS = pamtris.o2 $(ADDL_OBJECTS) + +.PHONY: all +all: $(BINARIES) + +pamtris:%:%.o $(ADDL_OBJECTS) + +include $(SRCDIR)/common.mk diff --git a/generator/pamtris/boundaries.c b/generator/pamtris/boundaries.c new file mode 100644 index 00000000..8ea28682 --- /dev/null +++ b/generator/pamtris/boundaries.c @@ -0,0 +1,334 @@ +/*============================================================================= + boundaries.c +=============================================================================== + Boundary buffer functions + + New triangles are drawn one scanline at a time, and for every such scanline + we have left and right boundary columns within the frame buffer such that + the fraction of the triangle's area within that scanline is enclosed + between those two points (inclusive). Those coordinates may correspond to + columns outside the frame buffer's actual limits, in which case proper + post-processing should be made wherever such coordinates are used to + actually plot anything into the frame buffer. +=============================================================================*/ + +#include <stdlib.h> + +#include <netpbm/mallocvar.h> +#include <netpbm/pm.h> + +#include "utils.h" +#include "fract.h" + + +#include "boundaries.h" + + + +static fract +make_pos_fract(int32_t const quotient, + int32_t const remainder) { + + fract retval; + + retval.q = quotient; + retval.r = remainder; + retval.negative_flag = 0; + + return retval; +} + + + +void +init_boundary_buffer(boundary_info * const bi, + int16_t const height) { + + MALLOCARRAY(bi->buffer, height * 2); + + if (!bi->buffer) + pm_error("Unable to get memory for %u-row high boundary buffer", + height); +} + + + +void +free_boundary_buffer(boundary_info * bi) { + free(bi->buffer); +} + + + +bool +gen_triangle_boundaries(Xy const xy, + boundary_info * const bi, + int16_t const width, + int16_t const height) { +/*---------------------------------------------------------------------------- + Generate an entry in the boundary buffer for the boundaries of every + VISIBLE row of a particular triangle. In case there is no such row, + start_row is accordingly set to -1. The argument is a 3-element array + of pairs of int16_t's representing the coordinates of the vertices of + a triangle. Those vertices MUST be already sorted in order from the + uppermost to the lowermost vertex (which is what draw_triangle, the + only function which uses this one, does with the help of sort3). + + The return value indicates whether the middle vertex is to the left of the + line connecting the top vertex to the bottom vertex or not. +-----------------------------------------------------------------------------*/ + int16_t leftmost_x; + int16_t rightmost_x; + int mid_is_to_the_left; + fract left_x; + fract right_x; + bool no_upper_part; + int32_t top2mid_delta; + int32_t top2bot_delta; + int32_t mid2bot_delta; + fract top2mid_step; + fract top2bot_step; + fract mid2bot_step; + fract* upper_left_step; + fract* lower_left_step; + fract* upper_right_step; + fract* lower_right_step; + int32_t upper_left_delta; + int32_t lower_left_delta; + int32_t upper_right_delta; + int32_t lower_right_delta; + fract* left_step[2]; + fract* right_step[2]; + int32_t left_delta[2]; + int32_t right_delta[2]; + int16_t* num_rows_ptr[2]; + int32_t y; + int32_t i; + uint8_t k; + + leftmost_x = xy._[0][0]; /* initial value */ + rightmost_x = xy._[0][0]; /* initial value */ + + bi->start_scanline = -1; + bi->num_upper_rows = 0; + bi->num_lower_rows = 0; + + if (xy._[2][1] < 0 || xy._[0][1] >= height) { + /* Triangle is either completely above the topmost scanline or + completely below the bottom scanline. + */ + + return false; /* Actual value doesn't matter. */ + } + + { + unsigned int i; + + for (i = 1; i < 3; i++) { + if (xy._[i][0] < leftmost_x) { + leftmost_x = xy._[i][0]; + } + + if (xy._[i][0] > rightmost_x) { + rightmost_x = xy._[i][0]; + } + } + } + if (rightmost_x < 0 || leftmost_x >= width) { + /* Triangle is either completely to the left of the leftmost + framebuffer column or completely to the right of the rightmost + framebuffer column. + */ + return false; /* Actual value doesn't matter. */ + } + + if (xy._[0][1] == xy._[1][1] && xy._[1][1] == xy._[2][1]) { + /* Triangle is degenarate: its visual representation consists only of + a horizontal straight line. + */ + + bi->start_scanline = xy._[0][1]; + + return false; /* Actual value doesn't matter. */ + } + + mid_is_to_the_left = 2; + + left_x = make_pos_fract(xy._[0][0], 0); + right_x = make_pos_fract(xy._[0][0], 0); + + if (xy._[0][1] == xy._[1][1]) { + /* Triangle has only a lower part. */ + + mid_is_to_the_left = 0; + + right_x.q = xy._[1][0]; + } else if (xy._[1][1] == xy._[2][1]) { + /* Triangle has only an upper part (plus the row of the middle + vertex). + */ + + mid_is_to_the_left = 1; + } + + no_upper_part = (xy._[1][1] == xy._[0][1]); + + top2mid_delta = xy._[1][1] - xy._[0][1] + !no_upper_part; + top2bot_delta = xy._[2][1] - xy._[0][1] + 1; + mid2bot_delta = xy._[2][1] - xy._[1][1] + no_upper_part; + + gen_steps(&xy._[0][0], &xy._[1][0], &top2mid_step, 1, top2mid_delta); + gen_steps(&xy._[0][0], &xy._[2][0], &top2bot_step, 1, top2bot_delta); + gen_steps(&xy._[1][0], &xy._[2][0], &mid2bot_step, 1, mid2bot_delta); + + if (mid_is_to_the_left == 2) { + if (top2bot_step.negative_flag) { + if (top2mid_step.negative_flag) { + if (top2mid_step.q == top2bot_step.q) { + mid_is_to_the_left = + top2mid_step.r * top2bot_delta > + top2bot_step.r * top2mid_delta; + } else { + mid_is_to_the_left = top2mid_step.q < top2bot_step.q; + } + } else { + mid_is_to_the_left = 0; + } + } else { + if (!top2mid_step.negative_flag) { + if (top2mid_step.q == top2bot_step.q) { + mid_is_to_the_left = + top2mid_step.r * top2bot_delta < + top2bot_step.r * top2mid_delta; + } else { + mid_is_to_the_left = top2mid_step.q < top2bot_step.q; + } + } else { + mid_is_to_the_left = 1; + } + } + } + if (mid_is_to_the_left) { + upper_left_step = &top2mid_step; + lower_left_step = &mid2bot_step; + upper_right_step = &top2bot_step; + lower_right_step = upper_right_step; + + upper_left_delta = top2mid_delta; + lower_left_delta = mid2bot_delta; + upper_right_delta = top2bot_delta; + lower_right_delta = upper_right_delta; + } else { + upper_right_step = &top2mid_step; + lower_right_step = &mid2bot_step; + upper_left_step = &top2bot_step; + lower_left_step = upper_left_step; + + upper_right_delta = top2mid_delta; + lower_right_delta = mid2bot_delta; + upper_left_delta = top2bot_delta; + lower_left_delta = upper_left_delta; + } + + left_step[0] = upper_left_step; + left_step[1] = lower_left_step; + right_step[0] = upper_right_step; + right_step[1] = lower_right_step; + left_delta[0] = upper_left_delta; + left_delta[1] = lower_left_delta; + right_delta[0] = upper_right_delta; + right_delta[1] = lower_right_delta; + num_rows_ptr[0] = &bi->num_upper_rows; + num_rows_ptr[1] = &bi->num_lower_rows; + + y = xy._[0][1]; + + i = 0; + k = 0; + + if (no_upper_part) { + k = 1; + + right_x.q = xy._[1][0]; + } + + step_up(&left_x, left_step[k], 1, left_delta[k]); + step_up(&right_x, right_step[k], 1, right_delta[k]); + + while (k < 2) { + int32_t end; + + end = xy._[k + 1][1] + k; /* initial value */ + + if (y < 0) { + int32_t delta; + + if (end > 0) { + delta = -y; + } else { + delta = xy._[k + 1][1] - y; + } + + y += delta; + + multi_step_up(&left_x, left_step[k], 1, delta, left_delta[k]); + multi_step_up(&right_x, right_step[k], 1, delta, right_delta[k]); + + if (y < 0) { + k++; + continue; + } + } else if(y >= height) { + return mid_is_to_the_left; + } + + if (end > height) { + end = height; + } + + while (y < end) { + if (left_x.q >= width || right_x.q < 0) { + if (bi->start_scanline > -1) { + return mid_is_to_the_left; + } + } else { + if (bi->start_scanline == -1) { + bi->start_scanline = y; + } + + bi->buffer[i++] = left_x.q; + bi->buffer[i++] = right_x.q; + + (*(num_rows_ptr[k]))++; + } + + step_up(&left_x, left_step[k], 1, left_delta[k]); + step_up(&right_x, right_step[k], 1, right_delta[k]); + + y++; + } + k++; + } + return mid_is_to_the_left; +} + + + +void +get_triangle_boundaries(uint16_t const row_index, + int32_t * const left, + int32_t * const right, + const boundary_info * const bi) { +/*---------------------------------------------------------------------------- + Return the left and right boundaries for a given VISIBLE triangle row (the + row index is relative to the first visible row). These values may be out of + the horizontal limits of the frame buffer, which is necessary in order to + compute correct attribute interpolations. +-----------------------------------------------------------------------------*/ + uint32_t const i = row_index << 1; + + *left = bi->buffer[i]; + *right = bi->buffer[i + 1]; +} + + diff --git a/generator/pamtris/boundaries.h b/generator/pamtris/boundaries.h new file mode 100644 index 00000000..d41719cb --- /dev/null +++ b/generator/pamtris/boundaries.h @@ -0,0 +1,71 @@ +#ifndef BOUNDARIES_H_INCLUDED +#define BOUNDARIES_H_INCLUDED + +#include <stdint.h> + +#include "triangle.h" + +typedef struct boundary_info { +/*---------------------------------------------------------------------------- + Information about visible triangle rows' boundaries. Also see the + "boundary buffer functions" below. + + A "visible" triangle row is one which: + + 1. Corresponds to a frame buffer row whose index (from top to bottom) is + equal to or greater than 0 and smaller than the image height; and + + 2. Has at least some of its pixels between the frame buffer columns whose + index (from left to right) is equal to or greater than 0 and smaller + than the image width. +-----------------------------------------------------------------------------*/ + int16_t start_scanline; + /* Index of the frame buffer scanline which contains the first visible + row of the current triangle, if there is any such row. If not, it + contains the value -1. + */ + + int16_t num_upper_rows; + /* The number of visible rows in the upper part of the triangle. The + upper part of a triangle is composed of all the rows starting from + the top vertex down to the middle vertex, but not including this + last one. + */ + + int16_t num_lower_rows; + /* The number of visible rows in the lower part of the triangle. The + lower part of a triangle is composed of all the rows from the + middle vertex to the bottom vertex -- all inclusive. + */ + + int16_t * buffer; + /* This is the "boundary buffer": a pointer to an array of int16_t's + where each consecutive pair of values indicates, in this order, the + columns of the left and right boundary pixels for a particular + visible triangle row. Those boundaries are inclusive on both sides + and may be outside the limits of the frame buffer. This field is + initialized and freed by the functions "init_boundary_buffer" and + "free_boundary_buffer", respectively. + */ +} boundary_info; + +void +init_boundary_buffer(boundary_info * const bdi, + int16_t const height); + +void +free_boundary_buffer(boundary_info *); + +bool +gen_triangle_boundaries(Xy const xy, + boundary_info * const bdi, + int16_t const width, + int16_t const height); + +void +get_triangle_boundaries(uint16_t const row_index, + int32_t * const left, + int32_t * const right, + const boundary_info * const bdi); + +#endif diff --git a/generator/pamtris/fract.h b/generator/pamtris/fract.h new file mode 100644 index 00000000..ff3c4402 --- /dev/null +++ b/generator/pamtris/fract.h @@ -0,0 +1,54 @@ +#ifndef FRACT_H_INCLUDED +#define FRACT_H_INCLUDED + +#include <stdbool.h> +#include <stdint.h> + + +typedef struct { +/*---------------------------------------------------------------------------- + This struct and the functions that manipulate variables of this type act + as a substitute for floating point computations. Here, whenever we need a + value with a fractional component, we represent it using two parts: 1. An + integer part, called the "quotient", and 2. A fractional part, which is + itself composed of a "remainder" (or "numerator") and a "divisor" (or + "denominator"). The fract struct provides storage for the quotient and the + remainder, but the divisor must be given separately (because it often + happens in this program that whenever we are dealing with one variable of + type fract, we are dealing with more of them at the same time, and they + all have the same divisor). + + To be more precise, the way we actually use variables of this type works + like this: We read integer values through standard input; When drawing + triangles, we need need to calculate differences between some pairs of + these input values and divide such differences by some other integer, + which is the above mentioned divisor. That result is then used to compute + successive interpolations between the two values for which we had + originally calculated the difference, and is therefore called the + "interpolation step". The values between which we wish to take successive + interpolations are called the "initial value" and the "final value". The + interpolation procedure works like this: First, we transform the initial + value into a fract variable by equating the quotient of that variable to + the initial value and assigning 0 to its remainder. Then, we successivelly + apply the interpolation step to that variable through successive calls to + step_up and/or multi_step_up until the quotient of the variable equals the + final value. Each application of step_up or multi_step_up yields a + particular linear interpolation between the initial and final values. + + If and only if a particular fract variable represents an interpolation + step, the "negative_flag" field indicates whether the step is negative + (i. e. negative_flag == true) or not (negative_flag == false). This is + necessary in order to make sure that variables are "stepped up" in the + appropriate direction, so to speak, as the field which stores the + remainder in any fract variable, "r", is always equal to or above 0, and + the quotient of a step may be 0, so the actual sign of the step value is + not always discoverable through a simple examination of the sign of the + quotient. On the other hand, if the variable does not represent an + interpolation step, the negative_flag is meaningless. +-----------------------------------------------------------------------------*/ + int32_t q; /* Quotient */ + int32_t r: 31; /* Remainder */ + bool negative_flag: 1; +} fract; + +#endif diff --git a/generator/pamtris/framebuffer.c b/generator/pamtris/framebuffer.c new file mode 100644 index 00000000..03cd720c --- /dev/null +++ b/generator/pamtris/framebuffer.c @@ -0,0 +1,334 @@ +/*============================================================================= + 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 <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "utils.h" +#include "fract.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); + + return 1; + } 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 suceeds, 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, + fract * const attribs_start, + const fract * const attribs_steps, + int32_t const div, + 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. +-----------------------------------------------------------------------------*/ + uint8_t const num_planes = fbi->num_attribs + 1; + + unsigned int i; + + /* Process each pixel in the span: */ + + for (i = 0; i < length; i++) { + int32_t const z = MAX_Z - attribs_start[fbi->num_attribs].q; + uint32_t const z_mask = -(~(z - fbi->z.buffer[base + i]) >> 31); + uint32_t const n_z_mask = ~z_mask; + + uint32_t const j = base + i; + uint32_t const k = j * num_planes; + + unsigned int l; + + /* The following statements will only have any effect if the depth + test, performed above, has suceeded. I. e. if the depth test fails, + no changes will be made on the framebuffer; otherwise, the + framebuffer will be updated with the new values. + */ + fbi->z.buffer[j] = (fbi->z.buffer[j] & n_z_mask) | (z & z_mask); + + for (l = 0; l < fbi->num_attribs; l++) { + fbi->img.buffer[k + l] = + (fbi->img.buffer[k + l] & n_z_mask) | + (attribs_start[l].q & z_mask); + } + + fbi->img.buffer[k + fbi->num_attribs] = + (fbi->img.buffer[k + fbi->num_attribs] & n_z_mask) | + (fbi->maxval & z_mask); + + /* Compute the attribute values for the next pixel: */ + + step_up(attribs_start, attribs_steps, num_planes, div); + } +} + + + diff --git a/generator/pamtris/framebuffer.h b/generator/pamtris/framebuffer.h new file mode 100644 index 00000000..73ec96be --- /dev/null +++ b/generator/pamtris/framebuffer.h @@ -0,0 +1,75 @@ +#ifndef FRAMEBUFFER_H_INCLUDED +#define FRAMEBUFFER_H_INCLUDED + +#include <stdint.h> +#include <stdbool.h> +#include "fract.h" +#include "netpbm/pam.h" + +typedef struct framebuffer_info { +/*---------------------------------------------------------------------------- + Information about the frame buffer and PAM output +-----------------------------------------------------------------------------*/ + /* These fields are initialized once by reading the command line + arguments. "maxval" and "num_attribs" may be modified later + through "realloc_image_buffer". + */ + int32_t width; + int32_t height; + int32_t maxval; + int32_t num_attribs; + + /* The fields below must be initialized by "init_framebuffer" and + freed by "free_framebuffer", except for the tuple_type field in + "outpam" which is initialized once by reading the command line + arguments and may be modified later through "set_tupletype". + */ + struct { + uint16_t * buffer; + uint32_t bytes; + } img; /* Image buffer */ + + struct { + uint32_t * buffer; + uint32_t bytes; + } z; /* Z-buffer */ + + struct pam outpam; + + tuple * pamrow; +} framebuffer_info; + + + +int +set_tupletype(const char * const str, + char * const tupletype); + +int +init_framebuffer(framebuffer_info * const fbi); + +void +free_framebuffer(framebuffer_info * const fbi); + +void +print_framebuffer(framebuffer_info * const fbi); + +void +clear_framebuffer(bool const clear_image_buffer, + bool const clear_z_buffer, + framebuffer_info * const fbi); + +int +realloc_image_buffer(int32_t const new_maxval, + int32_t const new_num_attribs, + framebuffer_info * const fbi); + +void +draw_span(uint32_t const base, + uint16_t const length, + fract * const attribs_start, + const fract * const attribs_steps, + int32_t const divisor, + framebuffer_info * const fbi); + +#endif diff --git a/generator/pamtris/input.c b/generator/pamtris/input.c new file mode 100644 index 00000000..166d6db5 --- /dev/null +++ b/generator/pamtris/input.c @@ -0,0 +1,641 @@ +/*============================================================================= + input.c +=============================================================================== + Input handling functions +=============================================================================*/ +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> + +#include "netpbm/mallocvar.h" + +#include "limits_pamtris.h" +#include "framebuffer.h" +#include "triangle.h" + +#include "input.h" + +#define MAX_COORD 32767 +#define MIN_COORD -MAX_COORD + +#define DRAW_MODE_TRIANGLES 1 +#define DRAW_MODE_STRIP 2 +#define DRAW_MODE_FAN 3 + +#define CMD_SET_MODE "mode" +#define CMD_SET_ATTRIBS "attribs" +#define CMD_VERTEX "vertex" +#define CMD_PRINT "print" +#define CMD_CLEAR "clear" +#define CMD_RESET "reset" +#define CMD_QUIT "quit" + +#define ARG_TRIANGLES "triangles" +#define ARG_STRIP "strip" +#define ARG_FAN "fan" +#define ARG_IMAGE "image" +#define ARG_DEPTH "depth" + +#define WARNING_EXCESS_ARGS "warning: ignoring excess arguments: line %lu." +#define SYNTAX_ERROR "syntax error: line %lu." + +typedef struct { + Xy v_xy; + /* X- and Y-coordinates of the vertices for the current triangle. + int32_t v_attribs[3][MAX_NUM_ATTRIBS + 1]; // Vertex attributes for + the current triangle. Includes the Z-coordinates. + */ + Attribs v_attribs; + /* Vertex attributes for the current triangle. Includes the + Z-coordinates. + */ + int32_t curr_attribs[MAX_NUM_ATTRIBS]; + /* Attributes that will be assigned to the next vertex. Does not + include the Z-coordinate. + */ + uint8_t next; + /* Index of the next vertex to be read. */ + bool draw; + /* If true, draws a new triangle upon reading a new vertex. */ + + uint8_t mode; + /* Drawing mode. */ + + bool initialized; +} state_info; + + + +static void +clear_attribs(state_info * const si, + int32_t const maxval, + int16_t const num_attribs) { + + unsigned int i; + + for (i = 0; i < num_attribs; i++) { + si->curr_attribs[i] = maxval; + } +} + + + +void +init_input_processor(input_info * const ii) { + + MALLOCARRAY_NOFAIL(ii->buffer, 128); + + ii->length = 128; + ii->number = 1; +} + + + +void +free_input_processor(input_info * const ii) { + free(ii->buffer); +} + + + +typedef struct { +/*---------------------------------------------------------------------------- + Indicates a whitespace-delimited input symbol. "begin" points to its first + character, and "end" points to one position past its last character. +-----------------------------------------------------------------------------*/ + char * begin; + char * end; +} token; + + + +static token +next_token(char * const startPos) { + + token retval; + char * p; + + for (p = startPos; *p && isspace(*p); ++p); + + retval.begin = p; + + for (; *p && !isspace(*p); ++p); + + retval.end = p; + + return retval; +} + + + +static bool +string_is_valid(const char * const target, + const char * const srcBegin, + const char * const srcEnd) { + + unsigned int charsMatched; + const char * p; + + for (p = srcBegin, charsMatched = 0; + p != srcEnd && target[charsMatched] != '\0'; ++p) { + + if (*p == target[charsMatched]) + ++charsMatched; + else + break; + } + + return (*p == '\0' || isspace(*p)); +} + + + +static void +init_state(state_info * const si) { + + si->next = 0; + si->draw = false; + si->mode = DRAW_MODE_TRIANGLES; +} + + + +static void +make_lowercase(token const t) { + + char * p; + + for (p = t.begin; p != t.end; ++p) + *p = tolower(*p); +} + + + +static void +remove_comments(char * const str) { + + char * p; + + for (p = &str[0]; *p; ++p) { + if (*p == '#') { + *p = '\0'; + + break; + } + } +} + + + +int +process_next_command(input_info * const line, + struct boundary_info * const bi, + framebuffer_info * const fbi) { +/*---------------------------------------------------------------------------- + Doesn't necessarily process a command, just the next line of input, which + may be empty. Always returns 1, except when it cannot read any more lines of + input, an image buffer reallocation fails, or a "q" command is found in the + input -- in such cases it returns 0. +-----------------------------------------------------------------------------*/ + static state_info state; + + token nt; + + long int i_args[MAX_NUM_ATTRIBS]; + /* For storing potential integer arguments. */ + char * strtol_end; + /* To compare against nt.end when checking for errors with strtol */ + bool unrecognized_cmd; + /* To print out an error message in case an unrecognized command was + given. + */ + bool unrecognized_arg; + /* To print out an error message in case an unrecognized argument was + given. + */ + bool must_break_out; + /* To break out of the below switch statement when an invalid argument + is found. + */ + bool ok; + /* Indicates whether the input line was OK so that we can print out a + warning in case of excess arguments. + */ + + /* initial values */ + strtol_end = NULL; + unrecognized_cmd = false; + unrecognized_arg = false; + must_break_out = false; + ok = false; + + if (!state.initialized) { + init_state(&state); + clear_attribs(&state, fbi->maxval, fbi->num_attribs); + + state.initialized = true; + } + + if (getline(&line->buffer, &line->length, stdin) == -1) { + return 0; + } + + remove_comments(line->buffer); + + nt = next_token(line->buffer); + + make_lowercase(nt); + + switch (*nt.begin) { + case 'm': + if (!string_is_valid(CMD_SET_MODE, nt.begin, nt.end)) { + unrecognized_cmd = true; + + break; + } + + nt = next_token(nt.end); + + if (*nt.begin == '\0') { + pm_errormsg(SYNTAX_ERROR, line->number); + + break; + } + + make_lowercase(nt); + + switch(*nt.begin) { + case 't': + if (!string_is_valid(ARG_TRIANGLES, nt.begin, nt.end)) { + unrecognized_arg = true; + + break; + } + + state.mode = DRAW_MODE_TRIANGLES; + state.draw = false; + state.next = 0; + + ok = true; + + break; + case 's': + if (!string_is_valid(ARG_STRIP, nt.begin, nt.end)) { + unrecognized_arg = true; + + break; + } + + state.mode = DRAW_MODE_STRIP; + state.draw = false; + state.next = 0; + + ok = true; + + break; + case 'f': + if (!string_is_valid(ARG_FAN, nt.begin, nt.end)) { + unrecognized_arg = true; + + break; + } + + state.mode = DRAW_MODE_FAN; + state.draw = false; + state.next = 0; + + ok = true; + + break; + default: + unrecognized_arg = true; + } + + if (unrecognized_arg) { + pm_errormsg("error: unrecognized drawing mode in line %lu.", + line->number); + } + + break; + case 'a': { + uint8_t i; + if (!string_is_valid(CMD_SET_ATTRIBS, nt.begin, nt.end)) { + unrecognized_cmd = true; + + break; + } + + for (i = 0; i < fbi->num_attribs; i++) { + nt = next_token(nt.end); + + i_args[i] = strtol(nt.begin, &strtol_end, 10); + + if (*nt.begin == '\0' || strtol_end != nt.end) { + pm_errormsg(SYNTAX_ERROR, line->number); + + must_break_out = true; + + break; + } + + if (i_args[i] < 0 || i_args[i] > fbi->maxval) { + pm_errormsg("error: argument(s) out of bounds: line %lu.", + line->number); + + must_break_out = true; + + break; + } + } + + if (must_break_out) + { + break; + } + + for (i = 0; i < fbi->num_attribs; i++) { + state.curr_attribs[i] = i_args[i]; + } + + ok = true; + + } break; + case 'v': { + uint8_t i; + + if (!string_is_valid(CMD_VERTEX, nt.begin, nt.end)) { + unrecognized_cmd = true; + + break; + } + + for (i = 0; i < 3; i++) { + nt = next_token(nt.end); + + i_args[i] = strtol(nt.begin, &strtol_end, 10); + + if (*nt.begin == '\0' || strtol_end != nt.end) { + pm_errormsg(SYNTAX_ERROR, line->number); + + must_break_out = true; + + break; + } + + if (i < 2) { + if (i_args[i] < MIN_COORD || i_args[i] > MAX_COORD) { + pm_errormsg( + "error: coordinates out of bounds: line %lu.", + line->number); + + must_break_out = true; + + break; + } + } else { + if (i_args[i] < 0 || i_args[i] > MAX_Z) { + pm_errormsg( + "error: Z component out of bounds: line %lu.", + line->number); + + must_break_out = true; + + break; + } + } + } + + if (must_break_out) + { + break; + } + + for (i = 0; i < fbi->num_attribs; i++) { + state.v_attribs._[state.next][i] = state.curr_attribs[i]; + } + + state.v_attribs._[state.next][fbi->num_attribs] = i_args[2]; + + state.v_xy._[state.next][0] = i_args[0]; + state.v_xy._[state.next][1] = i_args[1]; + + state.next++; + + if (!state.draw) { + if (state.next == 3) { + state.draw = true; + } + } + + if (state.draw) { + draw_triangle(state.v_xy, state.v_attribs, bi, fbi); + } + + if (state.next == 3) { + switch(state.mode) { + case DRAW_MODE_FAN: + state.next = 1; + break; + case DRAW_MODE_TRIANGLES: + state.draw = false; + case DRAW_MODE_STRIP: + default: + state.next = 0; + } + } + + ok = true; + + } break; + case 'p': + if (!string_is_valid(CMD_PRINT, nt.begin, nt.end)) { + unrecognized_cmd = true; + + break; + } + case '!': + if (*nt.begin == '!') { + if (nt.end - nt.begin > 1) { + unrecognized_cmd = true; + + break; + } + } + + print_framebuffer(fbi); + + ok = true; + + break; + case 'c': + if (!string_is_valid(CMD_CLEAR, nt.begin, nt.end)) { + unrecognized_cmd = true; + + break; + } + case '*': + if (*nt.begin == '*') { + if(nt.end - nt.begin > 1) { + unrecognized_cmd = true; + + break; + } + } + + nt = next_token(nt.end); + + if (*nt.begin != '\0') { + make_lowercase(nt); + + switch(*nt.begin) { + case 'i': + if (!string_is_valid("image", nt.begin, nt.end)) { + unrecognized_arg = true; + + break; + } + + clear_framebuffer(true, false, fbi); + + break; + case 'd': + if (!string_is_valid("depth", nt.begin, nt.end)) { + unrecognized_arg = true; + + break; + } + case 'z': + if (*nt.begin == 'z') { + if (nt.end - nt.begin > 1) { + unrecognized_arg = true; + + break; + } + } + + clear_framebuffer(false, true, fbi); + + break; + default: + unrecognized_arg = true; + } + + if (unrecognized_arg) { + pm_errormsg("error: unrecognized argument: line %lu.", + line->number); + + break; + } + } else { + clear_framebuffer(true, true, fbi); + } + + ok = true; + + break; + case 'r': { + uint8_t i; + + if (!string_is_valid(CMD_RESET, nt.begin, nt.end)) { + unrecognized_cmd = true; + + break; + } + + for (i = 0; i < 2; i++) { + nt = next_token(nt.end); + + i_args[i] = strtol(nt.begin, &strtol_end, 10); + + if (*nt.begin == '\0' || nt.end != strtol_end) { + pm_errormsg(SYNTAX_ERROR, line->number); + + must_break_out = true; + + break; + } + } + + if (must_break_out) { + break; + } + + if (i_args[0] < 1 || i_args[0] > PAM_OVERALL_MAXVAL) { + pm_errormsg("error: invalid new maxval: line %lu.", + line->number); + + break; + } + + if (i_args[1] < 1 || i_args[1] > MAX_NUM_ATTRIBS) { + pm_errormsg("error: invalid new number of generic vertex " + "attributes: line %lu.", line->number); + + break; + } + + nt = next_token(nt.end); + + if (*nt.begin != '\0') { + if (!set_tupletype(nt.begin, fbi->outpam.tuple_type)) { + pm_message("warning: could not set new tuple type; " + "using a null string: line %lu.", + line->number); + + set_tupletype(NULL, fbi->outpam.tuple_type); + } + } else { + set_tupletype(NULL, fbi->outpam.tuple_type); + } + + if (!realloc_image_buffer(i_args[0], i_args[1], fbi)) { + pm_errormsg + ( + "fatal error upon reading line %lu: " + "could not reallocate image buffer -- " + "terminating pamtris.", + line->number + ); + + return 0; + } + + state.next = 0; + state.draw = false; + + clear_attribs(&state, fbi->maxval, fbi->num_attribs); + + } break; + case 'q': + if (!string_is_valid(CMD_QUIT, nt.begin, nt.end)) { + unrecognized_cmd = true; + + break; + } + + return 0; + case '\0': + break; + default: + unrecognized_cmd = true; + } + + { + char const next = *next_token(nt.end).begin; + + if (unrecognized_cmd) { + pm_errormsg("error: unrecognized command: line %lu.", + line->number); + } + else if (ok && next != '\0') { + pm_message(WARNING_EXCESS_ARGS, line->number); + } + } + line->number++; + + return 1; +} + + diff --git a/generator/pamtris/input.h b/generator/pamtris/input.h new file mode 100644 index 00000000..66969bb2 --- /dev/null +++ b/generator/pamtris/input.h @@ -0,0 +1,31 @@ +#ifndef INPUT_H_INCLUDED +#define INPUT_H_INCLUDED + +#include <stdint.h> + +struct boundary_info; +struct framebuffer_info; + +typedef struct input_info { +/*---------------------------------------------------------------------------- + Information necessary for the "process_next_command" function. It must be + initialized through "init_input_processor" and freed by + "free_input_processor". +-----------------------------------------------------------------------------*/ + char * buffer; + size_t length; + uint64_t number; +} input_info; + +void +init_input_processor(input_info * const ii); + +void +free_input_processor(input_info * const ii); + +int +process_next_command(input_info * const ii, + struct boundary_info * const bdi, + struct framebuffer_info * const fbi); + +#endif diff --git a/generator/pamtris/limits_pamtris.h b/generator/pamtris/limits_pamtris.h new file mode 100644 index 00000000..dcf1f1e6 --- /dev/null +++ b/generator/pamtris/limits_pamtris.h @@ -0,0 +1,7 @@ +#ifndef LIMITS_H_INCLUDED +#define LIMITS_H_INCLUDED + +#define MAX_NUM_ATTRIBS 20 +#define MAX_Z ((1 << 30) - 1) + +#endif diff --git a/generator/pamtris/pamtris.c b/generator/pamtris/pamtris.c new file mode 100644 index 00000000..74663531 --- /dev/null +++ b/generator/pamtris/pamtris.c @@ -0,0 +1,140 @@ +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#include "netpbm/mallocvar.h" +#include "netpbm/shhopt.h" +#include "netpbm/pam.h" + +#include "limits_pamtris.h" +#include "framebuffer.h" +#include "boundaries.h" +#include "input.h" + +#define MAX_METRICS 8192 + + + +static int +parse_command_line(int * const argc_ptr, + const char ** const argv, + int32_t * const width, + int32_t * const height, + int32_t * const maxval, + int32_t * const num_attribs, + char * const tupletype) { + + optEntry * option_def; + optStruct3 opt; + /* Instructions to pm_optParseOptions3 on how to parse our options */ + unsigned int option_def_index; + + char * tupletype_ptr; + + unsigned int width_spec, height_spec, maxval_spec, attribs_spec; + unsigned int tupletype_spec; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "width", OPT_INT, width, &width_spec, 0); + OPTENT3(0, "height", OPT_INT, height, &height_spec, 0); + OPTENT3(0, "maxval", OPT_INT, maxval, &maxval_spec, 0); + OPTENT3(0, "num_attribs", OPT_INT, num_attribs, &attribs_spec, 0); + OPTENT3(0, "tupletype", OPT_STRING, &tupletype_ptr, &tupletype_spec, 0); + + opt.opt_table = option_def; + opt.short_allowed = false; + opt.allowNegNum = false; + + pm_optParseOptions3(argc_ptr, (char **)argv, opt, sizeof(opt), 0); + + if (!width_spec || !height_spec || !attribs_spec) { + pm_errormsg( + "you must at least specify -width, -height and -num_attribs."); + + return 0; + } + + if (*width < 1 || *width > MAX_METRICS) { + pm_errormsg("invalid width."); + + return 0; + } + + if (*height < 1 || *height > MAX_METRICS) { + pm_errormsg("invalid height."); + + return 0; + } + + if (maxval_spec) { + if (*maxval < 1 || *maxval > PAM_OVERALL_MAXVAL) { + + + pm_errormsg("invalid maxval."); + + return 0; + } + } else { + *maxval = 255; + } + + if (*num_attribs < 1 || *num_attribs > MAX_NUM_ATTRIBS) { + pm_errormsg("invalid number of generic attributes per vertex."); + + return 0; + } + + if (tupletype_spec) { + if (!set_tupletype(tupletype_ptr, tupletype)) { + pm_errormsg("warning: invalid tuple type; using the null string."); + + set_tupletype(NULL, tupletype); + } + } + + free(option_def); + + return 1; +} + + + +int +main(int argc, const char ** argv) { + + framebuffer_info fbi; + boundary_info bi; + input_info ii; + + pm_proginit(&argc, (const char**)argv); + + set_tupletype(NULL, fbi.outpam.tuple_type); + + if (!parse_command_line(&argc, argv, + &fbi.width, &fbi.height, &fbi.maxval, + &fbi.num_attribs, fbi.outpam.tuple_type)) { + return 1; + } + + if (!init_framebuffer(&fbi)) { + pm_errormsg("out of memory."); + + return 3; + } + + init_boundary_buffer(&bi, fbi.height); + + init_input_processor(&ii); + + while (process_next_command(&ii, &bi, &fbi)); + + free_input_processor(&ii); + free_boundary_buffer(&bi); + free_framebuffer(&fbi); + + return 0; +} + + diff --git a/generator/pamtris/triangle.c b/generator/pamtris/triangle.c new file mode 100644 index 00000000..09d821e0 --- /dev/null +++ b/generator/pamtris/triangle.c @@ -0,0 +1,427 @@ +/*============================================================================= + triangle.c +=============================================================================== + Triangle functions +=============================================================================*/ +#include <stdlib.h> +#include <string.h> + +#include "netpbm/mallocvar.h" + +#include "utils.h" +#include "fract.h" +#include "boundaries.h" +#include "framebuffer.h" + +#include "triangle.h" + +static void +draw_partial_triangle( + const fract * const left_attribs_input, + const fract * const left_attribs_steps, + const fract * const rght_attribs_input, + const fract * const rght_attribs_steps, + int32_t const left_div, + int32_t const rght_div, + bool const upper_part, + const boundary_info * const bi, + framebuffer_info * const fbi) { + + uint8_t const num_planes = fbi->num_attribs + 1; + + fract * left_attribs; + fract * rght_attribs; + + int32_t first_row_index; + int32_t last_row_index; + + MALLOCARRAY_NOFAIL(left_attribs, num_planes); + MALLOCARRAY_NOFAIL(rght_attribs, num_planes); + + memcpy(left_attribs, left_attribs_input, num_planes * sizeof(fract)); + memcpy(rght_attribs, rght_attribs_input, num_planes * sizeof(fract)); + + if (upper_part) { + first_row_index = 0; + last_row_index = bi->num_upper_rows - 1; + } else { + first_row_index = bi->num_upper_rows; + last_row_index = bi->num_upper_rows + bi->num_lower_rows - 1; + } + + { + int32_t const row_delta = last_row_index - first_row_index; + + int32_t row; + + int32_t left_boundary; + int32_t rght_boundary; + + for (row = first_row_index; row <= last_row_index; ) { + get_triangle_boundaries(row, &left_boundary, &rght_boundary, bi); + { + int32_t const column_delta = rght_boundary - left_boundary; + int32_t start_column; + int32_t span_length; + + fract * attribs_start; + int32_t * attribs_begin; + int32_t * attribs_end; + fract * attribs_steps; + + MALLOCARRAY_NOFAIL(attribs_start, num_planes); + MALLOCARRAY_NOFAIL(attribs_begin, num_planes); + MALLOCARRAY_NOFAIL(attribs_end, num_planes); + MALLOCARRAY_NOFAIL(attribs_steps, num_planes); + + start_column = left_boundary; /* initial value */ + span_length = column_delta; /* initial value */ + + fract_to_int32_array(left_attribs, attribs_begin, num_planes); + fract_to_int32_array(rght_attribs, attribs_end, num_planes); + + int32_to_fract_array(attribs_begin, attribs_start, num_planes); + + gen_steps(attribs_begin, attribs_end, attribs_steps, + num_planes, column_delta); + + if (left_boundary < 0) { + start_column = 0; + + span_length += left_boundary; + + multi_step_up(attribs_start, attribs_steps, num_planes, + -left_boundary, column_delta); + } + + if (rght_boundary >= fbi->width) { + span_length -= rght_boundary - fbi->width; + } else { + span_length++; + } + + draw_span( + ((bi->start_scanline + row) * fbi->width) + start_column, + span_length, attribs_start, attribs_steps, column_delta, + fbi); + + if (row_delta > 0) { + step_up(left_attribs, left_attribs_steps, num_planes, + left_div); + step_up(rght_attribs, rght_attribs_steps, num_planes, + rght_div); + } + row++; + free(attribs_steps); + free(attribs_end); + free(attribs_begin); + free(attribs_start); + } + } + } + free(rght_attribs); + free(left_attribs); +} + + + +static void +draw_degenerate_horizontal(Xy const xy, + fract * const attribs_left, + fract * const attribs_mid, + const fract * const top2mid_steps, + const fract * const top2bot_steps, + const fract * const mid2bot_steps, + int32_t const top2mid_delta, + int32_t const top2bot_delta, + int32_t const mid2bot_delta, + framebuffer_info * const fbi) { + + uint8_t const num_planes = fbi->num_attribs + 1; + + fract * attribs_left_bkup; + + MALLOCARRAY_NOFAIL(attribs_left_bkup, num_planes); + + memcpy(attribs_left_bkup, attribs_left, num_planes * sizeof(fract)); + + { + int16_t const y = xy._[0][1]; + + int16_t x[3]; + int16_t x_start[3]; + fract * attribs[3]; + const fract * steps[3]; + int32_t span_length[3]; + unsigned int i; + + x[0] = xy._[0][0]; + x[1] = xy._[1][0]; + x[2] = xy._[2][0]; + + x_start[0] = x[0]; + x_start[1] = x[0]; + x_start[2] = x[1]; + + attribs[0] = attribs_left; + attribs[1] = attribs_left_bkup; + attribs[2] = attribs_mid; + + steps[0] = top2bot_steps; + steps[1] = top2mid_steps; + steps[2] = mid2bot_steps; + + span_length[0] = x[2] - x[0]; + span_length[1] = x[1] - x[0]; + span_length[2] = x[2] - x[1]; + + for (i = 0; i < 3; i++) { + int32_t const column_delta = span_length[i]; + + if (x_start[i] >= fbi->width || x_start[i] + span_length[i] < 0) { + continue; + } + + if (x_start[i] < 0) { + multi_step_up(attribs[i], steps[i], num_planes, -x_start[i], + column_delta); + + span_length[i] += x_start[i]; + + x_start[i] = 0; + } + + if (x_start[i] + span_length[i] >= fbi->width) { + span_length[i] -= x_start[i] + span_length[i] - fbi->width; + } else { + span_length[i]++; + } + + draw_span((y * fbi->width) + x_start[i], span_length[i], + attribs[i], steps[i], column_delta, fbi); + } + } + free(attribs_left_bkup); +} + + + +void +draw_triangle(Xy const xy_input, + Attribs const attribs_input, + boundary_info * const bi, + framebuffer_info * const fbi) { + + uint8_t const num_planes = fbi->num_attribs + 1; + + Xy xy; + int32_t * attribs[3]; + unsigned int i; + uint8_t index_array[3]; + int32_t y_array[3]; + int32_t x_array[3]; + + MALLOCARRAY_NOFAIL(attribs[0], num_planes); + MALLOCARRAY_NOFAIL(attribs[1], num_planes); + MALLOCARRAY_NOFAIL(attribs[2], num_planes); + + xy = xy_input; + + for (i = 0; i < 3; i++) { + memcpy(attribs[i], attribs_input._[i], num_planes * sizeof(int32_t)); + } + + /* Argument preparations for sort3: */ + + index_array[0] = 0; index_array[1] = 1; index_array[2] = 2; + y_array[0] = xy._[0][1]; y_array[1] = xy._[1][1]; y_array[2] = xy._[2][1]; + x_array[0] = xy._[0][0]; x_array[1] = xy._[1][0]; x_array[2] = xy._[2][0]; + + sort3(index_array, y_array, x_array); + + { + uint8_t const top = index_array[0]; + uint8_t const mid = index_array[1]; + uint8_t const bot = index_array[2]; + + bool mid_is_to_the_left; + + Xy xy_sorted; + + xy_sorted._[0][0] = xy._[top][0]; + xy_sorted._[0][1] = xy._[top][1]; + xy_sorted._[1][0] = xy._[mid][0]; + xy_sorted._[1][1] = xy._[mid][1]; + xy_sorted._[2][0] = xy._[bot][0]; + xy_sorted._[2][1] = xy._[bot][1]; + + mid_is_to_the_left = + gen_triangle_boundaries(xy_sorted, bi, fbi->width, fbi->height); + + if (bi->start_scanline == -1) { + /* Triangle is completely out of the bounds of the framebuffer. */ + } else { + bool const no_upper_part = + (xy_sorted._[1][1] == xy_sorted._[0][1]); + + bool const horizontal = + (xy._[0][1] == xy._[1][1] && xy._[1][1] == xy._[2][1]); + /* We are dealing with a degenerate horizontal triangle */ + + uint8_t t = ~horizontal & 1; + + int32_t top2mid_delta = xy._[mid][t] - xy._[top][t]; + int32_t top2bot_delta = xy._[bot][t] - xy._[top][t]; + int32_t mid2bot_delta = xy._[bot][t] - xy._[mid][t]; + + fract * top2mid_steps; + fract * top2bot_steps; + fract * mid2bot_steps; + + fract * upper_left_attribs_steps; + fract * lower_left_attribs_steps; + fract * upper_rght_attribs_steps; + fract * lower_rght_attribs_steps; + + int32_t upper_left_delta; + int32_t lower_left_delta; + int32_t upper_rght_delta; + int32_t lower_rght_delta; + + fract * left_attribs; + fract * rght_attribs; + + bool degenerate_horizontal; + + MALLOCARRAY_NOFAIL(top2mid_steps, num_planes); + MALLOCARRAY_NOFAIL(top2bot_steps, num_planes); + MALLOCARRAY_NOFAIL(mid2bot_steps, num_planes); + MALLOCARRAY_NOFAIL(left_attribs, num_planes); + MALLOCARRAY_NOFAIL(rght_attribs, num_planes); + + if (!horizontal) { + top2mid_delta += !no_upper_part; + top2bot_delta += 1; + mid2bot_delta += no_upper_part; + } + + gen_steps(attribs[top], attribs[mid], top2mid_steps, num_planes, + top2mid_delta); + gen_steps(attribs[top], attribs[bot], top2bot_steps, num_planes, + top2bot_delta); + gen_steps(attribs[mid], attribs[bot], mid2bot_steps, num_planes, + mid2bot_delta); + + int32_to_fract_array(attribs[top], left_attribs, num_planes); + int32_to_fract_array(attribs[top], rght_attribs, num_planes); + + if (mid_is_to_the_left) { + upper_left_attribs_steps = top2mid_steps; + lower_left_attribs_steps = mid2bot_steps; + upper_rght_attribs_steps = top2bot_steps; + lower_rght_attribs_steps = upper_rght_attribs_steps; + + upper_left_delta = top2mid_delta; + lower_left_delta = mid2bot_delta; + upper_rght_delta = top2bot_delta; + lower_rght_delta = upper_rght_delta; + } else { + upper_rght_attribs_steps = top2mid_steps; + lower_rght_attribs_steps = mid2bot_steps; + upper_left_attribs_steps = top2bot_steps; + lower_left_attribs_steps = upper_left_attribs_steps; + + upper_rght_delta = top2mid_delta; + lower_rght_delta = mid2bot_delta; + upper_left_delta = top2bot_delta; + lower_left_delta = upper_left_delta; + } + + if (no_upper_part) { + int32_to_fract_array(attribs[mid], rght_attribs, num_planes); + + if (horizontal) { + degenerate_horizontal = true; + } else { + degenerate_horizontal = false; + + step_up(left_attribs, lower_left_attribs_steps, num_planes, + lower_left_delta); + step_up(rght_attribs, lower_rght_attribs_steps, num_planes, + lower_rght_delta); + } + } else { + int32_t delta; + + degenerate_horizontal = false; + + step_up(left_attribs, upper_left_attribs_steps, num_planes, + upper_left_delta); + step_up(rght_attribs, upper_rght_attribs_steps, num_planes, + upper_rght_delta); + + if (bi->num_upper_rows > 0) { + + if (bi->start_scanline > xy._[top][1]) { + delta = bi->start_scanline - xy._[top][1]; + + multi_step_up(left_attribs, upper_left_attribs_steps, + num_planes, delta, upper_left_delta); + multi_step_up(rght_attribs, upper_rght_attribs_steps, + num_planes, delta, upper_rght_delta); + } + + draw_partial_triangle( + left_attribs, upper_left_attribs_steps, + rght_attribs, upper_rght_attribs_steps, + upper_left_delta, upper_rght_delta, + true, + bi, + fbi + ); + + delta = xy._[mid][1] - bi->start_scanline; + } else { + delta = top2mid_delta; + } + + multi_step_up(left_attribs, upper_left_attribs_steps, + num_planes, delta, upper_left_delta); + multi_step_up(rght_attribs, upper_rght_attribs_steps, + num_planes, delta, upper_rght_delta); + } + if (degenerate_horizontal) { + draw_degenerate_horizontal( + xy_sorted, + left_attribs, rght_attribs, + top2mid_steps, top2bot_steps, mid2bot_steps, + top2mid_delta, top2bot_delta, mid2bot_delta, + fbi + ); + } else { + if (bi->start_scanline > xy._[mid][1]) { + int32_t const delta = bi->start_scanline - xy._[mid][1]; + + multi_step_up(left_attribs, lower_left_attribs_steps, + num_planes, delta, lower_left_delta); + multi_step_up(rght_attribs, lower_rght_attribs_steps, + num_planes, delta, lower_rght_delta); + } + + draw_partial_triangle( + left_attribs, lower_left_attribs_steps, + rght_attribs, lower_rght_attribs_steps, + lower_left_delta, lower_rght_delta, + false, + bi, + fbi + ); + } + free(rght_attribs); free(left_attribs); + free(mid2bot_steps); free(top2bot_steps); free(top2mid_steps); + } + } + free(attribs[2]); free(attribs[1]); free(attribs[0]); +} + + diff --git a/generator/pamtris/triangle.h b/generator/pamtris/triangle.h new file mode 100644 index 00000000..79178ad0 --- /dev/null +++ b/generator/pamtris/triangle.h @@ -0,0 +1,25 @@ +#ifndef TRIANGLE_H_INCLUDED +#define TRIANGLE_H_INCLUDED + +#include <stdint.h> + +#include "limits_pamtris.h" + +struct boundary_info; +struct framebuffer_info; + +typedef struct { + int32_t _[3][2]; +} Xy; + +typedef struct { + int32_t _[3][MAX_NUM_ATTRIBS + 1]; +} Attribs; + +void +draw_triangle(Xy const xy, + Attribs const attribs, + struct boundary_info * const bdi, + struct framebuffer_info * const fbi); + +#endif diff --git a/generator/pamtris/utils.c b/generator/pamtris/utils.c new file mode 100644 index 00000000..09c9b4d0 --- /dev/null +++ b/generator/pamtris/utils.c @@ -0,0 +1,256 @@ +/*============================================================================= + utils.c +=============================================================================== + Utility functions +=============================================================================*/ + +#include <stdlib.h> +#include <stdint.h> + +#include "fract.h" + +#include "utils.h" + + + +void +step_up(fract * const vars, + const fract * const steps, + uint8_t const element_ct, + int32_t const divisor) { +/*---------------------------------------------------------------------------- + Apply interpolation steps steps[] to a collection of fract variables vars[] + once. I.e. add each steps[i] to vars[i]. + + 'element_ct' is the number of elements in 'vars' and 'steps'. + + 'divisor' is the divisor used to interpret the fractions. + + It *is* safe to pass a 0 divisor to this function. +-----------------------------------------------------------------------------*/ + unsigned int i; + + for (i = 0; i < element_ct; ++i) { + /* To add the fraction steps[i] to the fraction vars[i]: add the + quotient of step steps[i] to the quotient of variable vars[i] and + the remainder of the step to the remainder of the variable. If this + makes the agumented remainder equal to or larger than the divisor, + increment the quotient of the variable if the step is positive or + decrement it if the step is negative, and subtract the divisor from + the remainder of the variable (in either case). + */ + + vars[i].q += steps[i].q; + vars[i].r += steps[i].r; + + { + uint32_t const negative_mask = -steps[i].negative_flag; + /* (-1 if the step is negative; 1 otherwise) */ + + uint32_t const overdiv_mask = + -(((uint32_t)~(vars[i].r - divisor)) >> 31); + /* = ~0 if var->r >= div; 0 otherwise. */ + + vars[i].q += (negative_mask | 1) & overdiv_mask; + vars[i].r -= divisor & overdiv_mask; + } + } +} + + + +void +multi_step_up(fract * const vars, + const fract * const steps, + uint8_t const elements, + int32_t const times, + int32_t const div) { +/*---------------------------------------------------------------------------- + Similar to step_up, but apply the interpolation step an arbitrary number + of times, instead of just once. + + It *is* also safe to pass a 0 divisor to this function. +-----------------------------------------------------------------------------*/ + unsigned int i; + + for (i = 0; i < elements; i++) { + uint32_t const negative_mask = -steps[i].negative_flag; + + vars[i].q += times * steps[i].q; + vars[i].r += times * steps[i].r; + + if(vars[i].r >= div && div != 0) { + int32_t const r_q = vars[i].r / div; + int32_t const r_r = vars[i].r % div; + + vars[i].q += (-r_q & negative_mask) | (r_q & ~negative_mask); + /* = -r_q if the step is negative; r_q, otherwise. */ + vars[i].r = r_r; + } + } +} + + + +void +gen_steps(const int32_t * const begin, + const int32_t * const end, + fract * const out, + uint8_t const elements, + int32_t const div) { +/*---------------------------------------------------------------------------- + Generate the interpolation steps for a collection of initial and final + values. "begin" points to an array of initial values, "end" points to the + array of corresponding final values; each interpolation step is stored in + the appropriate position in the array pointed by "out"; "elements" indicates + the number of elements in each of the previously mentioned arrays and + "divisor" is the common value by which we want to divide the difference + between each element in the array pointed to by "end" and the corresponding + element in the array pointed to by "begin". After an execution of this + function, for each out[i], with 0 <= i < elements, the following will hold: + + 1. If divisor > 1: + out[i].q = (end[i] - begin[i]) / divisor + out[i].r = abs((end[i] - begin[i]) % divisor) + + 2. If divisor == 1 || divisor == 0: + out[i].q = end[i] - begin[i] + out[i].r = 0 +-----------------------------------------------------------------------------*/ + if (div > 1) { + unsigned int i; + + for (i = 0; i < elements; i++) { + int32_t const delta = end[i] - begin[i]; + + out[i].q = delta / div; + out[i].r = abs(delta % div); + out[i].negative_flag = ((uint32_t)delta) >> 31; + } + } else { + unsigned int i; + + for (i = 0; i < elements; i++) { + int32_t const delta = end[i] - begin[i]; + + out[i].q = delta; + out[i].r = 0; + out[i].negative_flag = ((uint32_t)delta) >> 31; + } + } +} + + + +void +fract_to_int32_array(const fract * const in, + int32_t * const out, + uint8_t const elements) { + + unsigned int i; + + for (i = 0; i < elements; i++) { + out[i] = in[i].q; + } +} + + + +void +int32_to_fract_array(const int32_t * const in, + fract * const out, + uint8_t const elements) { + + unsigned int i; + + for (i = 0; i < elements; i++) { + out[i].q = in[i]; + out[i].r = 0; + } +} + + + +static void +swap(uint8_t * const a, + uint8_t * const b) { +/*---------------------------------------------------------------------------- + Swap the contents pointed to by a and b. +-----------------------------------------------------------------------------*/ + uint8_t const temp = *a; + + *a = *b; + *b = temp; +} + + + +void +sort3(uint8_t * const index_array, + const int32_t * const y_array, + const int32_t * const x_array) { +/*---------------------------------------------------------------------------- + Sort an index array of 3 elements. This function is used to sort vertices + with regard to relative row from top to bottom, but instead of sorting + an array of vertices with all their coordinates, we simply sort their + indices. Each element in the array pointed to by "index_array" should + contain one of the numbers 0, 1 or 2, and each one of them should be + different. "y_array" should point to an array containing the corresponding + Y coordinates (row) of each vertex and "x_array" should point to an array + containing the corresponding X coordinates (column) of each vertex. + + If the Y coordinates are all equal, the indices are sorted with regard to + relative X coordinate from left to right. If only the top two vertex have + the same Y coordinate, the array is sorted normally with regard to relative + Y coordinate, but the first two indices are then sorted with regard to + relative X coordinate. Finally, If only the bottom two vertex have the same + Y coordinate, the array is sorted normally with regard to relative Y + coordinate, but the last two indices are then sorted with regard to relative + X coordinate. +-----------------------------------------------------------------------------*/ + uint8_t * const ia = index_array; + + const int32_t * ya; + const int32_t * xa; + + ya = y_array; /* initial value */ + xa = x_array; /* initial value */ + + if (ya[0] == ya[1] && ya[1] == ya[2]) { + /* In case the vertices represent a degenerate horizontal triangle, we + sort according to relative X coordinate, as opposed to Y. + */ + ya = xa; + } + + if (ya[ia[2]] < ya[ia[1]]) { + swap(ia, ia + 2); + if (ya[ia[2]] < ya[ia[1]]) { + swap(ia + 1, ia + 2); + if (ya[ia[1]] < ya[ia[0]]) { + swap(ia, ia + 1); + } + } + } else if (ya[ia[1]] < ya[ia[0]]) { + swap(ia, ia + 1); + if (ya[ia[2]] < ya[ia[1]]) { + swap(ia + 1, ia + 2); + } + } + + if (ya == xa) { + return; + } + + if (ya[ia[0]] == ya[ia[1]]) { + if (xa[ia[1]] < xa[ia[0]]) { + swap(ia, ia + 1); + } + } else if (ya[ia[1]] == ya[ia[2]]) { + if (xa[ia[2]] < xa[ia[1]]) { + swap(ia + 1, ia + 2); + } + } +} + + diff --git a/generator/pamtris/utils.h b/generator/pamtris/utils.h new file mode 100644 index 00000000..3b7cfbe4 --- /dev/null +++ b/generator/pamtris/utils.h @@ -0,0 +1,41 @@ +#ifndef UTIL_H_INCLUDED +#define UTIL_H_INCLUDED + +#include "fract.h" + +void +gen_steps(const int32_t * const begin, + const int32_t * const end, + fract * const out, + uint8_t const elements, + int32_t const divisor); + +void +step_up(fract * const vars, + const fract * const steps, + uint8_t const elements, + int32_t const divisor); + +void +multi_step_up(fract * const vars, + const fract * const steps, + uint8_t const elements, + int32_t const times, + int32_t const divisor); + +void +fract_to_int32_array(const fract * const in, + int32_t * const out, + uint8_t const elements); + +void +int32_to_fract_array(const int32_t * const in, + fract * const out, + uint8_t const elements); + +void +sort3(uint8_t * const index_array, + const int32_t * const y_array, + const int32_t * const x_array); + +#endif diff --git a/generator/pbmtext.c b/generator/pbmtext.c index 7d9f7cf7..e52d5199 100644 --- a/generator/pbmtext.c +++ b/generator/pbmtext.c @@ -179,59 +179,26 @@ parseCommandLine(int argc, const char ** argv, static void -reportFont(struct font2 * const fontP) { - - unsigned int n; - unsigned int c; +reportFont(const struct font2 * const fontP) { pm_message("FONT:"); - pm_message(" character dimensions: %uw x %uh", + pm_message(" Name: %s", fontP->name); + pm_message(" Encoding: %s", fontP->charset_string); + pm_message(" Origin: %s", pbmFontOrigin[fontP->load_fn]); + pm_message(" Character dimensions: %uw x %uh", fontP->maxwidth, fontP->maxheight); pm_message(" Additional vert white space: %d pixels", fontP->y); - - for (c = 0, n = 0; c <= fontP->maxglyph; ++c) { - if (fontP->glyph[c]) - ++n; - } - - pm_message(" # characters: %u", n); + pm_message(" # characters loaded: %u", fontP->chars); } -static struct font * -fontFromFile(const char * const fileName) { - - struct font * retval; - - jmp_buf jmpbuf; - int rc; - - rc = setjmp(jmpbuf); - - if (rc == 0) { - /* This is the normal program flow */ - pm_setjmpbuf(&jmpbuf); - - retval = pbm_loadfont(fileName); - - pm_setjmpbuf(NULL); - } else { - /* This is the second pass, after pbm_loadfont does a longjmp - because it fails. - */ - pm_setjmpbuf(NULL); - - pm_error("Failed to load font from file '%s'", fileName); - } - - return retval; -} static struct font2 * -font2FromFile(const char * const fileName) { +font2FromFile(const char * const fileName, + PM_WCHAR const maxmaxglyph) { struct font2 * font2P; @@ -244,7 +211,7 @@ font2FromFile(const char * const fileName) { /* This is the normal program flow */ pm_setjmpbuf(&jmpbuf); - font2P = pbm_loadbdffont2(fileName, PM_FONT2_MAXGLYPH); + font2P = pbm_loadfont2(fileName, maxmaxglyph); pm_setjmpbuf(NULL); } else { @@ -267,21 +234,14 @@ computeFont(struct CmdlineInfo const cmdline, struct font2 * font2P; - if (cmdline.wchar && cmdline.font) - font2P = font2FromFile(cmdline.font); - else { - struct font * fontP; - - if (cmdline.font) - fontP = fontFromFile(cmdline.font); - else { - if (cmdline.builtin) - fontP = pbm_defaultfont(cmdline.builtin); - else - fontP = pbm_defaultfont("bdf"); - } - font2P = pbm_expandbdffont(fontP); - } + if (cmdline.font) + font2P = font2FromFile(cmdline.font, + cmdline.wchar ? PM_FONT2_MAXGLYPH : + PM_FONT_MAXGLYPH); + else if (cmdline.builtin) + font2P = pbm_defaultfont2(cmdline.builtin); + else + font2P = pbm_defaultfont2(cmdline.wchar ? "bdf" : "bdf"); if (cmdline.verbose) reportFont(font2P); @@ -292,7 +252,7 @@ computeFont(struct CmdlineInfo const cmdline, struct Text { - PM_WCHAR ** textArray; /* malloc'ed */ + PM_WCHAR ** textArray; /* malloc'ed */ /* This is strictly characters that are in user's font - no control characters, no undefined code points. */ @@ -996,6 +956,7 @@ getText(PM_WCHAR const cmdlineText[], inputText.lineCount = 1; fixControlChars(cmdlineText, fontP, (const PM_WCHAR**)&inputText.textArray[0], fixMode); + free((void *) cmdlineText); } else { /* Read text from stdin. */ @@ -1053,6 +1014,7 @@ getText(PM_WCHAR const cmdlineText[], inputText.textArray = textArray; inputText.lineCount = lineCount; inputText.allocatedLineCount = lineCount; + free(buf); } *inputTextP = inputText; } @@ -1209,6 +1171,9 @@ renderText(unsigned int const cols, insertCharacters(bits, formattedText, fontP, vmargin, hmargin + maxleftb, space, cols, rows, lspace, fixedAdvance); + /* Free all font data */ + pbm_destroybdffont2(fontP); + { unsigned int row; @@ -1412,10 +1377,6 @@ main(int argc, const char *argv[]) { else pbmtext(cmdline, fontP, stdout); - /* Note that *fontP is unfreeable. See pbm_loadbdffont2, - pbm_expandbdffont - */ - pm_close(stdout); return 0; diff --git a/generator/ppmcie.c b/generator/ppmcie.c index 717ed13b..86325ba6 100644 --- a/generator/ppmcie.c +++ b/generator/ppmcie.c @@ -10,7 +10,7 @@ granted, without any conditions or restrictions. This software is provided "as is" without express or implied warranty. - This program was called cietoppm in Walker's original work. + This program was called cietoppm in Walker's original work. Because "cie" is not a graphics format, Bryan changed the name when he integrated it into the Netpbm package in March 2000. */ @@ -294,10 +294,10 @@ static struct colorSystem const */ static struct colorSystem Customsystem = { "Custom", - 0.64, 0.33, 0.30, 0.60, 0.15, 0.06, + 0.64, 0.33, 0.30, 0.60, 0.15, 0.06, IlluminantD65, GAMMA_REC709 }; - + static void @@ -344,11 +344,11 @@ xyz_to_rgb(const struct colorSystem * const cs, which sums to the desired chromaticity. If the requested chromaticity falls outside the Maxwell triangle (color gamut) formed by the three primaries, one of the r, g, or b weights will - be negative. + be negative. Caller can use constrain_rgb() to desaturate an outside-gamut color to the closest representation within the available - gamut. + gamut. -----------------------------------------------------------------------------*/ double xr, yr, zr, xg, yg, zg, xb, yb, zb; double xw, yw, zw; @@ -542,7 +542,7 @@ drawTongueOutline(pixel ** const pixels, computeMonochromeColorLocation(wavelength, pxcols, pxrows, upvp, &icx, &icy); - + if (wavelength > 380) ppmd_line(pixels, pixcols, pixrows, maxval, B(lx, ly), B(icx, icy), @@ -589,7 +589,7 @@ findTongue(pixel ** const pixels, int const leftEdge = i; *presentP = true; - + for (j = pxcols - 1; j >= leftEdge && PPM_GETR(Bixels(row, j)) == 0; --j); @@ -651,12 +651,12 @@ fillInTongue(pixel ** const pixels, xyz_to_rgb(cs, cx, cy, cz, &jr, &jg, &jb); mx = maxval; - + /* Check whether the requested color is within the gamut achievable with the given color system. If not, draw it in a reduced intensity, interpolated by desaturation to the closest within-gamut color. */ - + if (constrain_rgb(&jr, &jg, &jb)) mx = highlightGamut ? maxval : ((maxval + 1) * 3) / 4; @@ -688,7 +688,7 @@ drawYAxis(pixel ** const pixels, unsigned int const xBias, unsigned int const yBias, pixel const axisColor) { - + unsigned int const pxrows = pixrows - yBias; ppmd_line(pixels, pixcols, pixrows, maxval, @@ -706,7 +706,7 @@ drawXAxis(pixel ** const pixels, unsigned int const xBias, unsigned int const yBias, pixel const axisColor) { - + unsigned int const pxcols = pixcols - xBias; unsigned int const pxrows = pixrows - yBias; @@ -778,7 +778,7 @@ tickY(pixel ** const pixels, /* Pixel row where the top of the tick goes */ unsigned int const tickThickness = Sz(3); /* Thickness of the tick in pixels */ - + char s[20]; assert(tenth < 10); @@ -970,7 +970,7 @@ plotBlackBodyCurve(pixel ** const pixels, /* Label selected tick marks with decreasing density. */ - if (t <= 5000.1 || (t > 5000.0 && + if (t <= 5000.1 || (t > 5000.0 && ((((int) t) % 5000) == 0) && t != 20000.0)) { char bb[20]; @@ -980,7 +980,7 @@ plotBlackBodyCurve(pixel ** const pixels, B(lx - Sz(12), ly - Sz(4)), Sz(6), 0, bb, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } - + } } lx = xb; @@ -998,7 +998,7 @@ overlappingLegend(bool const upvp, if (upvp) retval = (waveLength == 430 || waveLength == 640); - else + else retval = (waveLength == 460 || waveLength == 630 || waveLength == 640); return retval; } @@ -1054,7 +1054,7 @@ plotMonochromeWavelengths( /* Draw the tick mark */ PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); tx = icx + ((x < 520) ? Sz(-2) : ((x >= 535) ? Sz(2) : 0)); - ty = icy + ((x < 520) ? 0 : ((x >= 535) ? Sz(-1) : Sz(-2))); + ty = icy + ((x < 520) ? 0 : ((x >= 535) ? Sz(-1) : Sz(-2))); ppmd_line(pixels, pixcols, pixrows, maxval, B(icx, icy), B(tx, ty), PPMD_NULLDRAWPROC, (char *) &rgbcolor); @@ -1113,7 +1113,7 @@ writeLabel(pixel ** const pixels, char sysdesc[256]; PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); - + pm_snprintf(sysdesc, sizeof(sysdesc), "System: %s\n" "Primary illuminants (X, Y)\n" @@ -1198,9 +1198,9 @@ main(int argc, } else if (pm_keymatch(argv[argn], "-smpte", 2)) { cs = &SMPTEsystem; } else if (pm_keymatch(argv[argn], "-hdtv", 2)) { - cs = &HDTVsystem; + cs = &HDTVsystem; } else if (pm_keymatch(argv[argn], "-cie", 1)) { - cs = &CIEsystem; + cs = &CIEsystem; } else if (pm_keymatch(argv[argn], "-black", 3)) { showBlack = true; /* Show black body curve */ } else if (pm_keymatch(argv[argn], "-wpoint", 2)) { |