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