]> git.bts.cx Git - benzene.git/blob - src/bz/gfx/gfx.c
0f26e3bdb0193a74843996e9d6a2688bb2adedd0
[benzene.git] / src / bz / gfx / gfx.c
1 #include <bz/gfx/gfx_internal.h>
2
3 #include <bz/gfx/font_internal.h>
4 #include <bz/math/math.h>
5 #include <bz/memory/allocator.h>
6
7 #include <stdlib.h>
8 #include <string.h> // memset.....
9
10 // http://members.chello.at/~easyfilter/Bresenham.pdf
11
12 size_t bufferStride = 0;
13 size_t bufferStrideShift = 0;
14 static uint8_t *drawBuffer;
15 static uint8_t *paletteBuffer;
16 static uint8_t *maskBuffer;
17 static uint8_t *overlayBuffer;
18
19 uint8_t *bzGfxCompositedBuffer;
20
21 static uint8_t *currentBuffer;
22
23 static int32_t canvasWidth = 0; // These need to be signed because of comparisons (don't want to keep casting...)
24 static int32_t canvasHeight = 0;
25
26 static float cameraX = 0;
27 static float cameraY = 0;
28 static int cameraXInt = 0;
29 static int cameraYInt = 0;
30 static uint16_t fillPattern;
31
32 static BZMatrix globalViewMatrix = { .a = 1, .b = 0, .c = 0, .d = 1, .x = 0, .y = 0 };
33 //static BZMatrix globalLocalMatrix = { .a = 1, .b = 0, .c = 0, .d = 1, .x = 0, .y = 0 };
34
35 //static bool fillPattern[4][4];
36
37 #define _bzBufferLocation(buffer, x, y) buffer[((size_t)(y) << bufferStrideShift) + (size_t)(x)]
38 #define fillPatternBit(x,y) (((fillPattern >> (12 - (((y + cameraYInt) & 0b11) * 4)) >> (3 - ((x + cameraXInt) & 0b11))) & 1) * 255)
39 #define patternVal(buffer, x,y,c) ((_bzBufferLocation(buffer, x, y) & ~(fillPatternBit(x, y))) | ((c) & fillPatternBit(x, y)))
40
41 //#define _bzBufferSet(buffer, x, y, value) buffer[((y) << bufferStrideShift) + (x)] = (value)
42 #define _bzBufferSet(buffer, x, y, value) _bzBufferLocation(buffer, x, y) = patternVal(buffer, x, y, value)
43 #define _bzBufferSetSafe(buffer, x, y, value) { if ((x)>= 0 && (x)<canvasWidth && (y)>= 0 && (y)<canvasHeight) _bzBufferSet(buffer, x, y, value); }
44 #define _bzBufferSetAssert(buffer, x, y, value) { bzAssert(buffer!=NULL);bzAssertMessage((x)>= 0 && (x)<canvasWidth, "invalid x: %d   %s, %d", x, __FILE__, __LINE__);bzAssertMessage((y)>= 0 && (y)<canvasHeight, "invalid y: %d   %s, %d", y, __FILE__, __LINE__);_bzBufferSet(buffer, x, y, value); }
45 #define _bzBufferSetNull(buffer, x, y, value) {}
46
47 #define bzBufferSet _bzBufferSet
48
49 #define swap(a, b) { typeof(a) tmp = b; b = a; a = tmp; }
50
51 #define bresenhamVariables(id, x0, y0, x1, y1) \
52         int id##xEnd = x1, id##yEnd = y1; \
53         int id##deltaX = bzAbs(x1 - x0); \
54         int id##deltaY = -bzAbs(y1 - y0); \
55         int id##signX = bzSgn(x1 - x0); \
56         int id##signY = bzSgn(y1 - y0); \
57         int id##err = id##deltaX + id##deltaY; \
58         int id##x = x0, id##y = y0;
59
60 #define bresenhamStep(id) \
61         int id##e2 = id##err << 1; \
62         if (id##e2 >= id##deltaY) { \
63                 if (id##x == id##xEnd) break; \
64                 id##err += id##deltaY; \
65                 id##x += id##signX; \
66         } \
67         if (id##e2 <= id##deltaX) { \
68                 if (id##y == id##yEnd) break; \
69                 id##err += id##deltaX; \
70                 id##y += id##signY; \
71         }
72
73 //uint32_t intermediateBuffer[canvasHeight][canvasWidth];
74
75 #define SS_WIDTH 128
76 #define SS_HEIGHT 128
77 #define kSpriteSheetStrideShift 7
78
79 //static uint8_t spritesheet[SS_HEIGHT][SS_WIDTH];
80 static uint8_t *spritesheet = NULL;
81
82 static BZFont *currentFont;
83
84 /*uint8_t palettes[16][16] = {
85         {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
86         {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
87         {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
88         {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
89         {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
90         {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
91         {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
92         {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
93 };*/
94
95 static size_t gPaletteCount;
96 static size_t gPaletteColorCount;
97 static uint8_t *gPalettes;
98
99 //BZRect bzCalculateCullRect() {
100 //      return bzRectMake(cameraX, cameraY, canvasWidth, SS_HEIGHT);
101 //}
102
103 void bzGfxPreparePalettes(BZMemoryArenaID arena, size_t paletteCount, size_t colorCount) {
104         gPaletteCount = paletteCount;
105         gPaletteColorCount = colorCount;
106         gPalettes = bzMemoryAlloc(arena, gPaletteCount * gPaletteColorCount * sizeof(uint8_t));
107
108         for (size_t p = 0, i = 0; p < gPaletteCount; ++p) {
109                 for (size_t c = 0; c < gPaletteColorCount; ++c, ++i) {
110                         gPalettes[i] = c;
111                 }
112         }
113 }
114
115 void bzGfxPrepareCanvasBuffer(BZMemoryArenaID arena, size_t width, size_t height) {
116         canvasWidth = width;
117         canvasHeight = height;
118
119         bufferStrideShift = __SIZE_WIDTH__ - __builtin_clzl(canvasWidth - 1);
120         bufferStride = 1 << bufferStrideShift;
121
122         size_t canvasMemorySize = bufferStride * height * sizeof(uint8_t);
123         drawBuffer = bzMemoryAlloc(arena, canvasMemorySize);
124         paletteBuffer = bzMemoryAlloc(arena, canvasMemorySize);
125         maskBuffer = bzMemoryAlloc(arena, canvasMemorySize);
126         overlayBuffer = bzMemoryAlloc(arena, canvasMemorySize);
127         bzGfxCompositedBuffer = bzMemoryAlloc(arena, canvasMemorySize);
128
129         bzSetOutputBuffer(0);
130
131         bzLog("Data: %d %d %d %d", canvasWidth, canvasMemorySize, bufferStrideShift, bufferStride);
132 }
133
134 void bzGfxPrepareSpritesheet(BZMemoryArenaID arena, size_t width, size_t height, void *data) {
135         uint8_t *imageData = (uint8_t *)data;
136         bzLog("Preparing spritesheet %dx%d", width, height);
137
138         size_t spritesheetMemorySize = width * height * sizeof(uint8_t);
139         spritesheet = bzMemoryAlloc(arena, spritesheetMemorySize);
140         memcpy(spritesheet, data, spritesheetMemorySize);
141 }
142
143 void bzGfxPrepareFont(BZMemoryArenaID arena, void *font) {
144         currentFont = font;
145 }
146
147 static bool prepareFrame(int *minXOut, int *minYOut, int *maxXOut, int *maxYOut, int x0, int y0, int x1, int y1, int *clippedLeftX, int *clippedTopY, int *clippedRightX, int *clippedBottomY) {
148         int xMin = x0, xMax = x1;
149         int yMin = y0, yMax = y1;
150
151         if (x1 < x0) {
152                 xMin = x1;
153                 xMax = x0;
154         }
155
156         if (y1 < y0) {
157                 yMin = y1;
158                 yMax = y0;
159         }
160
161         int xMinOut = xMin/* - cameraX*/, xMaxOut = xMax/* - cameraX*/;
162         int yMinOut = yMin/* - cameraY*/, yMaxOut = yMax/* - cameraY*/;
163         
164         if (xMaxOut < 0 || yMaxOut < 0 || xMinOut >= canvasWidth || yMinOut >= canvasHeight) {
165                 return false;
166         }
167
168         int safeXMinOut = bzMax(0, xMinOut);
169         int safeXMaxOut = bzMin(canvasWidth - 1, xMaxOut);
170         int safeYMinOut = bzMax(0, yMinOut);
171         int safeYMaxOut = bzMin(canvasHeight - 1, yMaxOut);
172
173         *minXOut = safeXMinOut;
174         *minYOut = safeYMinOut;
175         *maxXOut = safeXMaxOut;
176         *maxYOut = safeYMaxOut;
177
178         if (clippedLeftX != NULL) {
179                 *clippedLeftX = safeXMinOut - xMinOut;
180                 *clippedTopY  = safeYMinOut - yMinOut;
181
182                 *clippedRightX  = xMaxOut - safeXMaxOut;
183                 *clippedBottomY = yMaxOut - safeYMaxOut;
184         }
185
186         return true;
187 }
188
189 static bool preparePositionXY(int *xOut, int *yOut, float x, float y) {
190         //BZVector origin = bzVectorMake(, );
191         //bzMatrixTransformVector(&origin, &origin, &globalViewMatrix);
192         BZVector position = bzVectorMake(x, y);
193         bzMatrixTransformVector/*bzMatrixTranslateVector*/(&position, &position, &globalViewMatrix);
194         //bzMatrixTransformVector(&position, &position, &globalViewMatrix);
195         //bzVectorAdd(&position, &origin, &position);
196         *xOut = bzFloor(position.x - cameraX);
197         *yOut = bzFloor(position.y - cameraY);
198         return true;
199 }
200
201 static bool prepareSizeXY(int *xOut, int *yOut, float x, float y) {
202         BZVector size = bzVectorMake(x, y);
203         bzMatrixScaleRotateVector(&size, &size, &globalViewMatrix);
204         *xOut = bzFloor(size.x);
205         if (yOut != NULL) *yOut = bzFloor(size.y);
206         return size.x > 0 && size.y > 0;
207 }
208
209 static bool prepareRadius(int *rOut, float r) {
210         BZVector size = bzVectorMake(r, 0);
211         bzMatrixScaleRotateVector(&size, &size, &globalViewMatrix);
212         *rOut = bzFloor(bzAbs(size.x) + bzAbs(size.y));
213         return *rOut > 0;
214 }
215
216 static bool prepareColor(int *colorOut, int color) {
217         *colorOut = gPalettes[color % gPaletteColorCount]; // FIXME
218         return true;
219         /*int c = (color < gPaletteColorCount) ? gPalettes[color] : color;
220         if (c > 0) {
221                 *colorOut = c & globalBlendColor;
222                 return true;
223         } else {
224                 return false;
225         }*/
226 }
227
228 void bzPSet(BZCoordinate x, BZCoordinate y, BZPaletteColor c) {
229         //if (c == 0) return;
230
231         //c = ((c < gPaletteColorCount) ? gPalettes[c] : c);
232
233         prepareColor(&c, c);
234
235         int ox, oy;
236         preparePositionXY(&ox, &oy, x, y);
237         if (ox >= 0 && ox < canvasWidth && oy >=0 && oy < canvasHeight) {
238                 bzBufferSet(currentBuffer, ox, oy, c);
239         }
240 }
241
242 BZPaletteColor bzSGet(int x, int y) {
243         bzAssert(spritesheet != NULL);
244         if (x >= 0 && x < SS_WIDTH && y >=0 && y < SS_HEIGHT) {
245                 return spritesheet[(y << kSpriteSheetStrideShift) + x];
246         } else {
247                 return 0;
248         }
249 }
250
251 //void bzSSet(int x, int y, int c);
252
253 //bool bzFGet(int n, int f);
254 //void bzFSet(int n, int f, bool v);
255
256 void bzCls(BZPaletteColor c) {
257         memset(currentBuffer, c, bufferStride * canvasHeight);
258
259         //memset(&buffer[currentBuffer], c, BUFFER_WIDTH * canvasHeight);
260
261 //      for (size_t i = 0; i < )
262
263 //      for (size_t y = 0; y < canvasHeight; ++y) {
264 //              for (size_t x = 0; x < canvasWidth; ++x) {
265 //                      bzBufferSet(currentBuffer, x, y, c);
266 //              }
267 //      }
268
269         //bzCamera(0, 0);
270         bzFillP(0x0000);
271 }
272
273 void bzGetCamera(BZCoordinate *x, BZCoordinate *y) {
274         if (x != NULL) *x = cameraX;
275         if (y != NULL) *y = cameraY;
276 }
277
278 void bzCamera(BZCoordinate x, BZCoordinate y) {
279         cameraX = x;
280         cameraY = y;
281         cameraXInt = bzFloor(cameraX);
282         cameraYInt = bzFloor(cameraY);
283 }
284
285 void bzCirc(BZCoordinate xm, BZCoordinate ym, BZCoordinate r, BZPaletteColor c) {
286         int oxm, oym, or, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut;
287         if (preparePositionXY(&oxm, &oym, xm, ym) && prepareRadius(&or, r) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, oxm - or, oym - or, oxm + or, oym + or, NULL, NULL, NULL, NULL)) {
288                 int x = -or;
289                 int y = 0;
290                 int err = 2 - 2 * or;
291
292                 do {
293                         int x0 = oxm-x;
294                         int y0 = oym+y;
295                         if (x0 >= 0 && x0 < canvasWidth && y0 >=0 && y0 < canvasHeight) { // FIXME, easier way to offset this...
296                                 bzBufferSet(currentBuffer, x0, y0, cOut);
297                         }
298
299                         int x1 = oxm-y;
300                         int y1 = oym-x;
301                         if (x1 >= 0 && x1 < canvasWidth && y1 >=0 && y1 < canvasHeight) { // FIXME, easier way to offset this...
302                                 bzBufferSet(currentBuffer, x1, y1, cOut);
303                         }
304
305                         int x2 = oxm+x;
306                         int y2 = oym-y;
307                         if (x2 >= 0 && x2 < canvasWidth && y2 >=0 && y2 < canvasHeight) { // FIXME, easier way to offset this...
308                                 bzBufferSet(currentBuffer, x2, y2, cOut);
309                         }
310
311                         int x3 = oxm+y;
312                         int y3 = oym+x;
313                         if (x3 >= 0 && x3 < canvasWidth && y3 >=0 && y3 < canvasHeight) { // FIXME, easier way to offset this...
314                                 bzBufferSet(currentBuffer, x3, y3, cOut);
315                         }
316
317                         or = err;
318
319                         if (or <= y) err += (++y << 1) + 1;
320                         if (or > x || err > y) err += (++x << 1) + 1;
321                 } while (x <= 0);
322         }
323 }
324
325 void bzCircFill(BZCoordinate xm, BZCoordinate ym, BZCoordinate r, BZPaletteColor c) {
326         int oxm, oym, or, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut;
327         if (preparePositionXY(&oxm, &oym, xm, ym) && prepareRadius(&or, r) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, oxm - or, oym - or, oxm + or, oym + or, NULL, NULL, NULL, NULL)) {
328                 int x = -or;
329                 int y = 0;
330                 int err = 2 - 2 * or;
331
332                 do {
333                         int fy1 = oym - bzAbs(y);
334                         int fy2 = oym + bzAbs(y);
335                         int fx  = bzMax(oxm - bzAbs(x), 0);
336                         int tx  = bzMin(oxm + bzAbs(x), canvasWidth - 1); // FIXME, not as many abs
337
338                         bool validFy1 = fy1 >= 0 && fy1 < canvasHeight;
339                         bool validFy2 = fy2 >= 0 && fy2 < canvasHeight;
340
341                         if (validFy1 && validFy2) {
342                                 for (int px = fx; px <= tx; ++px) {
343                                         bzBufferSet(currentBuffer, px, fy1, cOut); // FIXME, speed
344                                         bzBufferSet(currentBuffer, px, fy2, cOut);
345                                 }
346                         } else if (validFy1) {
347                                 for (int px = fx; px <= tx; ++px) {
348                                         bzBufferSet(currentBuffer, px, fy1, cOut);
349                                 }
350                         } else if (validFy2) {
351                                 for (int px = fx; px <= tx; ++px) {
352                                         bzBufferSet(currentBuffer, px, fy2, cOut);
353                                 }
354                         }
355
356                         or = err;
357
358                         if (or <= y) err += (++y << 1) + 1;
359                         if (or > x || err > y) err += (++x << 1) + 1;
360                 } while (x <= 0);
361         }
362 }
363
364 //void bzOval(int x0, int y0, int x1, int y1, int c);
365 //void bzOvalFill(int x0, int y0, int x1, int y1, int c);
366
367 void bzLine(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c) {
368         int ox0, oy0, ox1, oy1, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
369         if (preparePositionXY(&ox0, &oy0, x0, y0) && preparePositionXY(&ox1, &oy1, x1, y1) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, ox0, oy0, ox1, oy1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
370                 bresenhamVariables(line_, ox0, oy0, ox1, oy1)
371
372                 for (;;) {
373                         if (line_x >= 0 && line_x < canvasWidth && line_y >=0 && line_y < canvasHeight) { // FIXME, easier way to offset this...
374                                 bzBufferSet(currentBuffer, line_x, line_y, cOut);
375                         }
376
377                         bresenhamStep(line_)
378                 }
379         }
380 }
381
382 void bzRect(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c) {
383         int ox0, oy0, ox1, oy1, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
384         if (preparePositionXY(&ox0, &oy0, x0, y0) && preparePositionXY(&ox1, &oy1, x1, y1) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, ox0, oy0, ox1, oy1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
385                 if (clipLeft == 0 && clipRight == 0) {
386                         for (int py = yMinOut; py <= yMaxOut; ++py) {
387                                 bzBufferSet(currentBuffer, xMinOut, py, cOut);
388                                 bzBufferSet(currentBuffer, xMaxOut, py, cOut);
389                         }
390                 } else if (clipLeft == 0) {
391                         for (int py = yMinOut; py <= yMaxOut; ++py) {
392                                 bzBufferSet(currentBuffer, xMinOut, py, cOut);
393                         }
394                 } else if (clipRight == 0) {
395                         for (int py = yMinOut; py <= yMaxOut; ++py) {
396                                 bzBufferSet(currentBuffer, xMaxOut, py, cOut);
397                         }
398                 }
399
400                 if (clipTop == 0 && clipBottom == 0) {
401                         for (int px = xMinOut; px <= xMaxOut; ++px) {
402                                 bzBufferSet(currentBuffer, px, yMinOut, cOut);
403                                 bzBufferSet(currentBuffer, px, yMaxOut, cOut);
404                         }
405                 } else if (clipTop == 0) {
406                         for (int px = xMinOut; px <= xMaxOut; ++px) {
407                                 bzBufferSet(currentBuffer, px, yMinOut, cOut);
408                         }
409                 } else if (clipBottom == 0) {
410                         for (int px = xMinOut; px <= xMaxOut; ++px) {
411                                 bzBufferSet(currentBuffer, px, yMaxOut, cOut);
412                         }
413                 }
414         }
415 }
416
417 void bzRectFill(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c) {
418         int ox0, oy0, ox1, oy1, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
419         if (preparePositionXY(&ox0, &oy0, x0, y0) && preparePositionXY(&ox1, &oy1, x1, y1) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, ox0, oy0, ox1, oy1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
420                 for (int py = yMinOut; py <= yMaxOut; ++py) {
421                         for (int px = xMinOut; px <= xMaxOut; ++px) {
422                                 bzBufferSet(currentBuffer, px, py, cOut);
423                         }
424                 }
425         }
426 }
427
428 void bzTriangle(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZCoordinate x2, BZCoordinate y2, BZPaletteColor c) {
429         int ox0, oy0, ox1, oy1, ox2, oy2;
430         
431         preparePositionXY(&ox0, &oy0, x0, y0);
432         preparePositionXY(&ox1, &oy1, x1, y1);
433         preparePositionXY(&ox2, &oy2, x2, y2);
434
435         if (oy2 < oy1 || (oy2 == oy1 && ox2 < ox1)) {
436                 swap(ox1, ox2);
437                 swap(oy1, oy2);
438         }
439
440         // after this, x0&y0 are always the smallest
441         if (oy1 < oy0 || (oy1 == oy0 && ox1 < ox0)) {
442                 swap(ox0, ox1);
443                 swap(oy0, oy1);
444         }
445
446         // one final comparison to swap for mid values
447         if (oy2 < oy1 || (oy2 == oy1 && ox2 < ox1)) {
448                 swap(ox1, ox2);
449                 swap(oy1, oy2);
450         }
451
452         // The above sorts pairs by Y position, but X isn't sorted
453         int minX = bzMin(bzMin(ox0, ox1), ox2);
454         int maxX = bzMax(bzMax(ox0, ox1), ox2);
455
456         int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
457         if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, minX, oy0, maxX, oy2, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
458                 bresenhamVariables(triangleLow_,  ox0, oy0, ox1, oy1)
459                 bresenhamVariables(triangleMid_,  ox1, oy1, ox2, oy2)
460                 bresenhamVariables(triangleHigh_, ox0, oy0, ox2, oy2)
461
462                 for (;;) {
463                         int targetY = triangleLow_y;
464                         
465                         int fromX1 = triangleLow_x, fromX2 = triangleLow_x;
466                         while (triangleLow_y == targetY) {
467                                 fromX1 = triangleLow_x, fromX2 = triangleLow_x;
468                                 bresenhamStep(triangleLow_)
469                         }
470
471                         int toX1 = triangleHigh_x, toX2 = triangleHigh_x;
472                         targetY = triangleHigh_y;
473                         while (triangleHigh_y == targetY) {
474                                 toX1 = triangleHigh_x, toX2 = triangleHigh_x;
475                                 bresenhamStep(triangleHigh_)
476                         }
477                         
478                         if (targetY >=0 && targetY < canvasHeight) {
479                                 int fromX, toX;
480
481                                 if (fromX1 < toX2) {
482                                         fromX = bzMin(fromX1, fromX2);
483                                         toX = bzMax(toX1, toX2);
484                                 } else {
485                                         fromX = bzMin(toX1, toX2);
486                                         toX = bzMax(fromX1, fromX2);
487                                 }
488
489                                 if (!((fromX < 0 && toX < 0) || (fromX >= canvasWidth && toX >= canvasWidth))) {
490                                         fromX = bzMax(0, bzMin(fromX, canvasWidth - 1));
491                                         toX = bzMax(0, bzMin(toX, canvasWidth - 1));
492
493                                         for (int x = fromX; x <= toX; ++x) {
494                                                 bzBufferSet(currentBuffer, x, targetY, cOut);
495                                         }
496                                 }
497                         }
498
499                         if (targetY >= oy1) {
500                                 break;
501                         }
502                 }
503
504                 for (;;) {
505                         int targetY = triangleMid_y;
506                         
507                         int fromX1 = triangleMid_x, fromX2 = triangleMid_x;
508                         while (triangleMid_y == targetY) {
509                                 fromX1 = triangleMid_x, fromX2 = triangleMid_x;
510                                 bresenhamStep(triangleMid_)
511                         }
512
513                         int toX1 = triangleHigh_x, toX2 = triangleHigh_x;
514                         targetY = triangleHigh_y;
515                         while (triangleHigh_y == targetY) {
516                                 toX1 = triangleHigh_x, toX2 = triangleHigh_x;
517                                 bresenhamStep(triangleHigh_)
518                         }
519                         
520                         if (targetY >=0 && targetY < canvasHeight) {
521                                 int fromX, toX;
522
523                                 if (fromX1 < toX2) {
524                                         fromX = bzMin(fromX1, fromX2);
525                                         toX = bzMax(toX1, toX2);
526                                 } else {
527                                         fromX = bzMin(toX1, toX2);
528                                         toX = bzMax(fromX1, fromX2);
529                                 }
530
531                                 if (!((fromX < 0 && toX < 0) || (fromX >= canvasWidth && toX >= canvasWidth))) {
532                                         fromX = bzMax(0, bzMin(fromX, canvasWidth - 1));
533                                         toX = bzMax(0, bzMin(toX, canvasWidth - 1));
534
535                                         for (int x = fromX; x <= toX; ++x) {
536                                                 bzBufferSet(currentBuffer, x, targetY, cOut);
537                                         }
538                                 }
539                         }
540
541                         if (targetY >= oy2) {
542                                 break;
543                         }
544                 }
545         }
546 }
547
548 void bzSpr(size_t n, BZCoordinate x, BZCoordinate y) {
549         bzSprExt(n, x, y, 1, 1, false, false);
550 }
551
552 void bzSprExt(size_t n, BZCoordinate x, BZCoordinate y, BZCoordinate w, BZCoordinate h, bool flipX, bool flipY) {
553         int px = (n % 16) * 8;
554         int py = (n / 16) * 8;
555         int pw = w * 8;
556         int ph = h * 8;
557         bzSSprExt(px, py, pw, ph, x, y, pw, ph, flipX, flipY);
558 }
559
560 void bzSSpr(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate sh, BZCoordinate dx, BZCoordinate dy) {
561         bzSSprExt(sx, sy, sw, sh, dx, dy, sw, sh, false, false);
562 }
563
564 void bzSSprExt(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate sh, BZCoordinate dx, BZCoordinate dy, BZCoordinate dw, BZCoordinate dh, bool flipX, bool flipY) {
565         bzAssert(spritesheet != NULL);
566         int odx, ody, odw, odh, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
567         if (preparePositionXY(&odx, &ody, dx, dy) && prepareSizeXY(&odw, &odh, dw, dh) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, odx, ody, odx + odw - 1, ody + odh - 1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
568                 int osx = bzFloor(sx);
569                 int osy = bzFloor(sy);
570                 int osw = bzFloor(sw);
571                 int osh = bzFloor(sh);
572
573                 for (size_t y = 0; y < osh - (clipTop + clipBottom); ++y) {
574                         for (size_t x = 0; x < osw - (clipLeft + clipRight); ++x) {
575                                 int color = gPalettes[spritesheet[((clipTop+osy+y) << kSpriteSheetStrideShift) + clipLeft+osx+x]];
576                                 if (color > 0) bzBufferSet(currentBuffer, xMinOut+x, yMinOut+y, color); // FIXME, scaled up and 0 check removal??
577                         }
578                 }
579         }
580         
581         /*int fromX = 0;
582         int toX = sw;
583         int dX = 1;
584
585         if (flipX) {
586                 fromX = toX - 1;
587                 toX = -1;
588                 dX = -1;
589         }
590
591         int fromY = 0;
592         int toY = sh;
593         int dY = 1;
594
595         if (flipY) {
596                 fromY = toY - 1;
597                 toY = -1;
598                 dY = -1;
599         }
600
601         for (int rY = fromY, wY = dy; rY != toY; rY += dY, ++wY) {
602                 for (int rX = fromX, wX = dx; rX != toX; rX += dX, ++wX) {
603                         bzPSet(wX, wY, bzSGet(sx + rX, sy + rY));
604                 }
605         }*/
606 }
607
608 void bzFillP(BZPattern p) {
609         fillPattern = ~p;
610 }
611
612 static void bzPrintPSet(int x, int y, int c) {
613         if (x >= 0 && x < canvasWidth && y >=0 && y < canvasHeight) {
614                 //c = ((c < gPaletteColorCount) ? gPalettes[c] : c);
615                 prepareColor(&c, c);
616                 bzBufferSet(currentBuffer, x, y, c);
617         }
618 }
619
620 void bzPrint(BZCoordinate x, BZCoordinate y, BZPaletteColor color, const char *text) {
621         int ox, oy;
622         preparePositionXY(&ox, &oy, x, y);
623         bzGfxRenderFont(bzPrintPSet, &ox, &oy, currentFont, (int)color, text);
624         //if (preparePositionXY(&ox, &oy, x, y)) {
625         //      bzGfxRenderFont(bzPSet, &ox, &oy, currentFont, (int)color, text);
626                 // FIXME, this won't work
627         //}
628 }
629
630 void bzSetPaletteColor(size_t palette, BZPaletteColor colorIdx, BZPaletteColor color) {
631         gPalettes[(palette * gPaletteColorCount) + (colorIdx % gPaletteColorCount)] = color;
632 }
633
634 void bzSetGlobalViewMatrix(const BZMatrix *mtx) {
635         bzMatrixCopy(&globalViewMatrix, mtx);
636 }
637
638 //void bzSetGlobalLocalMatrix(const BZMatrix *mtx) {
639 //      bzMatrixCopy(&globalLocalMatrix, mtx);
640 //}
641
642 //void bzSetGlobalBlendColor(BZPaletteColor color) {
643 //      globalBlendColor = color;
644 //}
645
646 void bzSetOutputBuffer(size_t idx) {
647         bzAssert(idx >= 0 && idx < 4);
648
649         switch (idx) {
650                 case 0:
651                         currentBuffer = drawBuffer;
652                         break;
653                 
654                 case 1:
655                         currentBuffer = paletteBuffer;
656                         break;
657
658                 case 2:
659                         currentBuffer = maskBuffer;
660                         break;
661
662                 case 3:
663                         currentBuffer = overlayBuffer;
664                         break;
665
666                 default: // FIXME
667                         bzError("invalid output buffer");
668                         break;
669         }
670 }
671
672 void bzGfxComposite() {
673         bzPerfTimerStart("game.blit.composite");
674
675         uint8_t *drawBufferReadHead = drawBuffer;
676         uint8_t *paletteBufferReadHead = paletteBuffer;
677         uint8_t *maskBufferReadHead = maskBuffer;
678         uint8_t *overlayBufferReadHead = overlayBuffer;
679         uint8_t *outputBufferWriteHead = bzGfxCompositedBuffer;
680         for (size_t i = 0, end = bufferStride * canvasHeight; i < end; ++i) {
681                 //*outputBufferWriteHead = (palettes[*paletteBufferReadHead + 1][*drawBufferReadHead] & *maskBufferReadHead) | *overlayBufferReadHead;
682                 *outputBufferWriteHead = *overlayBufferReadHead > 0 ? gPalettes[*overlayBufferReadHead] : gPalettes[(*paletteBufferReadHead + 1) *gPaletteColorCount + *drawBufferReadHead];
683
684                 ++drawBufferReadHead;
685                 ++paletteBufferReadHead;
686                 ++maskBufferReadHead;
687                 ++overlayBufferReadHead;
688                 ++outputBufferWriteHead;
689         }
690
691         bzPerfTimerStop("game.blit.composite");
692 }