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