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