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