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