xref: /reactos/ntoskrnl/inbv/bootanim.c (revision 1a920d9a)
1 /*
2  * PROJECT:     ReactOS Kernel
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Boot Theme & Animation
5  * COPYRIGHT:   Copyright 2007 Alex Ionescu (alex.ionescu@reactos.org)
6  *              Copyright 2007 Hervé Poussineau (hpoussin@reactos.org)
7  *              Copyright 2012-2022 Hermès Bélusca-Maïto
8  *              Copyright 2017-2018 Stanislav Motylkov
9  *              Copyright 2019-2020 Yaroslav Kibysh
10  */
11 
12 /* INCLUDES ******************************************************************/
13 
14 #include <ntoskrnl.h>
15 #include "inbv/logo.h"
16 
17 /* See also mm/ARM3/miarm.h */
18 #define MM_READONLY     1   // PAGE_READONLY
19 #define MM_READWRITE    4   // PAGE_WRITECOPY
20 
21 /* GLOBALS *******************************************************************/
22 
23 /*
24  * ReactOS uses the same boot screen for all the products.
25  *
26  * Enable this define when ReactOS will have different SKUs
27  * (Workstation, Server, Storage Server, Cluster Server, etc...).
28  */
29 // #define REACTOS_SKUS
30 
31 typedef enum _ROT_BAR_TYPE
32 {
33     RB_UNSPECIFIED,
34     RB_SQUARE_CELLS,
35     RB_PROGRESS_BAR
36 } ROT_BAR_TYPE;
37 
38 /*
39  * BitBltAligned() alignments
40  */
41 typedef enum _BBLT_VERT_ALIGNMENT
42 {
43     AL_VERTICAL_TOP = 0,
44     AL_VERTICAL_CENTER,
45     AL_VERTICAL_BOTTOM
46 } BBLT_VERT_ALIGNMENT;
47 
48 typedef enum _BBLT_HORZ_ALIGNMENT
49 {
50     AL_HORIZONTAL_LEFT = 0,
51     AL_HORIZONTAL_CENTER,
52     AL_HORIZONTAL_RIGHT
53 } BBLT_HORZ_ALIGNMENT;
54 
55 /*
56  * Enable this define when Inbv will support rotating progress bar.
57  */
58 #define INBV_ROTBAR_IMPLEMENTED
59 
60 extern ULONG ProgressBarLeft, ProgressBarTop;
61 extern BOOLEAN ShowProgressBar;
62 
63 #ifdef INBV_ROTBAR_IMPLEMENTED
64 /*
65  * Change this to modify progress bar behaviour
66  */
67 #define ROT_BAR_DEFAULT_MODE    RB_PROGRESS_BAR
68 
69 /*
70  * Values for PltRotBarStatus:
71  * - PltRotBarStatus == 1, do palette fading-in (done elsewhere in ReactOS);
72  * - PltRotBarStatus == 2, do rotation bar animation;
73  * - PltRotBarStatus == 3, stop the animation thread.
74  * - Any other value is ignored and the animation thread continues to run.
75  */
76 typedef enum _ROT_BAR_STATUS
77 {
78     RBS_FADEIN = 1,
79     RBS_ANIMATE,
80     RBS_STOP_ANIMATE,
81     RBS_STATUS_MAX
82 } ROT_BAR_STATUS;
83 
84 static BOOLEAN RotBarThreadActive = FALSE;
85 static ROT_BAR_TYPE RotBarSelection = RB_UNSPECIFIED;
86 static ROT_BAR_STATUS PltRotBarStatus = 0;
87 static UCHAR RotBarBuffer[24 * 9];
88 static UCHAR RotLineBuffer[SCREEN_WIDTH * 6];
89 #endif
90 
91 
92 /* FADE-IN FUNCTION **********************************************************/
93 
94 /** From include/psdk/wingdi.h and bootvid/precomp.h **/
95 typedef struct tagRGBQUAD
96 {
97     UCHAR rgbBlue;
98     UCHAR rgbGreen;
99     UCHAR rgbRed;
100     UCHAR rgbReserved;
101 } RGBQUAD, *LPRGBQUAD;
102 
103 //
104 // Bitmap Header
105 //
106 typedef struct tagBITMAPINFOHEADER
107 {
108     ULONG  biSize;
109     LONG   biWidth;
110     LONG   biHeight;
111     USHORT biPlanes;
112     USHORT biBitCount;
113     ULONG  biCompression;
114     ULONG  biSizeImage;
115     LONG   biXPelsPerMeter;
116     LONG   biYPelsPerMeter;
117     ULONG  biClrUsed;
118     ULONG  biClrImportant;
119 } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
120 /*******************************/
121 
122 static RGBQUAD MainPalette[16];
123 
124 #define PALETTE_FADE_STEPS  12
125 #define PALETTE_FADE_TIME   (15 * 1000) /* 15 ms */
126 
127 static VOID
128 BootLogoFadeIn(VOID)
129 {
130     UCHAR PaletteBitmapBuffer[sizeof(BITMAPINFOHEADER) + sizeof(MainPalette)];
131     PBITMAPINFOHEADER PaletteBitmap = (PBITMAPINFOHEADER)PaletteBitmapBuffer;
132     LPRGBQUAD Palette = (LPRGBQUAD)(PaletteBitmapBuffer + sizeof(BITMAPINFOHEADER));
133     ULONG Iteration, Index, ClrUsed;
134 
135     LARGE_INTEGER Delay;
136     Delay.QuadPart = -(PALETTE_FADE_TIME * 10);
137 
138     /* Check if we are installed and we own the display */
139     if (!InbvBootDriverInstalled ||
140         (InbvGetDisplayState() != INBV_DISPLAY_STATE_OWNED))
141     {
142         return;
143     }
144 
145     /*
146      * Build a bitmap containing the fade-in palette. The palette entries
147      * are then processed in a loop and set using VidBitBlt function.
148      */
149     ClrUsed = RTL_NUMBER_OF(MainPalette);
150     RtlZeroMemory(PaletteBitmap, sizeof(BITMAPINFOHEADER));
151     PaletteBitmap->biSize = sizeof(BITMAPINFOHEADER);
152     PaletteBitmap->biBitCount = 4;
153     PaletteBitmap->biClrUsed = ClrUsed;
154 
155     /*
156      * Main animation loop.
157      */
158     for (Iteration = 0; Iteration <= PALETTE_FADE_STEPS; ++Iteration)
159     {
160         for (Index = 0; Index < ClrUsed; Index++)
161         {
162             Palette[Index].rgbRed = (UCHAR)
163                 (MainPalette[Index].rgbRed * Iteration / PALETTE_FADE_STEPS);
164             Palette[Index].rgbGreen = (UCHAR)
165                 (MainPalette[Index].rgbGreen * Iteration / PALETTE_FADE_STEPS);
166             Palette[Index].rgbBlue = (UCHAR)
167                 (MainPalette[Index].rgbBlue * Iteration / PALETTE_FADE_STEPS);
168         }
169 
170         /* Do the animation */
171         InbvAcquireLock();
172         VidBitBlt(PaletteBitmapBuffer, 0, 0);
173         InbvReleaseLock();
174 
175         /* Wait for a bit */
176         KeDelayExecutionThread(KernelMode, FALSE, &Delay);
177     }
178 }
179 
180 static VOID
181 BitBltPalette(
182     IN PVOID Image,
183     IN BOOLEAN NoPalette,
184     IN ULONG X,
185     IN ULONG Y)
186 {
187     LPRGBQUAD Palette;
188     RGBQUAD OrigPalette[RTL_NUMBER_OF(MainPalette)];
189 
190     /* If requested, remove the palette from the image */
191     if (NoPalette)
192     {
193         /* Get bitmap header and palette */
194         PBITMAPINFOHEADER BitmapInfoHeader = Image;
195         Palette = (LPRGBQUAD)((PUCHAR)Image + BitmapInfoHeader->biSize);
196 
197         /* Save the image original palette and remove palette information */
198         RtlCopyMemory(OrigPalette, Palette, sizeof(OrigPalette));
199         RtlZeroMemory(Palette, sizeof(OrigPalette));
200     }
201 
202     /* Draw the image */
203     InbvBitBlt(Image, X, Y);
204 
205     /* Restore the image original palette */
206     if (NoPalette)
207     {
208         RtlCopyMemory(Palette, OrigPalette, sizeof(OrigPalette));
209     }
210 }
211 
212 static VOID
213 BitBltAligned(
214     IN PVOID Image,
215     IN BOOLEAN NoPalette,
216     IN BBLT_HORZ_ALIGNMENT HorizontalAlignment,
217     IN BBLT_VERT_ALIGNMENT VerticalAlignment,
218     IN ULONG MarginLeft,
219     IN ULONG MarginTop,
220     IN ULONG MarginRight,
221     IN ULONG MarginBottom)
222 {
223     PBITMAPINFOHEADER BitmapInfoHeader = Image;
224     ULONG X, Y;
225 
226     /* Calculate X */
227     switch (HorizontalAlignment)
228     {
229         case AL_HORIZONTAL_LEFT:
230             X = MarginLeft - MarginRight;
231             break;
232 
233         case AL_HORIZONTAL_CENTER:
234             X = MarginLeft - MarginRight + (SCREEN_WIDTH - BitmapInfoHeader->biWidth + 1) / 2;
235             break;
236 
237         case AL_HORIZONTAL_RIGHT:
238             X = MarginLeft - MarginRight + SCREEN_WIDTH - BitmapInfoHeader->biWidth;
239             break;
240 
241         default:
242             /* Unknown */
243             return;
244     }
245 
246     /* Calculate Y */
247     switch (VerticalAlignment)
248     {
249         case AL_VERTICAL_TOP:
250             Y = MarginTop - MarginBottom;
251             break;
252 
253         case AL_VERTICAL_CENTER:
254             Y = MarginTop - MarginBottom + (SCREEN_HEIGHT - BitmapInfoHeader->biHeight + 1) / 2;
255             break;
256 
257         case AL_VERTICAL_BOTTOM:
258             Y = MarginTop - MarginBottom + SCREEN_HEIGHT - BitmapInfoHeader->biHeight;
259             break;
260 
261         default:
262             /* Unknown */
263             return;
264     }
265 
266     /* Finally draw the image */
267     BitBltPalette(Image, NoPalette, X, Y);
268 }
269 
270 /* FUNCTIONS *****************************************************************/
271 
272 CODE_SEG("INIT")
273 BOOLEAN
274 NTAPI
275 BootAnimInitialize(
276     _In_ PLOADER_PARAMETER_BLOCK LoaderBlock,
277     _In_ ULONG Count)
278 {
279 #if 0
280     ULONG i;
281 
282     /* Quit if we're already installed */
283     if (InbvBootDriverInstalled) return TRUE;
284 
285     /* Find bitmap resources in the kernel */
286     ResourceCount = min(Count, RTL_NUMBER_OF(ResourceList) - 1);
287     for (i = 1; i <= ResourceCount; i++)
288     {
289         /* Do the lookup */
290         ResourceList[i] = FindBitmapResource(LoaderBlock, i);
291     }
292 
293     /* Set the progress bar ranges */
294     InbvSetProgressBarSubset(0, 100);
295 #endif
296 
297     /* Return install state */
298     return TRUE;
299 }
300 
301 /**
302  * @brief
303  * Ticks the progress bar. Used by InbvUpdateProgressBar() and related.
304  *
305  * @param[in]   SubPercentTimes100
306  * The progress percentage, scaled up by 100.
307  *
308  * @return None.
309  **/
310 VOID
311 NTAPI
312 BootAnimTickProgressBar(
313     _In_ ULONG SubPercentTimes100)
314 {
315     ULONG FillCount;
316 
317     /* Make sure the progress bar is enabled, that we own and are installed */
318     ASSERT(ShowProgressBar &&
319            InbvBootDriverInstalled &&
320            (InbvGetDisplayState() == INBV_DISPLAY_STATE_OWNED));
321 
322     /* Compute fill count */
323     FillCount = VID_PROGRESS_BAR_WIDTH * SubPercentTimes100 / (100 * 100);
324 
325     /* Acquire the lock */
326     InbvAcquireLock();
327 
328     /* Fill the progress bar */
329     VidSolidColorFill(ProgressBarLeft,
330                       ProgressBarTop,
331                       ProgressBarLeft + FillCount,
332                       ProgressBarTop + VID_PROGRESS_BAR_HEIGHT,
333                       BV_COLOR_WHITE);
334 
335     /* Release the lock */
336     InbvReleaseLock();
337 }
338 
339 #ifdef INBV_ROTBAR_IMPLEMENTED
340 static
341 VOID
342 NTAPI
343 InbvRotationThread(
344     _In_ PVOID Context)
345 {
346     ULONG X, Y, Index, Total;
347     LARGE_INTEGER Delay = {{0}};
348 
349     InbvAcquireLock();
350     if (RotBarSelection == RB_SQUARE_CELLS)
351     {
352         Index = 0;
353     }
354     else
355     {
356         Index = 32;
357     }
358     X = ProgressBarLeft + 2;
359     Y = ProgressBarTop + 2;
360     InbvReleaseLock();
361 
362     while (InbvGetDisplayState() == INBV_DISPLAY_STATE_OWNED)
363     {
364         /* Wait for a bit */
365         KeDelayExecutionThread(KernelMode, FALSE, &Delay);
366 
367         InbvAcquireLock();
368 
369         /* Unknown unexpected command */
370         ASSERT(PltRotBarStatus < RBS_STATUS_MAX);
371 
372         if (PltRotBarStatus == RBS_STOP_ANIMATE)
373         {
374             /* Stop the thread */
375             InbvReleaseLock();
376             break;
377         }
378 
379         if (RotBarSelection == RB_SQUARE_CELLS)
380         {
381             Delay.QuadPart = -800000; // 80 ms
382             Total = 18;
383             Index %= Total;
384 
385             if (Index >= 3)
386             {
387                 /* Fill previous bar position */
388                 VidSolidColorFill(X + ((Index - 3) * 8), Y, (X + ((Index - 3) * 8)) + 8 - 1, Y + 9 - 1, BV_COLOR_BLACK);
389             }
390             if (Index < Total - 1)
391             {
392                 /* Draw the progress bar bit */
393                 if (Index < 2)
394                 {
395                     /* Appearing from the left */
396                     VidBufferToScreenBlt(RotBarBuffer + 8 * (2 - Index) / 2, X, Y, 22 - 8 * (2 - Index), 9, 24);
397                 }
398                 else if (Index >= Total - 3)
399                 {
400                     /* Hiding to the right */
401                     VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22 - 8 * (4 - (Total - Index)), 9, 24);
402                 }
403                 else
404                 {
405                     VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22, 9, 24);
406                 }
407             }
408             Index++;
409         }
410         else if (RotBarSelection == RB_PROGRESS_BAR)
411         {
412             Delay.QuadPart = -600000; // 60 ms
413             Total = SCREEN_WIDTH;
414             Index %= Total;
415 
416             /* Right part */
417             VidBufferToScreenBlt(RotLineBuffer, Index, SCREEN_HEIGHT-6, SCREEN_WIDTH - Index, 6, SCREEN_WIDTH);
418             if (Index > 0)
419             {
420                 /* Left part */
421                 VidBufferToScreenBlt(RotLineBuffer + (SCREEN_WIDTH - Index) / 2, 0, SCREEN_HEIGHT-6, Index - 2, 6, SCREEN_WIDTH);
422             }
423             Index += 32;
424         }
425 
426         InbvReleaseLock();
427     }
428 
429     PsTerminateSystemThread(STATUS_SUCCESS);
430 }
431 
432 CODE_SEG("INIT")
433 VOID
434 NTAPI
435 InbvRotBarInit(VOID)
436 {
437     PltRotBarStatus = RBS_FADEIN;
438     /* Perform other initialization if needed */
439 }
440 #endif
441 
442 CODE_SEG("INIT")
443 static
444 VOID
445 NTAPI
446 DisplayFilter(
447     _Inout_ PCHAR* String)
448 {
449     /* Windows hack to skip first dots displayed by AUTOCHK */
450     static BOOLEAN DotHack = TRUE;
451 
452     /* If "." is given set *String to empty string */
453     if (DotHack && strcmp(*String, ".") == 0)
454         *String = "";
455 
456     if (**String)
457     {
458         /* Remove the filter */
459         InbvInstallDisplayStringFilter(NULL);
460 
461         DotHack = FALSE;
462 
463         /* Draw text screen */
464         DisplayBootBitmap(TRUE);
465     }
466 }
467 
468 CODE_SEG("INIT")
469 VOID
470 NTAPI
471 DisplayBootBitmap(
472     _In_ BOOLEAN TextMode)
473 {
474     PVOID BootCopy = NULL, BootProgress = NULL, BootLogo = NULL, Header = NULL, Footer = NULL;
475 
476 #ifdef INBV_ROTBAR_IMPLEMENTED
477     UCHAR Buffer[24 * 9];
478     PVOID Bar = NULL, LineBmp = NULL;
479     ROT_BAR_TYPE TempRotBarSelection = RB_UNSPECIFIED;
480     NTSTATUS Status;
481     HANDLE ThreadHandle = NULL;
482 #endif
483 
484 #ifdef REACTOS_SKUS
485     PVOID Text = NULL;
486 #endif
487 
488 #ifdef INBV_ROTBAR_IMPLEMENTED
489     /* Check if the animation thread has already been created */
490     if (RotBarThreadActive)
491     {
492         /* Yes, just reset the progress bar but keep the thread alive */
493         InbvAcquireLock();
494         RotBarSelection = RB_UNSPECIFIED;
495         InbvReleaseLock();
496     }
497 #endif
498 
499     ShowProgressBar = FALSE;
500 
501     /* Check if this is text mode */
502     if (TextMode)
503     {
504         /*
505          * Make the kernel resource section temporarily writable,
506          * as we are going to change the bitmaps' palette in place.
507          */
508         MmChangeKernelResourceSectionProtection(MM_READWRITE);
509 
510         /* Check the type of the OS: workstation or server */
511         if (SharedUserData->NtProductType == NtProductWinNt)
512         {
513             /* Workstation; set colors */
514             InbvSetTextColor(BV_COLOR_WHITE);
515             InbvSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_DARK_GRAY);
516             InbvSolidColorFill(0, VID_FOOTER_BG_TOP, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_RED);
517 
518             /* Get resources */
519             Header = InbvGetResourceAddress(IDB_WKSTA_HEADER);
520             Footer = InbvGetResourceAddress(IDB_WKSTA_FOOTER);
521         }
522         else
523         {
524             /* Server; set colors */
525             InbvSetTextColor(BV_COLOR_LIGHT_CYAN);
526             InbvSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_CYAN);
527             InbvSolidColorFill(0, VID_FOOTER_BG_TOP, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_RED);
528 
529             /* Get resources */
530             Header = InbvGetResourceAddress(IDB_SERVER_HEADER);
531             Footer = InbvGetResourceAddress(IDB_SERVER_FOOTER);
532         }
533 
534         /* Set the scrolling region */
535         InbvSetScrollRegion(VID_SCROLL_AREA_LEFT, VID_SCROLL_AREA_TOP,
536                             VID_SCROLL_AREA_RIGHT, VID_SCROLL_AREA_BOTTOM);
537 
538         /* Make sure we have resources */
539         if (Header && Footer)
540         {
541             /* BitBlt them on the screen */
542             BitBltAligned(Footer,
543                           TRUE,
544                           AL_HORIZONTAL_CENTER,
545                           AL_VERTICAL_BOTTOM,
546                           0, 0, 0, 59);
547             BitBltAligned(Header,
548                           FALSE,
549                           AL_HORIZONTAL_CENTER,
550                           AL_VERTICAL_TOP,
551                           0, 0, 0, 0);
552         }
553 
554         /* Restore the kernel resource section protection to be read-only */
555         MmChangeKernelResourceSectionProtection(MM_READONLY);
556     }
557     else
558     {
559         /* Is the boot driver installed? */
560         if (!InbvBootDriverInstalled) return;
561 
562         /*
563          * Make the kernel resource section temporarily writable,
564          * as we are going to change the bitmaps' palette in place.
565          */
566         MmChangeKernelResourceSectionProtection(MM_READWRITE);
567 
568         /* Load boot screen logo */
569         BootLogo = InbvGetResourceAddress(IDB_LOGO_DEFAULT);
570 
571 #ifdef REACTOS_SKUS
572         Text = NULL;
573         if (SharedUserData->NtProductType == NtProductWinNt)
574         {
575 #ifdef INBV_ROTBAR_IMPLEMENTED
576             /* Workstation product, use appropriate status bar color */
577             Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
578 #endif
579         }
580         else
581         {
582             /* Display correct branding based on server suite */
583             if (ExVerifySuite(StorageServer))
584             {
585                 /* Storage Server Edition */
586                 Text = InbvGetResourceAddress(IDB_STORAGE_SERVER);
587             }
588             else if (ExVerifySuite(ComputeServer))
589             {
590                 /* Compute Cluster Edition */
591                 Text = InbvGetResourceAddress(IDB_CLUSTER_SERVER);
592             }
593             else
594             {
595                 /* Normal edition */
596                 Text = InbvGetResourceAddress(IDB_SERVER_LOGO);
597             }
598 
599 #ifdef INBV_ROTBAR_IMPLEMENTED
600             /* Server product, use appropriate status bar color */
601             Bar = InbvGetResourceAddress(IDB_BAR_DEFAULT);
602 #endif
603         }
604 #else
605         /* Use default status bar */
606         Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
607 #endif
608 
609         /* Make sure we have a logo */
610         if (BootLogo)
611         {
612             /* Save the main image palette for implementing the fade-in effect */
613             PBITMAPINFOHEADER BitmapInfoHeader = BootLogo;
614             LPRGBQUAD Palette = (LPRGBQUAD)((PUCHAR)BootLogo + BitmapInfoHeader->biSize);
615             RtlCopyMemory(MainPalette, Palette, sizeof(MainPalette));
616 
617             /* Draw the logo at the center of the screen */
618             BitBltAligned(BootLogo,
619                           TRUE,
620                           AL_HORIZONTAL_CENTER,
621                           AL_VERTICAL_CENTER,
622                           0, 0, 0, 34);
623 
624 #ifdef INBV_ROTBAR_IMPLEMENTED
625             /* Choose progress bar */
626             TempRotBarSelection = ROT_BAR_DEFAULT_MODE;
627 #endif
628 
629             /* Set progress bar coordinates and display it */
630             InbvSetProgressBarCoordinates(VID_PROGRESS_BAR_LEFT,
631                                           VID_PROGRESS_BAR_TOP);
632 
633 #ifdef REACTOS_SKUS
634             /* Check for non-workstation products */
635             if (SharedUserData->NtProductType != NtProductWinNt)
636             {
637                 /* Overwrite part of the logo for a server product */
638                 InbvScreenToBufferBlt(Buffer, VID_SKU_SAVE_AREA_LEFT,
639                                       VID_SKU_SAVE_AREA_TOP, 7, 7, 8);
640                 InbvSolidColorFill(VID_SKU_AREA_LEFT, VID_SKU_AREA_TOP,
641                                    VID_SKU_AREA_RIGHT, VID_SKU_AREA_BOTTOM, BV_COLOR_BLACK);
642                 InbvBufferToScreenBlt(Buffer, VID_SKU_SAVE_AREA_LEFT,
643                                       VID_SKU_SAVE_AREA_TOP, 7, 7, 8);
644 
645                 /* In setup mode, you haven't selected a SKU yet */
646                 if (ExpInTextModeSetup) Text = NULL;
647             }
648 #endif
649         }
650 
651         /* Load and draw progress bar bitmap */
652         BootProgress = InbvGetResourceAddress(IDB_PROGRESS_BAR);
653         BitBltAligned(BootProgress,
654                       TRUE,
655                       AL_HORIZONTAL_CENTER,
656                       AL_VERTICAL_CENTER,
657                       0, 118, 0, 0);
658 
659         /* Load and draw copyright text bitmap */
660         BootCopy = InbvGetResourceAddress(IDB_COPYRIGHT);
661         BitBltAligned(BootCopy,
662                       TRUE,
663                       AL_HORIZONTAL_LEFT,
664                       AL_VERTICAL_BOTTOM,
665                       22, 0, 0, 20);
666 
667 #ifdef REACTOS_SKUS
668         /* Draw the SKU text if it exits */
669         if (Text)
670             BitBltPalette(Text, TRUE, VID_SKU_TEXT_LEFT, VID_SKU_TEXT_TOP);
671 #endif
672 
673 #ifdef INBV_ROTBAR_IMPLEMENTED
674         if ((TempRotBarSelection == RB_SQUARE_CELLS) && Bar)
675         {
676             /* Save previous screen pixels to buffer */
677             InbvScreenToBufferBlt(Buffer, 0, 0, 22, 9, 24);
678             /* Draw the progress bar bit */
679             BitBltPalette(Bar, TRUE, 0, 0);
680             /* Store it in global buffer */
681             InbvScreenToBufferBlt(RotBarBuffer, 0, 0, 22, 9, 24);
682             /* Restore screen pixels */
683             InbvBufferToScreenBlt(Buffer, 0, 0, 22, 9, 24);
684         }
685 
686         /*
687          * Add a rotating bottom horizontal bar when using a progress bar,
688          * to show that ReactOS can be still alive when the bar does not
689          * appear to progress.
690          */
691         if (TempRotBarSelection == RB_PROGRESS_BAR)
692         {
693             LineBmp = InbvGetResourceAddress(IDB_ROTATING_LINE);
694             if (LineBmp)
695             {
696                 /* Draw the line and store it in global buffer */
697                 BitBltPalette(LineBmp, TRUE, 0, SCREEN_HEIGHT-6);
698                 InbvScreenToBufferBlt(RotLineBuffer, 0, SCREEN_HEIGHT-6, SCREEN_WIDTH, 6, SCREEN_WIDTH);
699             }
700         }
701         else
702         {
703             /* Hide the simple progress bar if not used */
704             ShowProgressBar = FALSE;
705         }
706 #endif
707 
708         /* Restore the kernel resource section protection to be read-only */
709         MmChangeKernelResourceSectionProtection(MM_READONLY);
710 
711         /* Display the boot logo and fade it in */
712         BootLogoFadeIn();
713 
714 #ifdef INBV_ROTBAR_IMPLEMENTED
715         if (!RotBarThreadActive && TempRotBarSelection != RB_UNSPECIFIED)
716         {
717             /* Start the animation thread */
718             Status = PsCreateSystemThread(&ThreadHandle,
719                                           0,
720                                           NULL,
721                                           NULL,
722                                           NULL,
723                                           InbvRotationThread,
724                                           NULL);
725             if (NT_SUCCESS(Status))
726             {
727                 /* The thread has started, close the handle as we don't need it */
728                 RotBarThreadActive = TRUE;
729                 ObCloseHandle(ThreadHandle, KernelMode);
730             }
731         }
732 #endif
733 
734         /* Set filter which will draw text display if needed */
735         InbvInstallDisplayStringFilter(DisplayFilter);
736     }
737 
738 #ifdef INBV_ROTBAR_IMPLEMENTED
739     /* Do we have the animation thread? */
740     if (RotBarThreadActive)
741     {
742         /* We do, initialize the progress bar */
743         InbvAcquireLock();
744         RotBarSelection = TempRotBarSelection;
745         InbvRotBarInit();
746         InbvReleaseLock();
747     }
748 #endif
749 }
750 
751 CODE_SEG("INIT")
752 VOID
753 NTAPI
754 FinalizeBootLogo(VOID)
755 {
756     /* Acquire lock and check the display state */
757     InbvAcquireLock();
758     if (InbvGetDisplayState() == INBV_DISPLAY_STATE_OWNED)
759     {
760         /* Clear the screen */
761         VidSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_BLACK);
762     }
763 
764     /* Reset progress bar and lock */
765 #ifdef INBV_ROTBAR_IMPLEMENTED
766     PltRotBarStatus = RBS_STOP_ANIMATE;
767     RotBarThreadActive = FALSE;
768 #endif
769     InbvReleaseLock();
770 }
771