1 /*****************************************************************************
2 * Copyright (c) 2014-2020 OpenRCT2 developers
3 *
4 * For a complete list of all authors, please refer to contributors.md
5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6 *
7 * OpenRCT2 is licensed under the GNU General Public License version 3.
8 *****************************************************************************/
9
10 #include "X8DrawingEngine.h"
11
12 #include "../Context.h"
13 #include "../Game.h"
14 #include "../Intro.h"
15 #include "../config/Config.h"
16 #include "../core/Numerics.hpp"
17 #include "../interface/Screenshot.h"
18 #include "../interface/Viewport.h"
19 #include "../interface/Window.h"
20 #include "../ui/UiContext.h"
21 #include "../util/Util.h"
22 #include "../world/Climate.h"
23 #include "Drawing.h"
24 #include "IDrawingContext.h"
25 #include "IDrawingEngine.h"
26 #include "LightFX.h"
27 #include "Weather.h"
28
29 #include <algorithm>
30 #include <cstring>
31
32 using namespace OpenRCT2;
33 using namespace OpenRCT2::Drawing;
34 using namespace OpenRCT2::Ui;
35
X8WeatherDrawer()36 X8WeatherDrawer::X8WeatherDrawer()
37 {
38 _weatherPixels = new WeatherPixel[_weatherPixelsCapacity];
39 }
40
~X8WeatherDrawer()41 X8WeatherDrawer::~X8WeatherDrawer()
42 {
43 delete[] _weatherPixels;
44 }
45
Draw(rct_drawpixelinfo * dpi,int32_t x,int32_t y,int32_t width,int32_t height,int32_t xStart,int32_t yStart,const uint8_t * weatherpattern)46 void X8WeatherDrawer::Draw(
47 rct_drawpixelinfo* dpi, int32_t x, int32_t y, int32_t width, int32_t height, int32_t xStart, int32_t yStart,
48 const uint8_t* weatherpattern)
49 {
50 const uint8_t* pattern = weatherpattern;
51 auto patternXSpace = *pattern++;
52 auto patternYSpace = *pattern++;
53
54 uint8_t patternStartXOffset = xStart % patternXSpace;
55 uint8_t patternStartYOffset = yStart % patternYSpace;
56
57 uint32_t pixelOffset = (dpi->pitch + dpi->width) * y + x;
58 uint8_t patternYPos = patternStartYOffset % patternYSpace;
59
60 uint8_t* screenBits = dpi->bits;
61
62 // Stores the colours of changed pixels
63 WeatherPixel* newPixels = &_weatherPixels[_weatherPixelsCount];
64 for (; height != 0; height--)
65 {
66 auto patternX = pattern[patternYPos * 2];
67 if (patternX != 0xFF)
68 {
69 if (_weatherPixelsCount < (_weatherPixelsCapacity - static_cast<uint32_t>(width)))
70 {
71 uint32_t finalPixelOffset = width + pixelOffset;
72
73 uint32_t xPixelOffset = pixelOffset;
74 xPixelOffset += (static_cast<uint8_t>(patternX - patternStartXOffset)) % patternXSpace;
75
76 auto patternPixel = pattern[patternYPos * 2 + 1];
77 for (; xPixelOffset < finalPixelOffset; xPixelOffset += patternXSpace)
78 {
79 uint8_t current_pixel = screenBits[xPixelOffset];
80 screenBits[xPixelOffset] = patternPixel;
81 _weatherPixelsCount++;
82
83 // Store colour and position
84 *newPixels++ = { xPixelOffset, current_pixel };
85 }
86 }
87 }
88
89 pixelOffset += dpi->pitch + dpi->width;
90 patternYPos++;
91 patternYPos %= patternYSpace;
92 }
93 }
94
Restore(rct_drawpixelinfo * dpi)95 void X8WeatherDrawer::Restore(rct_drawpixelinfo* dpi)
96 {
97 if (_weatherPixelsCount > 0)
98 {
99 uint32_t numPixels = (dpi->width + dpi->pitch) * dpi->height;
100 uint8_t* bits = dpi->bits;
101 for (uint32_t i = 0; i < _weatherPixelsCount; i++)
102 {
103 WeatherPixel weatherPixel = _weatherPixels[i];
104 if (weatherPixel.Position >= numPixels)
105 {
106 // Pixel out of bounds, bail
107 break;
108 }
109
110 bits[weatherPixel.Position] = weatherPixel.Colour;
111 }
112 _weatherPixelsCount = 0;
113 }
114 }
115
116 #ifdef __WARN_SUGGEST_FINAL_METHODS__
117 # pragma GCC diagnostic push
118 # pragma GCC diagnostic ignored "-Wsuggest-final-methods"
119 #endif
120
X8DrawingEngine(const std::shared_ptr<Ui::IUiContext> & uiContext)121 X8DrawingEngine::X8DrawingEngine([[maybe_unused]] const std::shared_ptr<Ui::IUiContext>& uiContext)
122 {
123 _drawingContext = new X8DrawingContext(this);
124 _bitsDPI.DrawingEngine = this;
125 #ifdef __ENABLE_LIGHTFX__
126 lightfx_set_available(true);
127 _lastLightFXenabled = (gConfigGeneral.enable_light_fx != 0);
128 #endif
129 }
130
~X8DrawingEngine()131 X8DrawingEngine::~X8DrawingEngine()
132 {
133 delete _drawingContext;
134 delete[] _dirtyGrid.Blocks;
135 delete[] _bits;
136 }
137
Initialise()138 void X8DrawingEngine::Initialise()
139 {
140 }
141
Resize(uint32_t width,uint32_t height)142 void X8DrawingEngine::Resize(uint32_t width, uint32_t height)
143 {
144 uint32_t pitch = width;
145 ConfigureBits(width, height, pitch);
146 }
147
SetPalette(const GamePalette & palette)148 void X8DrawingEngine::SetPalette([[maybe_unused]] const GamePalette& palette)
149 {
150 }
151
SetVSync(bool vsync)152 void X8DrawingEngine::SetVSync([[maybe_unused]] bool vsync)
153 {
154 // Not applicable for this engine
155 }
156
Invalidate(int32_t left,int32_t top,int32_t right,int32_t bottom)157 void X8DrawingEngine::Invalidate(int32_t left, int32_t top, int32_t right, int32_t bottom)
158 {
159 left = std::max(left, 0);
160 top = std::max(top, 0);
161 right = std::min(right, static_cast<int32_t>(_width));
162 bottom = std::min(bottom, static_cast<int32_t>(_height));
163
164 if (left >= right)
165 return;
166 if (top >= bottom)
167 return;
168
169 right--;
170 bottom--;
171
172 left >>= _dirtyGrid.BlockShiftX;
173 right >>= _dirtyGrid.BlockShiftX;
174 top >>= _dirtyGrid.BlockShiftY;
175 bottom >>= _dirtyGrid.BlockShiftY;
176
177 uint32_t dirtyBlockColumns = _dirtyGrid.BlockColumns;
178 uint8_t* screenDirtyBlocks = _dirtyGrid.Blocks;
179 for (int16_t y = top; y <= bottom; y++)
180 {
181 uint32_t yOffset = y * dirtyBlockColumns;
182 for (int16_t x = left; x <= right; x++)
183 {
184 screenDirtyBlocks[yOffset + x] = 0xFF;
185 }
186 }
187 }
188
BeginDraw()189 void X8DrawingEngine::BeginDraw()
190 {
191 if (gIntroState == IntroState::None)
192 {
193 #ifdef __ENABLE_LIGHTFX__
194 // HACK we need to re-configure the bits if light fx has been enabled / disabled
195 if (_lastLightFXenabled != (gConfigGeneral.enable_light_fx != 0))
196 {
197 Resize(_width, _height);
198 }
199 #endif
200 _weatherDrawer.Restore(&_bitsDPI);
201 }
202 }
203
EndDraw()204 void X8DrawingEngine::EndDraw()
205 {
206 }
207
PaintWindows()208 void X8DrawingEngine::PaintWindows()
209 {
210 window_reset_visibilities();
211
212 // Redraw dirty regions before updating the viewports, otherwise
213 // when viewports get panned, they copy dirty pixels
214 DrawAllDirtyBlocks();
215 window_update_all_viewports();
216 DrawAllDirtyBlocks();
217 }
218
PaintWeather()219 void X8DrawingEngine::PaintWeather()
220 {
221 DrawWeather(&_bitsDPI, &_weatherDrawer);
222 }
223
CopyRect(int32_t x,int32_t y,int32_t width,int32_t height,int32_t dx,int32_t dy)224 void X8DrawingEngine::CopyRect(int32_t x, int32_t y, int32_t width, int32_t height, int32_t dx, int32_t dy)
225 {
226 if (dx == 0 && dy == 0)
227 return;
228
229 // Originally 0x00683359
230 // Adjust for move off screen
231 // NOTE: when zooming, there can be x, y, dx, dy combinations that go off the
232 // screen; hence the checks. This code should ultimately not be called when
233 // zooming because this function is specific to updating the screen on move
234 int32_t lmargin = std::min(x - dx, 0);
235 int32_t rmargin = std::min(static_cast<int32_t>(_width) - (x - dx + width), 0);
236 int32_t tmargin = std::min(y - dy, 0);
237 int32_t bmargin = std::min(static_cast<int32_t>(_height) - (y - dy + height), 0);
238 x -= lmargin;
239 y -= tmargin;
240 width += lmargin + rmargin;
241 height += tmargin + bmargin;
242
243 int32_t stride = _bitsDPI.width + _bitsDPI.pitch;
244 uint8_t* to = _bitsDPI.bits + y * stride + x;
245 uint8_t* from = _bitsDPI.bits + (y - dy) * stride + x - dx;
246
247 if (dy > 0)
248 {
249 // If positive dy, reverse directions
250 to += (height - 1) * stride;
251 from += (height - 1) * stride;
252 stride = -stride;
253 }
254
255 // Move bytes
256 for (int32_t i = 0; i < height; i++)
257 {
258 memmove(to, from, width);
259 to += stride;
260 from += stride;
261 }
262 }
263
Screenshot()264 std::string X8DrawingEngine::Screenshot()
265 {
266 return screenshot_dump_png(&_bitsDPI);
267 }
268
GetDrawingContext()269 IDrawingContext* X8DrawingEngine::GetDrawingContext()
270 {
271 return _drawingContext;
272 }
273
GetDrawingPixelInfo()274 rct_drawpixelinfo* X8DrawingEngine::GetDrawingPixelInfo()
275 {
276 return &_bitsDPI;
277 }
278
GetFlags()279 DRAWING_ENGINE_FLAGS X8DrawingEngine::GetFlags()
280 {
281 return static_cast<DRAWING_ENGINE_FLAGS>(DEF_DIRTY_OPTIMISATIONS | DEF_PARALLEL_DRAWING);
282 }
283
InvalidateImage(uint32_t image)284 void X8DrawingEngine::InvalidateImage([[maybe_unused]] uint32_t image)
285 {
286 // Not applicable for this engine
287 }
288
GetDPI()289 rct_drawpixelinfo* X8DrawingEngine::GetDPI()
290 {
291 return &_bitsDPI;
292 }
293
ConfigureBits(uint32_t width,uint32_t height,uint32_t pitch)294 void X8DrawingEngine::ConfigureBits(uint32_t width, uint32_t height, uint32_t pitch)
295 {
296 size_t newBitsSize = pitch * height;
297 uint8_t* newBits = new uint8_t[newBitsSize];
298 if (_bits == nullptr)
299 {
300 std::fill_n(newBits, newBitsSize, 0);
301 }
302 else
303 {
304 if (_pitch == pitch)
305 {
306 std::copy_n(_bits, std::min(_bitsSize, newBitsSize), newBits);
307 }
308 else
309 {
310 uint8_t* src = _bits;
311 uint8_t* dst = newBits;
312
313 uint32_t minWidth = std::min(_width, width);
314 uint32_t minHeight = std::min(_height, height);
315 for (uint32_t y = 0; y < minHeight; y++)
316 {
317 std::copy_n(src, minWidth, dst);
318 if (pitch - minWidth > 0)
319 {
320 std::fill_n(dst + minWidth, pitch - minWidth, 0);
321 }
322 src += _pitch;
323 dst += pitch;
324 }
325 }
326 delete[] _bits;
327 }
328
329 _bits = newBits;
330 _bitsSize = newBitsSize;
331 _width = width;
332 _height = height;
333 _pitch = pitch;
334
335 rct_drawpixelinfo* dpi = &_bitsDPI;
336 dpi->bits = _bits;
337 dpi->x = 0;
338 dpi->y = 0;
339 dpi->width = width;
340 dpi->height = height;
341 dpi->pitch = _pitch - width;
342
343 ConfigureDirtyGrid();
344
345 #ifdef __ENABLE_LIGHTFX__
346 if (lightfx_is_available())
347 {
348 lightfx_update_buffers(dpi);
349 }
350 #endif
351 }
352
OnDrawDirtyBlock(uint32_t x,uint32_t y,uint32_t columns,uint32_t rows)353 void X8DrawingEngine::OnDrawDirtyBlock(
354 [[maybe_unused]] uint32_t x, [[maybe_unused]] uint32_t y, [[maybe_unused]] uint32_t columns, [[maybe_unused]] uint32_t rows)
355 {
356 }
357
ConfigureDirtyGrid()358 void X8DrawingEngine::ConfigureDirtyGrid()
359 {
360 _dirtyGrid.BlockShiftX = 7;
361 _dirtyGrid.BlockShiftY = 6;
362 _dirtyGrid.BlockWidth = 1 << _dirtyGrid.BlockShiftX;
363 _dirtyGrid.BlockHeight = 1 << _dirtyGrid.BlockShiftY;
364 _dirtyGrid.BlockColumns = (_width >> _dirtyGrid.BlockShiftX) + 1;
365 _dirtyGrid.BlockRows = (_height >> _dirtyGrid.BlockShiftY) + 1;
366
367 delete[] _dirtyGrid.Blocks;
368 _dirtyGrid.Blocks = new uint8_t[_dirtyGrid.BlockColumns * _dirtyGrid.BlockRows];
369 }
370
DrawAllDirtyBlocks()371 void X8DrawingEngine::DrawAllDirtyBlocks()
372 {
373 for (uint32_t x = 0; x < _dirtyGrid.BlockColumns; x++)
374 {
375 for (uint32_t y = 0; y < _dirtyGrid.BlockRows; y++)
376 {
377 uint32_t yOffset = y * _dirtyGrid.BlockColumns;
378 if (_dirtyGrid.Blocks[yOffset + x] == 0)
379 {
380 continue;
381 }
382
383 // Determine columns
384 uint32_t xx;
385 for (xx = x; xx < _dirtyGrid.BlockColumns; xx++)
386 {
387 if (_dirtyGrid.Blocks[yOffset + xx] == 0)
388 {
389 break;
390 }
391 }
392
393 // Check rows
394 uint32_t columns = xx - x;
395 auto rows = GetNumDirtyRows(x, y, columns);
396 DrawDirtyBlocks(x, y, columns, rows);
397 }
398 }
399 }
400
GetNumDirtyRows(const uint32_t x,const uint32_t y,const uint32_t columns)401 uint32_t X8DrawingEngine::GetNumDirtyRows(const uint32_t x, const uint32_t y, const uint32_t columns)
402 {
403 uint32_t yy = y;
404
405 for (yy = y; yy < _dirtyGrid.BlockRows; yy++)
406 {
407 uint32_t yyOffset = yy * _dirtyGrid.BlockColumns;
408 for (uint32_t xx = x; xx < x + columns; xx++)
409 {
410 if (_dirtyGrid.Blocks[yyOffset + xx] == 0)
411 {
412 return yy - y;
413 }
414 }
415 }
416 return yy - y;
417 }
418
DrawDirtyBlocks(uint32_t x,uint32_t y,uint32_t columns,uint32_t rows)419 void X8DrawingEngine::DrawDirtyBlocks(uint32_t x, uint32_t y, uint32_t columns, uint32_t rows)
420 {
421 uint32_t dirtyBlockColumns = _dirtyGrid.BlockColumns;
422 uint8_t* screenDirtyBlocks = _dirtyGrid.Blocks;
423
424 // Unset dirty blocks
425 for (uint32_t top = y; top < y + rows; top++)
426 {
427 uint32_t topOffset = top * dirtyBlockColumns;
428 for (uint32_t left = x; left < x + columns; left++)
429 {
430 screenDirtyBlocks[topOffset + left] = 0;
431 }
432 }
433
434 // Determine region in pixels
435 uint32_t left = std::max<uint32_t>(0, x * _dirtyGrid.BlockWidth);
436 uint32_t top = std::max<uint32_t>(0, y * _dirtyGrid.BlockHeight);
437 uint32_t right = std::min(_width, left + (columns * _dirtyGrid.BlockWidth));
438 uint32_t bottom = std::min(_height, top + (rows * _dirtyGrid.BlockHeight));
439 if (right <= left || bottom <= top)
440 {
441 return;
442 }
443
444 // Draw region
445 OnDrawDirtyBlock(x, y, columns, rows);
446 window_draw_all(&_bitsDPI, left, top, right, bottom);
447 }
448
449 #ifdef __WARN_SUGGEST_FINAL_METHODS__
450 # pragma GCC diagnostic pop
451 #endif
452
X8DrawingContext(X8DrawingEngine * engine)453 X8DrawingContext::X8DrawingContext(X8DrawingEngine* engine)
454 {
455 _engine = engine;
456 }
457
GetEngine()458 IDrawingEngine* X8DrawingContext::GetEngine()
459 {
460 return _engine;
461 }
462
Clear(rct_drawpixelinfo * dpi,uint8_t paletteIndex)463 void X8DrawingContext::Clear(rct_drawpixelinfo* dpi, uint8_t paletteIndex)
464 {
465 int32_t w = dpi->width / dpi->zoom_level;
466 int32_t h = dpi->height / dpi->zoom_level;
467 uint8_t* ptr = dpi->bits;
468
469 for (int32_t y = 0; y < h; y++)
470 {
471 std::fill_n(ptr, w, paletteIndex);
472 ptr += w + dpi->pitch;
473 }
474 }
475
476 /** rct2: 0x0097FF04 */
477 // clang-format off
478 static constexpr const uint16_t Pattern[] = {
479 0b0111111110000000,
480 0b0011111111000000,
481 0b0001111111100000,
482 0b0000111111110000,
483 0b0000011111111000,
484 0b0000001111111100,
485 0b0000000111111110,
486 0b0000000011111111,
487 0b1000000001111111,
488 0b1100000000111111,
489 0b1110000000011111,
490 0b1111000000001111,
491 0b1111100000000111,
492 0b1111110000000011,
493 0b1111111000000001,
494 0b1111111100000000,
495 };
496
497 /** rct2: 0x0097FF14 */
498 static constexpr const uint16_t PatternInverse[] = {
499 0b1000000001111111,
500 0b1100000000111111,
501 0b1110000000011111,
502 0b1111000000001111,
503 0b1111100000000111,
504 0b1111110000000011,
505 0b1111111000000001,
506 0b1111111100000000,
507 0b0111111110000000,
508 0b0011111111000000,
509 0b0001111111100000,
510 0b0000111111110000,
511 0b0000011111111000,
512 0b0000001111111100,
513 0b0000000111111110,
514 0b0000000011111111,
515 };
516
517 /** rct2: 0x0097FEFC */
518 static constexpr const uint16_t* Patterns[] = {
519 Pattern,
520 PatternInverse,
521 };
522 // clang-format on
523
FillRect(rct_drawpixelinfo * dpi,uint32_t colour,int32_t left,int32_t top,int32_t right,int32_t bottom)524 void X8DrawingContext::FillRect(
525 rct_drawpixelinfo* dpi, uint32_t colour, int32_t left, int32_t top, int32_t right, int32_t bottom)
526 {
527 if (left > right)
528 return;
529 if (top > bottom)
530 return;
531 if (dpi->x > right)
532 return;
533 if (left >= dpi->x + dpi->width)
534 return;
535 if (bottom < dpi->y)
536 return;
537 if (top >= dpi->y + dpi->height)
538 return;
539
540 uint16_t crossPattern = 0;
541
542 int32_t startX = left - dpi->x;
543 if (startX < 0)
544 {
545 crossPattern ^= startX;
546 startX = 0;
547 }
548
549 int32_t endX = right - dpi->x + 1;
550 if (endX > dpi->width)
551 {
552 endX = dpi->width;
553 }
554
555 int32_t startY = top - dpi->y;
556 if (startY < 0)
557 {
558 crossPattern ^= startY;
559 startY = 0;
560 }
561
562 int32_t endY = bottom - dpi->y + 1;
563 if (endY > dpi->height)
564 {
565 endY = dpi->height;
566 }
567
568 int32_t width = endX - startX;
569 int32_t height = endY - startY;
570
571 if (colour & 0x1000000)
572 {
573 // Cross hatching
574 uint8_t* dst = (startY * (dpi->width + dpi->pitch)) + startX + dpi->bits;
575 for (int32_t i = 0; i < height; i++)
576 {
577 uint8_t* nextdst = dst + dpi->width + dpi->pitch;
578 uint32_t p = Numerics::ror32(crossPattern, 1);
579 p = (p & 0xFFFF0000) | width;
580
581 // Fill every other pixel with the colour
582 for (; (p & 0xFFFF) != 0; p--)
583 {
584 p = p ^ 0x80000000;
585 if (p & 0x80000000)
586 {
587 *dst = colour & 0xFF;
588 }
589 dst++;
590 }
591 crossPattern ^= 1;
592 dst = nextdst;
593 }
594 }
595 else if (colour & 0x2000000)
596 {
597 assert(false);
598 }
599 else if (colour & 0x4000000)
600 {
601 uint8_t* dst = startY * (dpi->width + dpi->pitch) + startX + dpi->bits;
602
603 // The pattern loops every 15 lines this is which
604 // part the pattern is on.
605 int32_t patternY = (startY + dpi->y) % 16;
606
607 // The pattern loops every 15 pixels this is which
608 // part the pattern is on.
609 int32_t startPatternX = (startX + dpi->x) % 16;
610 int32_t patternX = startPatternX;
611
612 const uint16_t* patternsrc = Patterns[colour >> 28]; // or possibly uint8_t)[esi*4] ?
613
614 for (int32_t numLines = height; numLines > 0; numLines--)
615 {
616 uint8_t* nextdst = dst + dpi->width + dpi->pitch;
617 uint16_t pattern = patternsrc[patternY];
618
619 for (int32_t numPixels = width; numPixels > 0; numPixels--)
620 {
621 if (pattern & (1 << patternX))
622 {
623 *dst = colour & 0xFF;
624 }
625 patternX = (patternX + 1) % 16;
626 dst++;
627 }
628 patternX = startPatternX;
629 patternY = (patternY + 1) % 16;
630 dst = nextdst;
631 }
632 }
633 else
634 {
635 uint8_t* dst = startY * (dpi->width + dpi->pitch) + startX + dpi->bits;
636 for (int32_t i = 0; i < height; i++)
637 {
638 std::fill_n(dst, width, colour & 0xFF);
639 dst += dpi->width + dpi->pitch;
640 }
641 }
642 }
643
FilterRect(rct_drawpixelinfo * dpi,FilterPaletteID palette,int32_t left,int32_t top,int32_t right,int32_t bottom)644 void X8DrawingContext::FilterRect(
645 rct_drawpixelinfo* dpi, FilterPaletteID palette, int32_t left, int32_t top, int32_t right, int32_t bottom)
646 {
647 if (left > right)
648 return;
649 if (top > bottom)
650 return;
651 if (dpi->x > right)
652 return;
653 if (left >= dpi->x + dpi->width)
654 return;
655 if (bottom < dpi->y)
656 return;
657 if (top >= dpi->y + dpi->height)
658 return;
659
660 int32_t startX = left - dpi->x;
661 if (startX < 0)
662 {
663 startX = 0;
664 }
665
666 int32_t endX = right - dpi->x + 1;
667 if (endX > dpi->width)
668 {
669 endX = dpi->width;
670 }
671
672 int32_t startY = top - dpi->y;
673 if (startY < 0)
674 {
675 startY = 0;
676 }
677
678 int32_t endY = bottom - dpi->y + 1;
679 if (endY > dpi->height)
680 {
681 endY = dpi->height;
682 }
683
684 int32_t width = endX - startX;
685 int32_t height = endY - startY;
686
687 // 0x2000000
688 // 00678B7E 00678C83
689 // Location in screen buffer?
690 uint8_t* dst = dpi->bits
691 + static_cast<uint32_t>(
692 (startY / dpi->zoom_level) * ((dpi->width / dpi->zoom_level) + dpi->pitch) + (startX / dpi->zoom_level));
693
694 // Find colour in colour table?
695 auto paletteMap = GetPaletteMapForColour(EnumValue(palette));
696 if (paletteMap.has_value())
697 {
698 const auto& paletteEntries = paletteMap.value();
699 const int32_t scaled_width = width / dpi->zoom_level;
700 const int32_t step = ((dpi->width / dpi->zoom_level) + dpi->pitch);
701
702 // Fill the rectangle with the colours from the colour table
703 auto c = height / dpi->zoom_level;
704 for (int32_t i = 0; i < c; i++)
705 {
706 uint8_t* nextdst = dst + step * i;
707 for (int32_t j = 0; j < scaled_width; j++)
708 {
709 auto index = *(nextdst + j);
710 *(nextdst + j) = paletteEntries[index];
711 }
712 }
713 }
714 }
715
DrawLine(rct_drawpixelinfo * dpi,uint32_t colour,const ScreenLine & line)716 void X8DrawingContext::DrawLine(rct_drawpixelinfo* dpi, uint32_t colour, const ScreenLine& line)
717 {
718 gfx_draw_line_software(dpi, line, colour);
719 }
720
DrawSprite(rct_drawpixelinfo * dpi,uint32_t image,int32_t x,int32_t y,uint32_t tertiaryColour)721 void X8DrawingContext::DrawSprite(rct_drawpixelinfo* dpi, uint32_t image, int32_t x, int32_t y, uint32_t tertiaryColour)
722 {
723 gfx_draw_sprite_software(dpi, ImageId::FromUInt32(image, tertiaryColour), { x, y });
724 }
725
DrawSpriteRawMasked(rct_drawpixelinfo * dpi,int32_t x,int32_t y,uint32_t maskImage,uint32_t colourImage)726 void X8DrawingContext::DrawSpriteRawMasked(
727 rct_drawpixelinfo* dpi, int32_t x, int32_t y, uint32_t maskImage, uint32_t colourImage)
728 {
729 gfx_draw_sprite_raw_masked_software(dpi, { x, y }, maskImage, colourImage);
730 }
731
DrawSpriteSolid(rct_drawpixelinfo * dpi,uint32_t image,int32_t x,int32_t y,uint8_t colour)732 void X8DrawingContext::DrawSpriteSolid(rct_drawpixelinfo* dpi, uint32_t image, int32_t x, int32_t y, uint8_t colour)
733 {
734 uint8_t palette[256];
735 std::fill_n(palette, sizeof(palette), colour);
736 palette[0] = 0;
737
738 const auto spriteCoords = ScreenCoordsXY{ x, y };
739 gfx_draw_sprite_palette_set_software(
740 dpi, ImageId::FromUInt32((image & 0x7FFFF) | IMAGE_TYPE_REMAP), spriteCoords, PaletteMap(palette));
741 }
742
DrawGlyph(rct_drawpixelinfo * dpi,uint32_t image,int32_t x,int32_t y,const PaletteMap & paletteMap)743 void X8DrawingContext::DrawGlyph(rct_drawpixelinfo* dpi, uint32_t image, int32_t x, int32_t y, const PaletteMap& paletteMap)
744 {
745 gfx_draw_sprite_palette_set_software(dpi, ImageId::FromUInt32(image), { x, y }, paletteMap);
746 }
747