about summary refs log tree commit diff
path: root/generator/pamtris/framebuffer.c
blob: 5665e9a4c807d70572268610e6d7369d96d7ac54 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/*=============================================================================
                              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 <math.h>

#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);
    }
}