aboutsummaryrefslogtreecommitdiff
path: root/src/r_patch.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/r_patch.c')
-rw-r--r--src/r_patch.c786
1 files changed, 786 insertions, 0 deletions
diff --git a/src/r_patch.c b/src/r_patch.c
new file mode 100644
index 0000000..e466fb2
--- /dev/null
+++ b/src/r_patch.c
@@ -0,0 +1,786 @@
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom: a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2002 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 * Copyright 2005, 2006 by
12 * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 *-----------------------------------------------------------------------------*/
30
31#include "z_zone.h"
32#include "doomstat.h"
33#include "w_wad.h"
34#include "r_main.h"
35#include "r_sky.h"
36#include "r_bsp.h"
37#include "r_things.h"
38#include "p_tick.h"
39#include "i_system.h"
40#include "r_draw.h"
41#include "lprintf.h"
42#include "r_patch.h"
43#include <assert.h>
44
45// posts are runs of non masked source pixels
46typedef struct
47{
48 byte topdelta; // -1 is the last post in a column
49 byte length; // length data bytes follows
50} post_t;
51
52// column_t is a list of 0 or more post_t, (byte)-1 terminated
53typedef post_t column_t;
54
55//
56// Patches.
57// A patch holds one or more columns.
58// Patches are used for sprites and all masked pictures,
59// and we compose textures from the TEXTURE1/2 lists
60// of patches.
61//
62
63typedef struct
64{
65 short width, height; // bounding box size
66 short leftoffset; // pixels to the left of origin
67 short topoffset; // pixels below the origin
68 int columnofs[8]; // only [width] used
69} patch_t;
70
71//---------------------------------------------------------------------------
72// Re-engineered patch support
73//---------------------------------------------------------------------------
74static rpatch_t *patches = 0;
75
76static rpatch_t *texture_composites = 0;
77
78//---------------------------------------------------------------------------
79void R_InitPatches(void) {
80 if (!patches)
81 {
82 patches = (rpatch_t*)malloc(numlumps * sizeof(rpatch_t));
83 // clear out new patches to signal they're uninitialized
84 memset(patches, 0, sizeof(rpatch_t)*numlumps);
85 }
86 if (!texture_composites)
87 {
88 texture_composites = (rpatch_t*)malloc(numtextures * sizeof(rpatch_t));
89 // clear out new patches to signal they're uninitialized
90 memset(texture_composites, 0, sizeof(rpatch_t)*numtextures);
91 }
92}
93
94//---------------------------------------------------------------------------
95void R_FlushAllPatches(void) {
96 int i;
97
98 if (patches)
99 {
100 for (i=0; i < numlumps; i++)
101 if (patches[i].locks > 0)
102 I_Error("R_FlushAllPatches: patch number %i still locked",i);
103 free(patches);
104 patches = NULL;
105 }
106 if (texture_composites)
107 {
108 for (i=0; i<numtextures; i++)
109 if (texture_composites[i].data)
110 free(texture_composites[i].data);
111 free(texture_composites);
112 texture_composites = NULL;
113 }
114}
115
116//---------------------------------------------------------------------------
117int R_NumPatchWidth(int lump)
118{
119 const rpatch_t *patch = R_CachePatchNum(lump);
120 int width = patch->width;
121 R_UnlockPatchNum(lump);
122 return width;
123}
124
125//---------------------------------------------------------------------------
126int R_NumPatchHeight(int lump)
127{
128 const rpatch_t *patch = R_CachePatchNum(lump);
129 int height = patch->height;
130 R_UnlockPatchNum(lump);
131 return height;
132}
133
134//---------------------------------------------------------------------------
135static int getPatchIsNotTileable(const patch_t *patch) {
136 int x=0, numPosts, lastColumnDelta = 0;
137 const column_t *column;
138 int cornerCount = 0;
139 int hasAHole = 0;
140
141 for (x=0; x<SHORT(patch->width); x++) {
142 column = (const column_t *)((const byte *)patch + LONG(patch->columnofs[x]));
143 if (!x) lastColumnDelta = column->topdelta;
144 else if (lastColumnDelta != column->topdelta) hasAHole = 1;
145
146 numPosts = 0;
147 while (column->topdelta != 0xff) {
148 // check to see if a corner pixel filled
149 if (x == 0 && column->topdelta == 0) cornerCount++;
150 else if (x == 0 && column->topdelta + column->length >= SHORT(patch->height)) cornerCount++;
151 else if (x == SHORT(patch->width)-1 && column->topdelta == 0) cornerCount++;
152 else if (x == SHORT(patch->width)-1 && column->topdelta + column->length >= SHORT(patch->height)) cornerCount++;
153
154 if (numPosts++) hasAHole = 1;
155 column = (const column_t *)((const byte *)column + column->length + 4);
156 }
157 }
158
159 if (cornerCount == 4) return 0;
160 return hasAHole;
161}
162
163//---------------------------------------------------------------------------
164static int getIsSolidAtSpot(const column_t *column, int spot) {
165 if (!column) return 0;
166 while (column->topdelta != 0xff) {
167 if (spot < column->topdelta) return 0;
168 if ((spot >= column->topdelta) && (spot <= column->topdelta + column->length)) return 1;
169 column = (const column_t*)((const byte*)column + 3 + column->length + 1);
170 }
171 return 0;
172}
173
174//---------------------------------------------------------------------------
175// Used to determine whether a column edge (top or bottom) should slope
176// up or down for smoothed masked edges - POPE
177//---------------------------------------------------------------------------
178static int getColumnEdgeSlope(const column_t *prevcolumn, const column_t *nextcolumn, int spot) {
179 int holeToLeft = !getIsSolidAtSpot(prevcolumn, spot);
180 int holeToRight = !getIsSolidAtSpot(nextcolumn, spot);
181
182 if (holeToLeft && !holeToRight) return 1;
183 if (!holeToLeft && holeToRight) return -1;
184 return 0;
185}
186
187//---------------------------------------------------------------------------
188static void createPatch(int id) {
189 rpatch_t *patch;
190 const int patchNum = id;
191 const patch_t *oldPatch = (const patch_t*)W_CacheLumpNum(patchNum);
192 const column_t *oldColumn, *oldPrevColumn, *oldNextColumn;
193 int x, y;
194 int pixelDataSize;
195 int columnsDataSize;
196 int postsDataSize;
197 int dataSize;
198 int *numPostsInColumn;
199 int numPostsTotal;
200 const unsigned char *oldColumnPixelData;
201 int numPostsUsedSoFar;
202 int edgeSlope;
203
204#ifdef RANGECHECK
205 if (id >= numlumps)
206 I_Error("createPatch: %i >= numlumps", id);
207#endif
208
209 patch = &patches[id];
210 // proff - 2003-02-16 What about endianess?
211 patch->width = SHORT(oldPatch->width);
212 patch->widthmask = 0;
213 patch->height = SHORT(oldPatch->height);
214 patch->leftoffset = SHORT(oldPatch->leftoffset);
215 patch->topoffset = SHORT(oldPatch->topoffset);
216 patch->isNotTileable = getPatchIsNotTileable(oldPatch);
217
218 // work out how much memory we need to allocate for this patch's data
219 pixelDataSize = (patch->width * patch->height + 4) & ~3;
220 columnsDataSize = sizeof(rcolumn_t) * patch->width;
221
222 // count the number of posts in each column
223 numPostsInColumn = (int*)malloc(sizeof(int) * patch->width);
224 numPostsTotal = 0;
225
226 for (x=0; x<patch->width; x++) {
227 oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x]));
228 numPostsInColumn[x] = 0;
229 while (oldColumn->topdelta != 0xff) {
230 numPostsInColumn[x]++;
231 numPostsTotal++;
232 oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4);
233 }
234 }
235
236 postsDataSize = numPostsTotal * sizeof(rpost_t);
237
238 // allocate our data chunk
239 dataSize = pixelDataSize + columnsDataSize + postsDataSize;
240 patch->data = (unsigned char*)Z_Malloc(dataSize, PU_CACHE, (void **)&patch->data);
241 memset(patch->data, 0, dataSize);
242
243 // set out pixel, column, and post pointers into our data array
244 patch->pixels = patch->data;
245 patch->columns = (rcolumn_t*)((unsigned char*)patch->pixels + pixelDataSize);
246 patch->posts = (rpost_t*)((unsigned char*)patch->columns + columnsDataSize);
247
248 // sanity check that we've got all the memory allocated we need
249 assert((((byte*)patch->posts + numPostsTotal*sizeof(rpost_t)) - (byte*)patch->data) == dataSize);
250
251 memset(patch->pixels, 0xff, (patch->width*patch->height));
252
253 // fill in the pixels, posts, and columns
254 numPostsUsedSoFar = 0;
255 for (x=0; x<patch->width; x++) {
256
257 oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x]));
258
259 if (patch->isNotTileable) {
260 // non-tiling
261 if (x == 0) oldPrevColumn = 0;
262 else oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x-1]));
263 if (x == patch->width-1) oldNextColumn = 0;
264 else oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x+1]));
265 }
266 else {
267 // tiling
268 int prevColumnIndex = x-1;
269 int nextColumnIndex = x+1;
270 while (prevColumnIndex < 0) prevColumnIndex += patch->width;
271 while (nextColumnIndex >= patch->width) nextColumnIndex -= patch->width;
272 oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[prevColumnIndex]));
273 oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[nextColumnIndex]));
274 }
275
276 // setup the column's data
277 patch->columns[x].pixels = patch->pixels + (x*patch->height) + 0;
278 patch->columns[x].numPosts = numPostsInColumn[x];
279 patch->columns[x].posts = patch->posts + numPostsUsedSoFar;
280
281 while (oldColumn->topdelta != 0xff) {
282 // set up the post's data
283 patch->posts[numPostsUsedSoFar].topdelta = oldColumn->topdelta;
284 patch->posts[numPostsUsedSoFar].length = oldColumn->length;
285 patch->posts[numPostsUsedSoFar].slope = 0;
286
287 edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta);
288 if (edgeSlope == 1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_TOP_UP;
289 else if (edgeSlope == -1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_TOP_DOWN;
290
291 edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta+oldColumn->length);
292 if (edgeSlope == 1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_BOT_UP;
293 else if (edgeSlope == -1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_BOT_DOWN;
294
295 // fill in the post's pixels
296 oldColumnPixelData = (const byte *)oldColumn + 3;
297 for (y=0; y<oldColumn->length; y++) {
298 patch->pixels[x * patch->height + oldColumn->topdelta + y] = oldColumnPixelData[y];
299 }
300
301 oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4);
302 numPostsUsedSoFar++;
303 }
304 }
305
306 if (1 || patch->isNotTileable) {
307 const rcolumn_t *column, *prevColumn;
308
309 // copy the patch image down and to the right where there are
310 // holes to eliminate the black halo from bilinear filtering
311 for (x=0; x<patch->width; x++) {
312 //oldColumn = (const column_t *)((const byte *)oldPatch + oldPatch->columnofs[x]);
313
314 column = R_GetPatchColumnClamped(patch, x);
315 prevColumn = R_GetPatchColumnClamped(patch, x-1);
316
317 if (column->pixels[0] == 0xff) {
318 // force the first pixel (which is a hole), to use
319 // the color from the next solid spot in the column
320 for (y=0; y<patch->height; y++) {
321 if (column->pixels[y] != 0xff) {
322 column->pixels[0] = column->pixels[y];
323 break;
324 }
325 }
326 }
327
328 // copy from above or to the left
329 for (y=1; y<patch->height; y++) {
330 //if (getIsSolidAtSpot(oldColumn, y)) continue;
331 if (column->pixels[y] != 0xff) continue;
332
333 // this pixel is a hole
334
335 if (x && prevColumn->pixels[y-1] != 0xff) {
336 // copy the color from the left
337 column->pixels[y] = prevColumn->pixels[y];
338 }
339 else {
340 // copy the color from above
341 column->pixels[y] = column->pixels[y-1];
342 }
343 }
344 }
345
346 // verify that the patch truly is non-rectangular since
347 // this determines tiling later on
348 }
349
350 W_UnlockLumpNum(patchNum);
351 free(numPostsInColumn);
352}
353
354typedef struct {
355 unsigned short patches;
356 unsigned short posts;
357 unsigned short posts_used;
358} count_t;
359
360static void switchPosts(rpost_t *post1, rpost_t *post2) {
361 rpost_t dummy;
362
363 dummy.topdelta = post1->topdelta;
364 dummy.length = post1->length;
365 dummy.slope = post1->slope;
366 post1->topdelta = post2->topdelta;
367 post1->length = post2->length;
368 post1->slope = post2->slope;
369 post2->topdelta = dummy.topdelta;
370 post2->length = dummy.length;
371 post2->slope = dummy.slope;
372}
373
374static void removePostFromColumn(rcolumn_t *column, int post) {
375 int i;
376#ifdef RANGECHECK
377 if (post >= column->numPosts)
378 I_Error("removePostFromColumn: invalid post index");
379#endif
380 if (post < column->numPosts)
381 for (i=post; i<(column->numPosts-1); i++) {
382 rpost_t *post1 = &column->posts[i];
383 rpost_t *post2 = &column->posts[i+1];
384 post1->topdelta = post2->topdelta;
385 post1->length = post2->length;
386 post1->slope = post2->slope;
387 }
388 column->numPosts--;
389}
390
391//---------------------------------------------------------------------------
392static void createTextureCompositePatch(int id) {
393 rpatch_t *composite_patch;
394 texture_t *texture;
395 texpatch_t *texpatch;
396 int patchNum;
397 const patch_t *oldPatch;
398 const column_t *oldColumn, *oldPrevColumn, *oldNextColumn;
399 int i, x, y;
400 int oy, count;
401 int pixelDataSize;
402 int columnsDataSize;
403 int postsDataSize;
404 int dataSize;
405 int numPostsTotal;
406 const unsigned char *oldColumnPixelData;
407 int numPostsUsedSoFar;
408 int edgeSlope;
409 count_t *countsInColumn;
410
411#ifdef RANGECHECK
412 if (id >= numtextures)
413 I_Error("createTextureCompositePatch: %i >= numtextures", id);
414#endif
415
416 composite_patch = &texture_composites[id];
417
418 texture = textures[id];
419
420 composite_patch->width = texture->width;
421 composite_patch->height = texture->height;
422 composite_patch->widthmask = texture->widthmask;
423 composite_patch->leftoffset = 0;
424 composite_patch->topoffset = 0;
425 composite_patch->isNotTileable = 0;
426
427 // work out how much memory we need to allocate for this patch's data
428 pixelDataSize = (composite_patch->width * composite_patch->height + 4) & ~3;
429 columnsDataSize = sizeof(rcolumn_t) * composite_patch->width;
430
431 // count the number of posts in each column
432 countsInColumn = (count_t *)calloc(sizeof(count_t), composite_patch->width);
433 numPostsTotal = 0;
434
435 for (i=0; i<texture->patchcount; i++) {
436 texpatch = &texture->patches[i];
437 patchNum = texpatch->patch;
438 oldPatch = (const patch_t*)W_CacheLumpNum(patchNum);
439
440 for (x=0; x<SHORT(oldPatch->width); x++) {
441 int tx = texpatch->originx + x;
442
443 if (tx < 0)
444 continue;
445 if (tx >= composite_patch->width)
446 break;
447
448 countsInColumn[tx].patches++;
449
450 oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x]));
451 while (oldColumn->topdelta != 0xff) {
452 countsInColumn[tx].posts++;
453 numPostsTotal++;
454 oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4);
455 }
456 }
457
458 W_UnlockLumpNum(patchNum);
459 }
460
461 postsDataSize = numPostsTotal * sizeof(rpost_t);
462
463 // allocate our data chunk
464 dataSize = pixelDataSize + columnsDataSize + postsDataSize;
465 composite_patch->data = (unsigned char*)Z_Malloc(dataSize, PU_STATIC, (void **)&composite_patch->data);
466 memset(composite_patch->data, 0, dataSize);
467
468 // set out pixel, column, and post pointers into our data array
469 composite_patch->pixels = composite_patch->data;
470 composite_patch->columns = (rcolumn_t*)((unsigned char*)composite_patch->pixels + pixelDataSize);
471 composite_patch->posts = (rpost_t*)((unsigned char*)composite_patch->columns + columnsDataSize);
472
473 // sanity check that we've got all the memory allocated we need
474 assert((((byte*)composite_patch->posts + numPostsTotal*sizeof(rpost_t)) - (byte*)composite_patch->data) == dataSize);
475
476 memset(composite_patch->pixels, 0xff, (composite_patch->width*composite_patch->height));
477
478 numPostsUsedSoFar = 0;
479
480 for (x=0; x<texture->width; x++) {
481 // setup the column's data
482 composite_patch->columns[x].pixels = composite_patch->pixels + (x*composite_patch->height);
483 composite_patch->columns[x].numPosts = countsInColumn[x].posts;
484 composite_patch->columns[x].posts = composite_patch->posts + numPostsUsedSoFar;
485 numPostsUsedSoFar += countsInColumn[x].posts;
486 }
487
488 // fill in the pixels, posts, and columns
489 for (i=0; i<texture->patchcount; i++) {
490 texpatch = &texture->patches[i];
491 patchNum = texpatch->patch;
492 oldPatch = (const patch_t*)W_CacheLumpNum(patchNum);
493
494 for (x=0; x<SHORT(oldPatch->width); x++) {
495 int tx = texpatch->originx + x;
496
497 if (tx < 0)
498 continue;
499 if (tx >= composite_patch->width)
500 break;
501
502 oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x]));
503
504 {
505 // tiling
506 int prevColumnIndex = x-1;
507 int nextColumnIndex = x+1;
508 while (prevColumnIndex < 0) prevColumnIndex += SHORT(oldPatch->width);
509 while (nextColumnIndex >= SHORT(oldPatch->width)) nextColumnIndex -= SHORT(oldPatch->width);
510 oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[prevColumnIndex]));
511 oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[nextColumnIndex]));
512 }
513
514 while (oldColumn->topdelta != 0xff) {
515 rpost_t *post = &composite_patch->columns[tx].posts[countsInColumn[tx].posts_used];
516 oldColumnPixelData = (const byte *)oldColumn + 3;
517 oy = texpatch->originy;
518 count = oldColumn->length;
519 // the original renderer had several bugs which we reproduce here
520 if (countsInColumn[tx].patches > 1) {
521 // when there are multiple patches, then we need to handle the
522 // column differently
523 if (i == 0) {
524 // draw first patch at original position, it will be partly
525 // overdrawn below
526 for (y=0; y<count; y++) {
527 int ty = oy + oldColumn->topdelta + y;
528 if (ty < 0)
529 continue;
530 if (ty >= composite_patch->height)
531 break;
532 composite_patch->pixels[tx * composite_patch->height + ty] = oldColumnPixelData[y];
533 }
534 }
535 // do the buggy clipping
536 if ((oy + oldColumn->topdelta) < 0) {
537 count += oy;
538 oy = 0;
539 }
540 } else {
541 // with a single patch only negative y origins are wrong
542 oy = 0;
543 }
544 // set up the post's data
545 post->topdelta = oldColumn->topdelta + oy;
546 post->length = count;
547 if ((post->topdelta + post->length) > composite_patch->height) {
548 if (post->topdelta > composite_patch->height)
549 post->length = 0;
550 else
551 post->length = composite_patch->height - post->topdelta;
552 }
553 if (post->topdelta < 0) {
554 if ((post->topdelta + post->length) <= 0)
555 post->length = 0;
556 else
557 post->length -= post->topdelta;
558 post->topdelta = 0;
559 }
560 post->slope = 0;
561
562 edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta);
563 if (edgeSlope == 1) post->slope |= RDRAW_EDGESLOPE_TOP_UP;
564 else if (edgeSlope == -1) post->slope |= RDRAW_EDGESLOPE_TOP_DOWN;
565
566 edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta+count);
567 if (edgeSlope == 1) post->slope |= RDRAW_EDGESLOPE_BOT_UP;
568 else if (edgeSlope == -1) post->slope |= RDRAW_EDGESLOPE_BOT_DOWN;
569
570 // fill in the post's pixels
571 for (y=0; y<count; y++) {
572 int ty = oy + oldColumn->topdelta + y;
573 if (ty < 0)
574 continue;
575 if (ty >= composite_patch->height)
576 break;
577 composite_patch->pixels[tx * composite_patch->height + ty] = oldColumnPixelData[y];
578 }
579
580 oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4);
581 countsInColumn[tx].posts_used++;
582 assert(countsInColumn[tx].posts_used <= countsInColumn[tx].posts);
583 }
584 }
585
586 W_UnlockLumpNum(patchNum);
587 }
588
589 for (x=0; x<texture->width; x++) {
590 rcolumn_t *column;
591
592 if (countsInColumn[x].patches <= 1)
593 continue;
594
595 // cleanup posts on multipatch columns
596 column = &composite_patch->columns[x];
597
598 i = 0;
599 while (i<(column->numPosts-1)) {
600 rpost_t *post1 = &column->posts[i];
601 rpost_t *post2 = &column->posts[i+1];
602 int length;
603
604 if ((post2->topdelta - post1->topdelta) < 0)
605 switchPosts(post1, post2);
606
607 if ((post1->topdelta + post1->length) >= post2->topdelta) {
608 length = (post1->length + post2->length) - ((post1->topdelta + post1->length) - post2->topdelta);
609 if (post1->length < length) {
610 post1->slope = post2->slope;
611 post1->length = length;
612 }
613 removePostFromColumn(column, i+1);
614 i = 0;
615 continue;
616 }
617 i++;
618 }
619 }
620
621 if (1 || composite_patch->isNotTileable) {
622 const rcolumn_t *column, *prevColumn;
623
624 // copy the patch image down and to the right where there are
625 // holes to eliminate the black halo from bilinear filtering
626 for (x=0; x<composite_patch->width; x++) {
627 //oldColumn = (const column_t *)((const byte *)oldPatch + oldPatch->columnofs[x]);
628
629 column = R_GetPatchColumnClamped(composite_patch, x);
630 prevColumn = R_GetPatchColumnClamped(composite_patch, x-1);
631
632 if (column->pixels[0] == 0xff) {
633 // force the first pixel (which is a hole), to use
634 // the color from the next solid spot in the column
635 for (y=0; y<composite_patch->height; y++) {
636 if (column->pixels[y] != 0xff) {
637 column->pixels[0] = column->pixels[y];
638 break;
639 }
640 }
641 }
642
643 // copy from above or to the left
644 for (y=1; y<composite_patch->height; y++) {
645 //if (getIsSolidAtSpot(oldColumn, y)) continue;
646 if (column->pixels[y] != 0xff) continue;
647
648 // this pixel is a hole
649
650 if (x && prevColumn->pixels[y-1] != 0xff) {
651 // copy the color from the left
652 column->pixels[y] = prevColumn->pixels[y];
653 }
654 else {
655 // copy the color from above
656 column->pixels[y] = column->pixels[y-1];
657 }
658 }
659 }
660
661 // verify that the patch truly is non-rectangular since
662 // this determines tiling later on
663 }
664
665 free(countsInColumn);
666}
667
668//---------------------------------------------------------------------------
669const rpatch_t *R_CachePatchNum(int id) {
670 const int locks = 1;
671
672 if (!patches)
673 I_Error("R_CachePatchNum: Patches not initialized");
674
675#ifdef RANGECHECK
676 if (id >= numlumps)
677 I_Error("createPatch: %i >= numlumps", id);
678#endif
679
680 if (!patches[id].data)
681 createPatch(id);
682
683 /* cph - if wasn't locked but now is, tell z_zone to hold it */
684 if (!patches[id].locks && locks) {
685 Z_ChangeTag(patches[id].data,PU_STATIC);
686#ifdef TIMEDIAG
687 patches[id].locktic = gametic;
688#endif
689 }
690 patches[id].locks += locks;
691
692#ifdef SIMPLECHECKS
693 if (!((patches[id].locks+1) & 0xf))
694 lprintf(LO_DEBUG, "R_CachePatchNum: High lock on %8s (%d)\n",
695 lumpinfo[id].name, patches[id].locks);
696#endif
697
698 return &patches[id];
699}
700
701void R_UnlockPatchNum(int id)
702{
703 const int unlocks = 1;
704#ifdef SIMPLECHECKS
705 if ((signed short)patches[id].locks < unlocks)
706 lprintf(LO_DEBUG, "R_UnlockPatchNum: Excess unlocks on %8s (%d-%d)\n",
707 lumpinfo[id].name, patches[id].locks, unlocks);
708#endif
709 patches[id].locks -= unlocks;
710 /* cph - Note: must only tell z_zone to make purgeable if currently locked,
711 * else it might already have been purged
712 */
713 if (unlocks && !patches[id].locks)
714 Z_ChangeTag(patches[id].data, PU_CACHE);
715}
716
717//---------------------------------------------------------------------------
718const rpatch_t *R_CacheTextureCompositePatchNum(int id) {
719 const int locks = 1;
720
721 if (!texture_composites)
722 I_Error("R_CacheTextureCompositePatchNum: Composite patches not initialized");
723
724#ifdef RANGECHECK
725 if (id >= numtextures)
726 I_Error("createTextureCompositePatch: %i >= numtextures", id);
727#endif
728
729 if (!texture_composites[id].data)
730 createTextureCompositePatch(id);
731
732 /* cph - if wasn't locked but now is, tell z_zone to hold it */
733 if (!texture_composites[id].locks && locks) {
734 Z_ChangeTag(texture_composites[id].data,PU_STATIC);
735#ifdef TIMEDIAG
736 texture_composites[id].locktic = gametic;
737#endif
738 }
739 texture_composites[id].locks += locks;
740
741#ifdef SIMPLECHECKS
742 if (!((texture_composites[id].locks+1) & 0xf))
743 lprintf(LO_DEBUG, "R_CacheTextureCompositePatchNum: High lock on %8s (%d)\n",
744 textures[id]->name, texture_composites[id].locks);
745#endif
746
747 return &texture_composites[id];
748
749}
750
751void R_UnlockTextureCompositePatchNum(int id)
752{
753 const int unlocks = 1;
754#ifdef SIMPLECHECKS
755 if ((signed short)texture_composites[id].locks < unlocks)
756 lprintf(LO_DEBUG, "R_UnlockTextureCompositePatchNum: Excess unlocks on %8s (%d-%d)\n",
757 textures[id]->name, texture_composites[id].locks, unlocks);
758#endif
759 texture_composites[id].locks -= unlocks;
760 /* cph - Note: must only tell z_zone to make purgeable if currently locked,
761 * else it might already have been purged
762 */
763 if (unlocks && !texture_composites[id].locks)
764 Z_ChangeTag(texture_composites[id].data, PU_CACHE);
765}
766
767//---------------------------------------------------------------------------
768const rcolumn_t *R_GetPatchColumnWrapped(const rpatch_t *patch, int columnIndex) {
769 while (columnIndex < 0) columnIndex += patch->width;
770 columnIndex %= patch->width;
771 return &patch->columns[columnIndex];
772}
773
774//---------------------------------------------------------------------------
775const rcolumn_t *R_GetPatchColumnClamped(const rpatch_t *patch, int columnIndex) {
776 if (columnIndex < 0) columnIndex = 0;
777 if (columnIndex >= patch->width) columnIndex = patch->width-1;
778 return &patch->columns[columnIndex];
779}
780
781//---------------------------------------------------------------------------
782const rcolumn_t *R_GetPatchColumn(const rpatch_t *patch, int columnIndex) {
783 if (patch->isNotTileable) return R_GetPatchColumnClamped(patch, columnIndex);
784 else return R_GetPatchColumnWrapped(patch, columnIndex);
785}
786