Reactos
1/*
2 * PROJECT: ReactOS Boot Video Driver
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Platform-independent common helpers and defines
5 * COPYRIGHT: Copyright 2010 Gregor Schneider <gregor.schneider@reactos.org>
6 * Copyright 2011 Rafal Harabien <rafalh@reactos.org>
7 * Copyright 2020 Stanislav Motylkov <x86corez@gmail.com>
8 */
9
10#include "precomp.h"
11
12/* GLOBALS ********************************************************************/
13
14/*
15 * Boot video driver default palette is similar to the standard 16-color
16 * CGA palette, but it has Red and Blue channels swapped, and also dark
17 * and light gray colors swapped.
18 */
19const RGBQUAD VidpDefaultPalette[BV_MAX_COLORS] =
20{
21 RGB( 0, 0, 0), /* Black */
22 RGB(128, 0, 0), /* Red */
23 RGB( 0, 128, 0), /* Green */
24 RGB(128, 128, 0), /* Brown */
25 RGB( 0, 0, 128), /* Blue */
26 RGB(128, 0, 128), /* Magenta */
27 RGB( 0, 128, 128), /* Cyan */
28 RGB(128, 128, 128), /* Dark Gray */
29 RGB(192, 192, 192), /* Light Gray */
30 RGB(255, 0, 0), /* Light Red */
31 RGB( 0, 255, 0), /* Light Green */
32 RGB(255, 255, 0), /* Yellow */
33 RGB( 0, 0, 255), /* Light Blue */
34 RGB(255, 0, 255), /* Light Magenta */
35 RGB( 0, 255, 255), /* Light Cyan */
36 RGB(255, 255, 255), /* White */
37};
38
39/* PRIVATE FUNCTIONS **********************************************************/
40
41static VOID
42BitBlt(
43 _In_ ULONG Left,
44 _In_ ULONG Top,
45 _In_ ULONG Width,
46 _In_ ULONG Height,
47 _In_reads_bytes_(Delta * Height) PUCHAR Buffer,
48 _In_ ULONG BitsPerPixel,
49 _In_ ULONG Delta)
50{
51 ULONG X, Y, Pixel;
52 UCHAR Colors;
53 PUCHAR InputBuffer;
54 const ULONG Bottom = Top + Height;
55 const ULONG Right = Left + Width;
56
57 /* Check if the buffer isn't 4bpp */
58 if (BitsPerPixel != 4)
59 {
60 /* FIXME: TODO */
61 DbgPrint("Unhandled BitBlt\n"
62 "%lux%lu @ (%lu|%lu)\n"
63 "Bits Per Pixel %lu\n"
64 "Buffer: %p. Delta: %lu\n",
65 Width,
66 Height,
67 Left,
68 Top,
69 BitsPerPixel,
70 Buffer,
71 Delta);
72 return;
73 }
74
75 PrepareForSetPixel();
76
77 /* 4bpp blitting */
78 for (Y = Top; Y < Bottom; ++Y)
79 {
80 InputBuffer = Buffer;
81
82 for (X = Left, Pixel = 0;
83 X < Right;
84 ++X, ++Pixel)
85 {
86 if (Pixel % 2 == 0)
87 {
88 /* Extract colors at every two pixels */
89 Colors = *InputBuffer++;
90
91 SetPixel(X, Y, Colors >> 4);
92 }
93 else
94 {
95 SetPixel(X, Y, Colors & 0x0F);
96 }
97 }
98
99 Buffer += Delta;
100 }
101}
102
103static VOID
104RleBitBlt(
105 _In_ ULONG Left,
106 _In_ ULONG Top,
107 _In_ ULONG Width,
108 _In_ ULONG Height,
109 _In_ PUCHAR Buffer)
110{
111 ULONG YDelta;
112 ULONG x;
113 ULONG RleValue, NewRleValue;
114 ULONG Color, Color2;
115 ULONG i, j;
116 ULONG Code;
117
118 PrepareForSetPixel();
119
120 /* Set Y height and current X value and start loop */
121 YDelta = Top + Height - 1;
122 x = Left;
123 for (;;)
124 {
125 /* Get the current value and advance in the buffer */
126 RleValue = *Buffer;
127 Buffer++;
128 if (RleValue)
129 {
130 /* Check if we've gone past the edge */
131 if ((x + RleValue) > (Width + Left))
132 {
133 /* Fixup the pixel value */
134 RleValue = Left - x + Width;
135 }
136
137 /* Get the new value */
138 NewRleValue = *Buffer;
139
140 /* Get the two colors */
141 Color = NewRleValue >> 4;
142 Color2 = NewRleValue & 0xF;
143
144 /* Increase buffer position */
145 Buffer++;
146
147 /* Check if we need to do a fill */
148 if (Color == Color2)
149 {
150 /* Do a fill and continue the loop */
151 RleValue += x;
152 VidSolidColorFill(x, YDelta, RleValue - 1, YDelta, (UCHAR)Color);
153 x = RleValue;
154 continue;
155 }
156
157 /* Check if the pixel value is 1 or below */
158 if (RleValue > 1)
159 {
160 /* Set loop variables */
161 for (i = (RleValue - 2) / 2 + 1; i > 0; --i)
162 {
163 /* Set the pixels */
164 SetPixel(x, YDelta, (UCHAR)Color);
165 x++;
166 SetPixel(x, YDelta, (UCHAR)Color2);
167 x++;
168
169 /* Decrease pixel value */
170 RleValue -= 2;
171 }
172 }
173
174 /* Check if there is any value at all */
175 if (RleValue)
176 {
177 /* Set the pixel and increase position */
178 SetPixel(x, YDelta, (UCHAR)Color);
179 x++;
180 }
181
182 /* Start over */
183 continue;
184 }
185
186 /* Get the current pixel value */
187 RleValue = *Buffer;
188 Code = RleValue;
189 switch (Code)
190 {
191 /* Case 0 */
192 case 0:
193 {
194 /* Set new x value, decrease distance and restart */
195 x = Left;
196 YDelta--;
197 Buffer++;
198 continue;
199 }
200
201 /* Case 1 */
202 case 1:
203 {
204 /* Done */
205 return;
206 }
207
208 /* Case 2 */
209 case 2:
210 {
211 /* Set new x value, decrease distance and restart */
212 Buffer++;
213 x += *Buffer;
214 Buffer++;
215 YDelta -= *Buffer;
216 Buffer++;
217 continue;
218 }
219
220 /* Other values */
221 default:
222 {
223 Buffer++;
224 break;
225 }
226 }
227
228 /* Check if we've gone past the edge */
229 if ((x + RleValue) > (Width + Left))
230 {
231 /* Set fixed up loop count */
232 i = RleValue - Left - Width + x;
233
234 /* Fixup pixel value */
235 RleValue -= i;
236 }
237 else
238 {
239 /* Clear loop count */
240 i = 0;
241 }
242
243 /* Check the value now */
244 if (RleValue > 1)
245 {
246 /* Set loop variables */
247 for (j = (RleValue - 2) / 2 + 1; j > 0; --j)
248 {
249 /* Get the new value */
250 NewRleValue = *Buffer;
251
252 /* Get the two colors */
253 Color = NewRleValue >> 4;
254 Color2 = NewRleValue & 0xF;
255
256 /* Increase buffer position */
257 Buffer++;
258
259 /* Set the pixels */
260 SetPixel(x, YDelta, (UCHAR)Color);
261 x++;
262 SetPixel(x, YDelta, (UCHAR)Color2);
263 x++;
264
265 /* Decrease pixel value */
266 RleValue -= 2;
267 }
268 }
269
270 /* Check if there is any value at all */
271 if (RleValue)
272 {
273 /* Set the pixel and increase position */
274 Color = *Buffer >> 4;
275 Buffer++;
276 SetPixel(x, YDelta, (UCHAR)Color);
277 x++;
278 i--;
279 }
280
281 /* Check loop count now */
282 if ((LONG)i > 0)
283 {
284 /* Decrease it */
285 i--;
286
287 /* Set new position */
288 Buffer = Buffer + (i / 2) + 1;
289 }
290
291 /* Check if we need to increase the buffer */
292 if ((ULONG_PTR)Buffer & 1) Buffer++;
293 }
294}
295
296/* PUBLIC FUNCTIONS ***********************************************************/
297
298VOID
299NTAPI
300VidBufferToScreenBlt(
301 _In_reads_bytes_(Delta * Height) PUCHAR Buffer,
302 _In_ ULONG Left,
303 _In_ ULONG Top,
304 _In_ ULONG Width,
305 _In_ ULONG Height,
306 _In_ ULONG Delta)
307{
308 /* Make sure we have a width and height */
309 if (!Width || !Height)
310 return;
311
312 /* Call the helper function */
313 BitBlt(Left, Top, Width, Height, Buffer, 4, Delta);
314}
315
316VOID
317NTAPI
318VidBitBlt(
319 _In_ PUCHAR Buffer,
320 _In_ ULONG Left,
321 _In_ ULONG Top)
322{
323 PBITMAPINFOHEADER BitmapInfoHeader;
324 LONG Delta;
325 PUCHAR BitmapOffset;
326 ULONG PaletteCount;
327
328 /* Get the Bitmap Header */
329 BitmapInfoHeader = (PBITMAPINFOHEADER)Buffer;
330
331 /* Initialize the palette */
332 PaletteCount = BitmapInfoHeader->biClrUsed ?
333 BitmapInfoHeader->biClrUsed : BV_MAX_COLORS;
334 InitPaletteWithTable((PULONG)(Buffer + BitmapInfoHeader->biSize),
335 PaletteCount);
336
337 /* Make sure we can support this bitmap */
338 ASSERT((BitmapInfoHeader->biBitCount * BitmapInfoHeader->biPlanes) <= 4);
339
340 /*
341 * Calculate the delta and align it on 32-bytes, then calculate
342 * the actual start of the bitmap data.
343 */
344 Delta = (BitmapInfoHeader->biBitCount * BitmapInfoHeader->biWidth) + 31;
345 Delta >>= 3;
346 Delta &= ~3;
347 BitmapOffset = Buffer + sizeof(BITMAPINFOHEADER) + PaletteCount * sizeof(ULONG);
348
349 /* Check the compression of the bitmap */
350 if (BitmapInfoHeader->biCompression == BI_RLE4)
351 {
352 /* Make sure we have a width and a height */
353 if ((BitmapInfoHeader->biWidth) && (BitmapInfoHeader->biHeight))
354 {
355 /* We can use RLE Bit Blt */
356 RleBitBlt(Left,
357 Top,
358 BitmapInfoHeader->biWidth,
359 BitmapInfoHeader->biHeight,
360 BitmapOffset);
361 }
362 }
363 else
364 {
365 /* Check if the height is negative */
366 if (BitmapInfoHeader->biHeight < 0)
367 {
368 /* Make it positive in the header */
369 BitmapInfoHeader->biHeight *= -1;
370 }
371 else
372 {
373 /* Update buffer offset */
374 BitmapOffset += ((BitmapInfoHeader->biHeight - 1) * Delta);
375 Delta *= -1;
376 }
377
378 /* Make sure we have a width and a height */
379 if ((BitmapInfoHeader->biWidth) && (BitmapInfoHeader->biHeight))
380 {
381 /* Do the BitBlt */
382 BitBlt(Left,
383 Top,
384 BitmapInfoHeader->biWidth,
385 BitmapInfoHeader->biHeight,
386 BitmapOffset,
387 BitmapInfoHeader->biBitCount,
388 Delta);
389 }
390 }
391}