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 UCHAR VidpTextColor = BV_COLOR_WHITE;
15
16 ULONG VidpCurrentX = 0;
17 ULONG VidpCurrentY = 0;
18
19 ULONG VidpScrollRegion[4] =
20 {
21 0,
22 0,
23 SCREEN_WIDTH - 1,
24 SCREEN_HEIGHT - 1
25 };
26
27 /*
28 * Boot video driver default palette is similar to the standard 16-color
29 * CGA palette, but it has Red and Blue channels swapped, and also dark
30 * and light gray colors swapped.
31 */
32 const RGBQUAD VidpDefaultPalette[BV_MAX_COLORS] =
33 {
34 RGB( 0, 0, 0), /* Black */
35 RGB(128, 0, 0), /* Red */
36 RGB( 0, 128, 0), /* Green */
37 RGB(128, 128, 0), /* Brown */
38 RGB( 0, 0, 128), /* Blue */
39 RGB(128, 0, 128), /* Magenta */
40 RGB( 0, 128, 128), /* Cyan */
41 RGB(128, 128, 128), /* Dark Gray */
42 RGB(192, 192, 192), /* Light Gray */
43 RGB(255, 0, 0), /* Light Red */
44 RGB( 0, 255, 0), /* Light Green */
45 RGB(255, 255, 0), /* Yellow */
46 RGB( 0, 0, 255), /* Light Blue */
47 RGB(255, 0, 255), /* Light Magenta */
48 RGB( 0, 255, 255), /* Light Cyan */
49 RGB(255, 255, 255), /* White */
50 };
51
52 static BOOLEAN ClearRow = FALSE;
53
54 /* PRIVATE FUNCTIONS **********************************************************/
55
56 static VOID
BitBlt(_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG Width,_In_ ULONG Height,_In_reads_bytes_ (Delta * Height)PUCHAR Buffer,_In_ ULONG BitsPerPixel,_In_ ULONG Delta)57 BitBlt(
58 _In_ ULONG Left,
59 _In_ ULONG Top,
60 _In_ ULONG Width,
61 _In_ ULONG Height,
62 _In_reads_bytes_(Delta * Height) PUCHAR Buffer,
63 _In_ ULONG BitsPerPixel,
64 _In_ ULONG Delta)
65 {
66 ULONG X, Y, Pixel;
67 UCHAR Colors;
68 PUCHAR InputBuffer;
69 const ULONG Bottom = Top + Height;
70 const ULONG Right = Left + Width;
71
72 /* Check if the buffer isn't 4bpp */
73 if (BitsPerPixel != 4)
74 {
75 /* FIXME: TODO */
76 DbgPrint("Unhandled BitBlt\n"
77 "%lux%lu @ (%lu|%lu)\n"
78 "Bits Per Pixel %lu\n"
79 "Buffer: %p. Delta: %lu\n",
80 Width,
81 Height,
82 Left,
83 Top,
84 BitsPerPixel,
85 Buffer,
86 Delta);
87 return;
88 }
89
90 PrepareForSetPixel();
91
92 /* 4bpp blitting */
93 for (Y = Top; Y < Bottom; ++Y)
94 {
95 InputBuffer = Buffer;
96
97 for (X = Left, Pixel = 0;
98 X < Right;
99 ++X, ++Pixel)
100 {
101 if (Pixel % 2 == 0)
102 {
103 /* Extract colors at every two pixels */
104 Colors = *InputBuffer++;
105
106 SetPixel(X, Y, Colors >> 4);
107 }
108 else
109 {
110 SetPixel(X, Y, Colors & 0x0F);
111 }
112 }
113
114 Buffer += Delta;
115 }
116 }
117
118 static VOID
RleBitBlt(_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG Width,_In_ ULONG Height,_In_ PUCHAR Buffer)119 RleBitBlt(
120 _In_ ULONG Left,
121 _In_ ULONG Top,
122 _In_ ULONG Width,
123 _In_ ULONG Height,
124 _In_ PUCHAR Buffer)
125 {
126 ULONG YDelta;
127 ULONG x;
128 ULONG RleValue, NewRleValue;
129 ULONG Color, Color2;
130 ULONG i, j;
131 ULONG Code;
132
133 PrepareForSetPixel();
134
135 /* Set Y height and current X value and start loop */
136 YDelta = Top + Height - 1;
137 x = Left;
138 for (;;)
139 {
140 /* Get the current value and advance in the buffer */
141 RleValue = *Buffer;
142 Buffer++;
143 if (RleValue)
144 {
145 /* Check if we've gone past the edge */
146 if ((x + RleValue) > (Width + Left))
147 {
148 /* Fixup the pixel value */
149 RleValue = Left - x + Width;
150 }
151
152 /* Get the new value */
153 NewRleValue = *Buffer;
154
155 /* Get the two colors */
156 Color = NewRleValue >> 4;
157 Color2 = NewRleValue & 0xF;
158
159 /* Increase buffer position */
160 Buffer++;
161
162 /* Check if we need to do a fill */
163 if (Color == Color2)
164 {
165 /* Do a fill and continue the loop */
166 RleValue += x;
167 VidSolidColorFill(x, YDelta, RleValue - 1, YDelta, (UCHAR)Color);
168 x = RleValue;
169 continue;
170 }
171
172 /* Check if the pixel value is 1 or below */
173 if (RleValue > 1)
174 {
175 /* Set loop variables */
176 for (i = (RleValue - 2) / 2 + 1; i > 0; --i)
177 {
178 /* Set the pixels */
179 SetPixel(x, YDelta, (UCHAR)Color);
180 x++;
181 SetPixel(x, YDelta, (UCHAR)Color2);
182 x++;
183
184 /* Decrease pixel value */
185 RleValue -= 2;
186 }
187 }
188
189 /* Check if there is any value at all */
190 if (RleValue)
191 {
192 /* Set the pixel and increase position */
193 SetPixel(x, YDelta, (UCHAR)Color);
194 x++;
195 }
196
197 /* Start over */
198 continue;
199 }
200
201 /* Get the current pixel value */
202 RleValue = *Buffer;
203 Code = RleValue;
204 switch (Code)
205 {
206 /* Case 0 */
207 case 0:
208 {
209 /* Set new x value, decrease distance and restart */
210 x = Left;
211 YDelta--;
212 Buffer++;
213 continue;
214 }
215
216 /* Case 1 */
217 case 1:
218 {
219 /* Done */
220 return;
221 }
222
223 /* Case 2 */
224 case 2:
225 {
226 /* Set new x value, decrease distance and restart */
227 Buffer++;
228 x += *Buffer;
229 Buffer++;
230 YDelta -= *Buffer;
231 Buffer++;
232 continue;
233 }
234
235 /* Other values */
236 default:
237 {
238 Buffer++;
239 break;
240 }
241 }
242
243 /* Check if we've gone past the edge */
244 if ((x + RleValue) > (Width + Left))
245 {
246 /* Set fixed up loop count */
247 i = RleValue - Left - Width + x;
248
249 /* Fixup pixel value */
250 RleValue -= i;
251 }
252 else
253 {
254 /* Clear loop count */
255 i = 0;
256 }
257
258 /* Check the value now */
259 if (RleValue > 1)
260 {
261 /* Set loop variables */
262 for (j = (RleValue - 2) / 2 + 1; j > 0; --j)
263 {
264 /* Get the new value */
265 NewRleValue = *Buffer;
266
267 /* Get the two colors */
268 Color = NewRleValue >> 4;
269 Color2 = NewRleValue & 0xF;
270
271 /* Increase buffer position */
272 Buffer++;
273
274 /* Set the pixels */
275 SetPixel(x, YDelta, (UCHAR)Color);
276 x++;
277 SetPixel(x, YDelta, (UCHAR)Color2);
278 x++;
279
280 /* Decrease pixel value */
281 RleValue -= 2;
282 }
283 }
284
285 /* Check if there is any value at all */
286 if (RleValue)
287 {
288 /* Set the pixel and increase position */
289 Color = *Buffer >> 4;
290 Buffer++;
291 SetPixel(x, YDelta, (UCHAR)Color);
292 x++;
293 i--;
294 }
295
296 /* Check loop count now */
297 if ((LONG)i > 0)
298 {
299 /* Decrease it */
300 i--;
301
302 /* Set new position */
303 Buffer = Buffer + (i / 2) + 1;
304 }
305
306 /* Check if we need to increase the buffer */
307 if ((ULONG_PTR)Buffer & 1) Buffer++;
308 }
309 }
310
311 /* PUBLIC FUNCTIONS ***********************************************************/
312
313 ULONG
314 NTAPI
VidSetTextColor(_In_ ULONG Color)315 VidSetTextColor(
316 _In_ ULONG Color)
317 {
318 ULONG OldColor;
319
320 /* Save the old color and set the new one */
321 OldColor = VidpTextColor;
322 VidpTextColor = Color;
323 return OldColor;
324 }
325
326 VOID
327 NTAPI
VidDisplayStringXY(_In_z_ PUCHAR String,_In_ ULONG Left,_In_ ULONG Top,_In_ BOOLEAN Transparent)328 VidDisplayStringXY(
329 _In_z_ PUCHAR String,
330 _In_ ULONG Left,
331 _In_ ULONG Top,
332 _In_ BOOLEAN Transparent)
333 {
334 ULONG BackColor;
335
336 /*
337 * If the caller wanted transparent, then send the special value (16),
338 * else use our default and call the helper routine.
339 */
340 BackColor = Transparent ? BV_COLOR_NONE : BV_COLOR_LIGHT_CYAN;
341
342 /* Loop every character and adjust the position */
343 for (; *String; ++String, Left += BOOTCHAR_WIDTH)
344 {
345 /* Display a character */
346 DisplayCharacter(*String, Left, Top, BV_COLOR_LIGHT_BLUE, BackColor);
347 }
348 }
349
350 VOID
351 NTAPI
VidSetScrollRegion(_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG Right,_In_ ULONG Bottom)352 VidSetScrollRegion(
353 _In_ ULONG Left,
354 _In_ ULONG Top,
355 _In_ ULONG Right,
356 _In_ ULONG Bottom)
357 {
358 /* Assert alignment */
359 ASSERT((Left % BOOTCHAR_WIDTH) == 0);
360 ASSERT((Right % BOOTCHAR_WIDTH) == BOOTCHAR_WIDTH - 1);
361
362 /* Set Scroll Region */
363 VidpScrollRegion[0] = Left;
364 VidpScrollRegion[1] = Top;
365 VidpScrollRegion[2] = Right;
366 VidpScrollRegion[3] = Bottom;
367
368 /* Set current X and Y */
369 VidpCurrentX = Left;
370 VidpCurrentY = Top;
371 }
372
373 VOID
374 NTAPI
VidDisplayString(_In_z_ PUCHAR String)375 VidDisplayString(
376 _In_z_ PUCHAR String)
377 {
378 /* Start looping the string */
379 for (; *String; ++String)
380 {
381 /* Treat new-line separately */
382 if (*String == '\n')
383 {
384 /* Modify Y position */
385 VidpCurrentY += BOOTCHAR_HEIGHT + 1;
386 if (VidpCurrentY + BOOTCHAR_HEIGHT > VidpScrollRegion[3])
387 {
388 /* Scroll the view and clear the current row */
389 DoScroll(BOOTCHAR_HEIGHT + 1);
390 VidpCurrentY -= BOOTCHAR_HEIGHT + 1;
391 PreserveRow(VidpCurrentY, BOOTCHAR_HEIGHT + 1, TRUE);
392 }
393 else
394 {
395 /* Preserve the current row */
396 PreserveRow(VidpCurrentY, BOOTCHAR_HEIGHT + 1, FALSE);
397 }
398
399 /* Update current X */
400 VidpCurrentX = VidpScrollRegion[0];
401
402 /* No need to clear this row */
403 ClearRow = FALSE;
404 }
405 else if (*String == '\r')
406 {
407 /* Update current X */
408 VidpCurrentX = VidpScrollRegion[0];
409
410 /* If a new-line does not follow we will clear the current row */
411 if (String[1] != '\n') ClearRow = TRUE;
412 }
413 else
414 {
415 /* Clear the current row if we had a return-carriage without a new-line */
416 if (ClearRow)
417 {
418 PreserveRow(VidpCurrentY, BOOTCHAR_HEIGHT + 1, TRUE);
419 ClearRow = FALSE;
420 }
421
422 /* Display this character */
423 DisplayCharacter(*String, VidpCurrentX, VidpCurrentY, VidpTextColor, BV_COLOR_NONE);
424 VidpCurrentX += BOOTCHAR_WIDTH;
425
426 /* Check if we should scroll */
427 if (VidpCurrentX + BOOTCHAR_WIDTH - 1 > VidpScrollRegion[2])
428 {
429 /* Update Y position and check if we should scroll it */
430 VidpCurrentY += BOOTCHAR_HEIGHT + 1;
431 if (VidpCurrentY + BOOTCHAR_HEIGHT > VidpScrollRegion[3])
432 {
433 /* Scroll the view and clear the current row */
434 DoScroll(BOOTCHAR_HEIGHT + 1);
435 VidpCurrentY -= BOOTCHAR_HEIGHT + 1;
436 PreserveRow(VidpCurrentY, BOOTCHAR_HEIGHT + 1, TRUE);
437 }
438 else
439 {
440 /* Preserve the current row */
441 PreserveRow(VidpCurrentY, BOOTCHAR_HEIGHT + 1, FALSE);
442 }
443
444 /* Update current X */
445 VidpCurrentX = VidpScrollRegion[0];
446 }
447 }
448 }
449 }
450
451 VOID
452 NTAPI
VidBufferToScreenBlt(_In_reads_bytes_ (Delta * Height)PUCHAR Buffer,_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG Width,_In_ ULONG Height,_In_ ULONG Delta)453 VidBufferToScreenBlt(
454 _In_reads_bytes_(Delta * Height) PUCHAR Buffer,
455 _In_ ULONG Left,
456 _In_ ULONG Top,
457 _In_ ULONG Width,
458 _In_ ULONG Height,
459 _In_ ULONG Delta)
460 {
461 /* Make sure we have a width and height */
462 if (!Width || !Height)
463 return;
464
465 /* Call the helper function */
466 BitBlt(Left, Top, Width, Height, Buffer, 4, Delta);
467 }
468
469 VOID
470 NTAPI
VidBitBlt(_In_ PUCHAR Buffer,_In_ ULONG Left,_In_ ULONG Top)471 VidBitBlt(
472 _In_ PUCHAR Buffer,
473 _In_ ULONG Left,
474 _In_ ULONG Top)
475 {
476 PBITMAPINFOHEADER BitmapInfoHeader;
477 LONG Delta;
478 PUCHAR BitmapOffset;
479 ULONG PaletteCount;
480
481 /* Get the Bitmap Header */
482 BitmapInfoHeader = (PBITMAPINFOHEADER)Buffer;
483
484 /* Initialize the palette */
485 PaletteCount = BitmapInfoHeader->biClrUsed ?
486 BitmapInfoHeader->biClrUsed : BV_MAX_COLORS;
487 InitPaletteWithTable((PULONG)(Buffer + BitmapInfoHeader->biSize),
488 PaletteCount);
489
490 /* Make sure we can support this bitmap */
491 ASSERT((BitmapInfoHeader->biBitCount * BitmapInfoHeader->biPlanes) <= 4);
492
493 /*
494 * Calculate the delta and align it on 32-bytes, then calculate
495 * the actual start of the bitmap data.
496 */
497 Delta = (BitmapInfoHeader->biBitCount * BitmapInfoHeader->biWidth) + 31;
498 Delta >>= 3;
499 Delta &= ~3;
500 BitmapOffset = Buffer + sizeof(BITMAPINFOHEADER) + PaletteCount * sizeof(ULONG);
501
502 /* Check the compression of the bitmap */
503 if (BitmapInfoHeader->biCompression == BI_RLE4)
504 {
505 /* Make sure we have a width and a height */
506 if ((BitmapInfoHeader->biWidth) && (BitmapInfoHeader->biHeight))
507 {
508 /* We can use RLE Bit Blt */
509 RleBitBlt(Left,
510 Top,
511 BitmapInfoHeader->biWidth,
512 BitmapInfoHeader->biHeight,
513 BitmapOffset);
514 }
515 }
516 else
517 {
518 /* Check if the height is negative */
519 if (BitmapInfoHeader->biHeight < 0)
520 {
521 /* Make it positive in the header */
522 BitmapInfoHeader->biHeight *= -1;
523 }
524 else
525 {
526 /* Update buffer offset */
527 BitmapOffset += ((BitmapInfoHeader->biHeight - 1) * Delta);
528 Delta *= -1;
529 }
530
531 /* Make sure we have a width and a height */
532 if ((BitmapInfoHeader->biWidth) && (BitmapInfoHeader->biHeight))
533 {
534 /* Do the BitBlt */
535 BitBlt(Left,
536 Top,
537 BitmapInfoHeader->biWidth,
538 BitmapInfoHeader->biHeight,
539 BitmapOffset,
540 BitmapInfoHeader->biBitCount,
541 Delta);
542 }
543 }
544 }
545