xref: /reactos/drivers/base/bootvid/common.c (revision 3b62a89d)
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