mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
544 lines
14 KiB
C
544 lines
14 KiB
C
/*
|
|
* PROJECT: ReactOS Boot Video Driver
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Platform-independent common helpers and defines
|
|
* COPYRIGHT: Copyright 2010 Gregor Schneider <gregor.schneider@reactos.org>
|
|
* Copyright 2011 Rafal Harabien <rafalh@reactos.org>
|
|
* Copyright 2020 Stanislav Motylkov <x86corez@gmail.com>
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
UCHAR VidpTextColor = BV_COLOR_WHITE;
|
|
|
|
ULONG VidpCurrentX = 0;
|
|
ULONG VidpCurrentY = 0;
|
|
|
|
ULONG VidpScrollRegion[4] =
|
|
{
|
|
0,
|
|
0,
|
|
SCREEN_WIDTH - 1,
|
|
SCREEN_HEIGHT - 1
|
|
};
|
|
|
|
/*
|
|
* Boot video driver default palette is similar to the standard 16-color
|
|
* CGA palette, but it has Red and Blue channels swapped, and also dark
|
|
* and light gray colors swapped.
|
|
*/
|
|
const RGBQUAD VidpDefaultPalette[BV_MAX_COLORS] =
|
|
{
|
|
RGB( 0, 0, 0), /* Black */
|
|
RGB(128, 0, 0), /* Red */
|
|
RGB( 0, 128, 0), /* Green */
|
|
RGB(128, 128, 0), /* Brown */
|
|
RGB( 0, 0, 128), /* Blue */
|
|
RGB(128, 0, 128), /* Magenta */
|
|
RGB( 0, 128, 128), /* Cyan */
|
|
RGB(128, 128, 128), /* Dark Gray */
|
|
RGB(192, 192, 192), /* Light Gray */
|
|
RGB(255, 0, 0), /* Light Red */
|
|
RGB( 0, 255, 0), /* Light Green */
|
|
RGB(255, 255, 0), /* Yellow */
|
|
RGB( 0, 0, 255), /* Light Blue */
|
|
RGB(255, 0, 255), /* Light Magenta */
|
|
RGB( 0, 255, 255), /* Light Cyan */
|
|
RGB(255, 255, 255), /* White */
|
|
};
|
|
|
|
static BOOLEAN ClearRow = FALSE;
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
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)
|
|
{
|
|
ULONG X, Y, Pixel;
|
|
UCHAR Colors;
|
|
PUCHAR InputBuffer;
|
|
const ULONG Bottom = Top + Height;
|
|
const ULONG Right = Left + Width;
|
|
|
|
/* Check if the buffer isn't 4bpp */
|
|
if (BitsPerPixel != 4)
|
|
{
|
|
/* FIXME: TODO */
|
|
DbgPrint("Unhandled BitBlt\n"
|
|
"%lux%lu @ (%lu|%lu)\n"
|
|
"Bits Per Pixel %lu\n"
|
|
"Buffer: %p. Delta: %lu\n",
|
|
Width,
|
|
Height,
|
|
Left,
|
|
Top,
|
|
BitsPerPixel,
|
|
Buffer,
|
|
Delta);
|
|
return;
|
|
}
|
|
|
|
PrepareForSetPixel();
|
|
|
|
/* 4bpp blitting */
|
|
for (Y = Top; Y < Bottom; ++Y)
|
|
{
|
|
InputBuffer = Buffer;
|
|
|
|
for (X = Left, Pixel = 0;
|
|
X < Right;
|
|
++X, ++Pixel)
|
|
{
|
|
if (Pixel % 2 == 0)
|
|
{
|
|
/* Extract colors at every two pixels */
|
|
Colors = *InputBuffer++;
|
|
|
|
SetPixel(X, Y, Colors >> 4);
|
|
}
|
|
else
|
|
{
|
|
SetPixel(X, Y, Colors & 0x0F);
|
|
}
|
|
}
|
|
|
|
Buffer += Delta;
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
RleBitBlt(
|
|
_In_ ULONG Left,
|
|
_In_ ULONG Top,
|
|
_In_ ULONG Width,
|
|
_In_ ULONG Height,
|
|
_In_ PUCHAR Buffer)
|
|
{
|
|
ULONG YDelta;
|
|
ULONG x;
|
|
ULONG RleValue, NewRleValue;
|
|
ULONG Color, Color2;
|
|
ULONG i, j;
|
|
ULONG Code;
|
|
|
|
PrepareForSetPixel();
|
|
|
|
/* Set Y height and current X value and start loop */
|
|
YDelta = Top + Height - 1;
|
|
x = Left;
|
|
for (;;)
|
|
{
|
|
/* Get the current value and advance in the buffer */
|
|
RleValue = *Buffer;
|
|
Buffer++;
|
|
if (RleValue)
|
|
{
|
|
/* Check if we've gone past the edge */
|
|
if ((x + RleValue) > (Width + Left))
|
|
{
|
|
/* Fixup the pixel value */
|
|
RleValue = Left - x + Width;
|
|
}
|
|
|
|
/* Get the new value */
|
|
NewRleValue = *Buffer;
|
|
|
|
/* Get the two colors */
|
|
Color = NewRleValue >> 4;
|
|
Color2 = NewRleValue & 0xF;
|
|
|
|
/* Increase buffer position */
|
|
Buffer++;
|
|
|
|
/* Check if we need to do a fill */
|
|
if (Color == Color2)
|
|
{
|
|
/* Do a fill and continue the loop */
|
|
RleValue += x;
|
|
VidSolidColorFill(x, YDelta, RleValue - 1, YDelta, (UCHAR)Color);
|
|
x = RleValue;
|
|
continue;
|
|
}
|
|
|
|
/* Check if the pixel value is 1 or below */
|
|
if (RleValue > 1)
|
|
{
|
|
/* Set loop variables */
|
|
for (i = (RleValue - 2) / 2 + 1; i > 0; --i)
|
|
{
|
|
/* Set the pixels */
|
|
SetPixel(x, YDelta, (UCHAR)Color);
|
|
x++;
|
|
SetPixel(x, YDelta, (UCHAR)Color2);
|
|
x++;
|
|
|
|
/* Decrease pixel value */
|
|
RleValue -= 2;
|
|
}
|
|
}
|
|
|
|
/* Check if there is any value at all */
|
|
if (RleValue)
|
|
{
|
|
/* Set the pixel and increase position */
|
|
SetPixel(x, YDelta, (UCHAR)Color);
|
|
x++;
|
|
}
|
|
|
|
/* Start over */
|
|
continue;
|
|
}
|
|
|
|
/* Get the current pixel value */
|
|
RleValue = *Buffer;
|
|
Code = RleValue;
|
|
switch (Code)
|
|
{
|
|
/* Case 0 */
|
|
case 0:
|
|
{
|
|
/* Set new x value, decrease distance and restart */
|
|
x = Left;
|
|
YDelta--;
|
|
Buffer++;
|
|
continue;
|
|
}
|
|
|
|
/* Case 1 */
|
|
case 1:
|
|
{
|
|
/* Done */
|
|
return;
|
|
}
|
|
|
|
/* Case 2 */
|
|
case 2:
|
|
{
|
|
/* Set new x value, decrease distance and restart */
|
|
Buffer++;
|
|
x += *Buffer;
|
|
Buffer++;
|
|
YDelta -= *Buffer;
|
|
Buffer++;
|
|
continue;
|
|
}
|
|
|
|
/* Other values */
|
|
default:
|
|
{
|
|
Buffer++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Check if we've gone past the edge */
|
|
if ((x + RleValue) > (Width + Left))
|
|
{
|
|
/* Set fixed up loop count */
|
|
i = RleValue - Left - Width + x;
|
|
|
|
/* Fixup pixel value */
|
|
RleValue -= i;
|
|
}
|
|
else
|
|
{
|
|
/* Clear loop count */
|
|
i = 0;
|
|
}
|
|
|
|
/* Check the value now */
|
|
if (RleValue > 1)
|
|
{
|
|
/* Set loop variables */
|
|
for (j = (RleValue - 2) / 2 + 1; j > 0; --j)
|
|
{
|
|
/* Get the new value */
|
|
NewRleValue = *Buffer;
|
|
|
|
/* Get the two colors */
|
|
Color = NewRleValue >> 4;
|
|
Color2 = NewRleValue & 0xF;
|
|
|
|
/* Increase buffer position */
|
|
Buffer++;
|
|
|
|
/* Set the pixels */
|
|
SetPixel(x, YDelta, (UCHAR)Color);
|
|
x++;
|
|
SetPixel(x, YDelta, (UCHAR)Color2);
|
|
x++;
|
|
|
|
/* Decrease pixel value */
|
|
RleValue -= 2;
|
|
}
|
|
}
|
|
|
|
/* Check if there is any value at all */
|
|
if (RleValue)
|
|
{
|
|
/* Set the pixel and increase position */
|
|
Color = *Buffer >> 4;
|
|
Buffer++;
|
|
SetPixel(x, YDelta, (UCHAR)Color);
|
|
x++;
|
|
i--;
|
|
}
|
|
|
|
/* Check loop count now */
|
|
if ((LONG)i > 0)
|
|
{
|
|
/* Decrease it */
|
|
i--;
|
|
|
|
/* Set new position */
|
|
Buffer = Buffer + (i / 2) + 1;
|
|
}
|
|
|
|
/* Check if we need to increase the buffer */
|
|
if ((ULONG_PTR)Buffer & 1) Buffer++;
|
|
}
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
ULONG
|
|
NTAPI
|
|
VidSetTextColor(
|
|
_In_ ULONG Color)
|
|
{
|
|
ULONG OldColor;
|
|
|
|
/* Save the old color and set the new one */
|
|
OldColor = VidpTextColor;
|
|
VidpTextColor = Color;
|
|
return OldColor;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
VidDisplayStringXY(
|
|
_In_z_ PUCHAR String,
|
|
_In_ ULONG Left,
|
|
_In_ ULONG Top,
|
|
_In_ BOOLEAN Transparent)
|
|
{
|
|
ULONG BackColor;
|
|
|
|
/*
|
|
* If the caller wanted transparent, then send the special value (16),
|
|
* else use our default and call the helper routine.
|
|
*/
|
|
BackColor = Transparent ? BV_COLOR_NONE : BV_COLOR_LIGHT_CYAN;
|
|
|
|
/* Loop every character and adjust the position */
|
|
for (; *String; ++String, Left += BOOTCHAR_WIDTH)
|
|
{
|
|
/* Display a character */
|
|
DisplayCharacter(*String, Left, Top, BV_COLOR_LIGHT_BLUE, BackColor);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
VidSetScrollRegion(
|
|
_In_ ULONG Left,
|
|
_In_ ULONG Top,
|
|
_In_ ULONG Right,
|
|
_In_ ULONG Bottom)
|
|
{
|
|
/* Assert alignment */
|
|
ASSERT((Left % BOOTCHAR_WIDTH) == 0);
|
|
ASSERT((Right % BOOTCHAR_WIDTH) == BOOTCHAR_WIDTH - 1);
|
|
|
|
/* Set Scroll Region */
|
|
VidpScrollRegion[0] = Left;
|
|
VidpScrollRegion[1] = Top;
|
|
VidpScrollRegion[2] = Right;
|
|
VidpScrollRegion[3] = Bottom;
|
|
|
|
/* Set current X and Y */
|
|
VidpCurrentX = Left;
|
|
VidpCurrentY = Top;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
VidDisplayString(
|
|
_In_z_ PUCHAR String)
|
|
{
|
|
/* Start looping the string */
|
|
for (; *String; ++String)
|
|
{
|
|
/* Treat new-line separately */
|
|
if (*String == '\n')
|
|
{
|
|
/* Modify Y position */
|
|
VidpCurrentY += BOOTCHAR_HEIGHT + 1;
|
|
if (VidpCurrentY + BOOTCHAR_HEIGHT > VidpScrollRegion[3])
|
|
{
|
|
/* Scroll the view and clear the current row */
|
|
DoScroll(BOOTCHAR_HEIGHT + 1);
|
|
VidpCurrentY -= BOOTCHAR_HEIGHT + 1;
|
|
PreserveRow(VidpCurrentY, BOOTCHAR_HEIGHT + 1, TRUE);
|
|
}
|
|
else
|
|
{
|
|
/* Preserve the current row */
|
|
PreserveRow(VidpCurrentY, BOOTCHAR_HEIGHT + 1, FALSE);
|
|
}
|
|
|
|
/* Update current X */
|
|
VidpCurrentX = VidpScrollRegion[0];
|
|
|
|
/* No need to clear this row */
|
|
ClearRow = FALSE;
|
|
}
|
|
else if (*String == '\r')
|
|
{
|
|
/* Update current X */
|
|
VidpCurrentX = VidpScrollRegion[0];
|
|
|
|
/* If a new-line does not follow we will clear the current row */
|
|
if (String[1] != '\n') ClearRow = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Clear the current row if we had a return-carriage without a new-line */
|
|
if (ClearRow)
|
|
{
|
|
PreserveRow(VidpCurrentY, BOOTCHAR_HEIGHT + 1, TRUE);
|
|
ClearRow = FALSE;
|
|
}
|
|
|
|
/* Display this character */
|
|
DisplayCharacter(*String, VidpCurrentX, VidpCurrentY, VidpTextColor, BV_COLOR_NONE);
|
|
VidpCurrentX += BOOTCHAR_WIDTH;
|
|
|
|
/* Check if we should scroll */
|
|
if (VidpCurrentX + BOOTCHAR_WIDTH - 1 > VidpScrollRegion[2])
|
|
{
|
|
/* Update Y position and check if we should scroll it */
|
|
VidpCurrentY += BOOTCHAR_HEIGHT + 1;
|
|
if (VidpCurrentY + BOOTCHAR_HEIGHT > VidpScrollRegion[3])
|
|
{
|
|
/* Scroll the view and clear the current row */
|
|
DoScroll(BOOTCHAR_HEIGHT + 1);
|
|
VidpCurrentY -= BOOTCHAR_HEIGHT + 1;
|
|
PreserveRow(VidpCurrentY, BOOTCHAR_HEIGHT + 1, TRUE);
|
|
}
|
|
else
|
|
{
|
|
/* Preserve the current row */
|
|
PreserveRow(VidpCurrentY, BOOTCHAR_HEIGHT + 1, FALSE);
|
|
}
|
|
|
|
/* Update current X */
|
|
VidpCurrentX = VidpScrollRegion[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
VidBufferToScreenBlt(
|
|
_In_reads_bytes_(Delta * Height) PUCHAR Buffer,
|
|
_In_ ULONG Left,
|
|
_In_ ULONG Top,
|
|
_In_ ULONG Width,
|
|
_In_ ULONG Height,
|
|
_In_ ULONG Delta)
|
|
{
|
|
/* Make sure we have a width and height */
|
|
if (!Width || !Height)
|
|
return;
|
|
|
|
/* Call the helper function */
|
|
BitBlt(Left, Top, Width, Height, Buffer, 4, Delta);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
VidBitBlt(
|
|
_In_ PUCHAR Buffer,
|
|
_In_ ULONG Left,
|
|
_In_ ULONG Top)
|
|
{
|
|
PBITMAPINFOHEADER BitmapInfoHeader;
|
|
LONG Delta;
|
|
PUCHAR BitmapOffset;
|
|
ULONG PaletteCount;
|
|
|
|
/* Get the Bitmap Header */
|
|
BitmapInfoHeader = (PBITMAPINFOHEADER)Buffer;
|
|
|
|
/* Initialize the palette */
|
|
PaletteCount = BitmapInfoHeader->biClrUsed ?
|
|
BitmapInfoHeader->biClrUsed : BV_MAX_COLORS;
|
|
InitPaletteWithTable((PULONG)(Buffer + BitmapInfoHeader->biSize),
|
|
PaletteCount);
|
|
|
|
/* Make sure we can support this bitmap */
|
|
ASSERT((BitmapInfoHeader->biBitCount * BitmapInfoHeader->biPlanes) <= 4);
|
|
|
|
/*
|
|
* Calculate the delta and align it on 32-bytes, then calculate
|
|
* the actual start of the bitmap data.
|
|
*/
|
|
Delta = (BitmapInfoHeader->biBitCount * BitmapInfoHeader->biWidth) + 31;
|
|
Delta >>= 3;
|
|
Delta &= ~3;
|
|
BitmapOffset = Buffer + sizeof(BITMAPINFOHEADER) + PaletteCount * sizeof(ULONG);
|
|
|
|
/* Check the compression of the bitmap */
|
|
if (BitmapInfoHeader->biCompression == BI_RLE4)
|
|
{
|
|
/* Make sure we have a width and a height */
|
|
if ((BitmapInfoHeader->biWidth) && (BitmapInfoHeader->biHeight))
|
|
{
|
|
/* We can use RLE Bit Blt */
|
|
RleBitBlt(Left,
|
|
Top,
|
|
BitmapInfoHeader->biWidth,
|
|
BitmapInfoHeader->biHeight,
|
|
BitmapOffset);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Check if the height is negative */
|
|
if (BitmapInfoHeader->biHeight < 0)
|
|
{
|
|
/* Make it positive in the header */
|
|
BitmapInfoHeader->biHeight *= -1;
|
|
}
|
|
else
|
|
{
|
|
/* Update buffer offset */
|
|
BitmapOffset += ((BitmapInfoHeader->biHeight - 1) * Delta);
|
|
Delta *= -1;
|
|
}
|
|
|
|
/* Make sure we have a width and a height */
|
|
if ((BitmapInfoHeader->biWidth) && (BitmapInfoHeader->biHeight))
|
|
{
|
|
/* Do the BitBlt */
|
|
BitBlt(Left,
|
|
Top,
|
|
BitmapInfoHeader->biWidth,
|
|
BitmapInfoHeader->biHeight,
|
|
BitmapOffset,
|
|
BitmapInfoHeader->biBitCount,
|
|
Delta);
|
|
}
|
|
}
|
|
}
|