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