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 "Drawing.h"
11
12 #include <algorithm>
13 #include <cstring>
14
15 template<DrawBlendOp TBlendOp, size_t TZoom>
DrawRLESpriteMagnify(rct_drawpixelinfo & dpi,const DrawSpriteArgs & args)16 static void FASTCALL DrawRLESpriteMagnify(rct_drawpixelinfo& dpi, const DrawSpriteArgs& args)
17 {
18 auto src0 = args.SourceImage.offset;
19 auto dst0 = args.DestinationBits;
20 auto srcX = args.SrcX;
21 auto srcY = args.SrcY;
22 auto width = args.Width;
23 auto height = args.Height;
24 auto& paletteMap = args.PalMap;
25 auto zoom = 1 << TZoom;
26 auto dstLineWidth = (static_cast<size_t>(dpi.width) << TZoom) + dpi.pitch;
27
28 // Move up to the first line of the image if source_y_start is negative. Why does this even occur?
29 if (srcY < 0)
30 {
31 srcY += zoom;
32 height -= zoom;
33 dst0 += dstLineWidth;
34 }
35
36 // For every line in the image
37 for (int32_t i = 0; i < height; i++)
38 {
39 int32_t y = srcY + i;
40
41 // The first part of the source pointer is a list of offsets to different lines
42 // This will move the pointer to the correct source line.
43 uint16_t lineOffset = src0[y * 2] | (src0[y * 2 + 1] << 8);
44 auto nextRun = src0 + lineOffset;
45 auto dstLineStart = dst0 + ((dstLineWidth * i) << TZoom);
46
47 // For every data chunk in the line
48 bool isEndOfLine = false;
49 while (!isEndOfLine)
50 {
51 // Read chunk metadata
52 auto src = nextRun;
53 auto dataSize = *src++;
54 auto firstPixelX = *src++;
55 isEndOfLine = (dataSize & 0x80) != 0;
56 dataSize &= 0x7F;
57
58 // Have our next source pointer point to the next data section
59 nextRun = src + dataSize;
60
61 int32_t x = firstPixelX - srcX;
62 int32_t numPixels = dataSize;
63 if (x < 0)
64 {
65 src += -x;
66 numPixels += x;
67 x = 0;
68 }
69
70 // If the end position is further out than the whole image
71 // end position then we need to shorten the line again
72 numPixels = std::min(numPixels, width - x);
73
74 auto dst = dstLineStart + (static_cast<size_t>(x) << TZoom);
75 while (numPixels > 0)
76 {
77 BlitPixels<TBlendOp>(src, dst, paletteMap, zoom, dstLineWidth);
78 src++;
79 dst += zoom;
80 numPixels--;
81 }
82 }
83 }
84 }
85
86 template<DrawBlendOp TBlendOp, size_t TZoom>
DrawRLESpriteMinify(rct_drawpixelinfo & dpi,const DrawSpriteArgs & args)87 static void FASTCALL DrawRLESpriteMinify(rct_drawpixelinfo& dpi, const DrawSpriteArgs& args)
88 {
89 auto src0 = args.SourceImage.offset;
90 auto dst0 = args.DestinationBits;
91 auto srcX = args.SrcX;
92 auto srcY = args.SrcY;
93 auto width = args.Width;
94 auto height = args.Height;
95 auto zoom = 1 << TZoom;
96 auto dstLineWidth = (static_cast<size_t>(dpi.width) >> TZoom) + dpi.pitch;
97
98 // Move up to the first line of the image if source_y_start is negative. Why does this even occur?
99 if (srcY < 0)
100 {
101 srcY += zoom;
102 height -= zoom;
103 dst0 += dstLineWidth;
104 }
105
106 // For every line in the image
107 for (int32_t i = 0; i < height; i += zoom)
108 {
109 int32_t y = srcY + i;
110
111 // The first part of the source pointer is a list of offsets to different lines
112 // This will move the pointer to the correct source line.
113 uint16_t lineOffset = src0[y * 2] | (src0[y * 2 + 1] << 8);
114 auto nextRun = src0 + lineOffset;
115 auto dstLineStart = dst0 + dstLineWidth * (i >> TZoom);
116
117 // For every data chunk in the line
118 auto isEndOfLine = false;
119 while (!isEndOfLine)
120 {
121 // Read chunk metadata
122 auto src = nextRun;
123 auto dataSize = *src++;
124 auto firstPixelX = *src++;
125 isEndOfLine = (dataSize & 0x80) != 0;
126 dataSize &= 0x7F;
127
128 // Have our next source pointer point to the next data section
129 nextRun = src + dataSize;
130
131 int32_t x = firstPixelX - srcX;
132 int32_t numPixels = dataSize;
133 if (x > 0)
134 {
135 // If x is not a multiple of zoom, round it up to a multiple
136 auto mod = x & (zoom - 1);
137 if (mod != 0)
138 {
139 auto offset = zoom - mod;
140 x += offset;
141 src += offset;
142 numPixels -= offset;
143 }
144 }
145 else if (x < 0)
146 {
147 // Clamp x to zero if negative
148 src += -x;
149 numPixels += x;
150 x = 0;
151 }
152
153 // If the end position is further out than the whole image
154 // end position then we need to shorten the line again
155 numPixels = std::min(numPixels, width - x);
156
157 auto dst = dstLineStart + (x >> TZoom);
158 if constexpr ((TBlendOp & BLEND_SRC) == 0 && (TBlendOp & BLEND_DST) == 0 && TZoom == 0)
159 {
160 // Since we're sampling each pixel at this zoom level, just do a straight std::memcpy
161 if (numPixels > 0)
162 {
163 std::memcpy(dst, src, numPixels);
164 }
165 }
166 else
167 {
168 auto& paletteMap = args.PalMap;
169 while (numPixels > 0)
170 {
171 BlitPixel<TBlendOp>(src, dst, paletteMap);
172 numPixels -= zoom;
173 src += zoom;
174 dst++;
175 }
176 }
177 }
178 }
179 }
180
DrawRLESprite(rct_drawpixelinfo & dpi,const DrawSpriteArgs & args)181 template<DrawBlendOp TBlendOp> static void FASTCALL DrawRLESprite(rct_drawpixelinfo& dpi, const DrawSpriteArgs& args)
182 {
183 auto zoom_level = static_cast<int8_t>(dpi.zoom_level);
184 switch (zoom_level)
185 {
186 case -2:
187 DrawRLESpriteMagnify<TBlendOp, 2>(dpi, args);
188 break;
189 case -1:
190 DrawRLESpriteMagnify<TBlendOp, 1>(dpi, args);
191 break;
192 case 0:
193 DrawRLESpriteMinify<TBlendOp, 0>(dpi, args);
194 break;
195 case 1:
196 DrawRLESpriteMinify<TBlendOp, 1>(dpi, args);
197 break;
198 case 2:
199 DrawRLESpriteMinify<TBlendOp, 2>(dpi, args);
200 break;
201 case 3:
202 DrawRLESpriteMinify<TBlendOp, 3>(dpi, args);
203 break;
204 default:
205 assert(false);
206 break;
207 }
208 }
209
210 /**
211 * Transfers readied images onto buffers
212 * This function copies the sprite data onto the screen
213 * rct2: 0x0067AA18
214 * @param imageId Only flags are used.
215 */
gfx_rle_sprite_to_buffer(rct_drawpixelinfo & dpi,const DrawSpriteArgs & args)216 void FASTCALL gfx_rle_sprite_to_buffer(rct_drawpixelinfo& dpi, const DrawSpriteArgs& args)
217 {
218 if (args.Image.HasPrimary())
219 {
220 if (args.Image.IsBlended())
221 {
222 DrawRLESprite<BLEND_TRANSPARENT | BLEND_SRC | BLEND_DST>(dpi, args);
223 }
224 else
225 {
226 DrawRLESprite<BLEND_TRANSPARENT | BLEND_SRC>(dpi, args);
227 }
228 }
229 else if (args.Image.IsBlended())
230 {
231 DrawRLESprite<BLEND_TRANSPARENT | BLEND_DST>(dpi, args);
232 }
233 else
234 {
235 DrawRLESprite<BLEND_TRANSPARENT>(dpi, args);
236 }
237 }
238