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