diff --git a/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c b/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c index 928c6068444..6c325fa52e6 100644 --- a/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c +++ b/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c @@ -134,7 +134,8 @@ XboxVideoInit(VOID) ScreenWidth, ScreenHeight, ScreenWidth, // PixelsPerScanLine - BytesPerPixel * 8); + BytesPerPixel * 8, + NULL); VidFbClearScreenColor(MAKE_COLOR(0, 0, 0), TRUE); } diff --git a/boot/freeldr/freeldr/arch/twidbits.h b/boot/freeldr/freeldr/arch/twidbits.h new file mode 100644 index 00000000000..bbc8f0faa95 --- /dev/null +++ b/boot/freeldr/freeldr/arch/twidbits.h @@ -0,0 +1,39 @@ +/* + * PROJECT: FreeLoader + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * or MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Bit twiddling helpers + * COPYRIGHT: Copyright 2025 Hermès Bélusca-Maïto + * + * Based on http://www.graphics.stanford.edu/~seander/bithacks.html + * and other sources. + */ + +// HAVE___BUILTIN_POPCOUNT + +#pragma once + +/** + * @brief Return the number of bits set in a 32-bit integer. + * @note Equivalent to __popcnt(). + **/ +FORCEINLINE +ULONG +CountNumberOfBits( + _In_ UINT32 n) +{ +#ifdef HAVE___BUILTIN_POPCOUNT + return __popcnt(n); +#else + n -= ((n >> 1) & 0x55555555); + n = (((n >> 2) & 0x33333333) + (n & 0x33333333)); +#if 0 + n = (((n >> 4) + n) & 0x0f0f0f0f); + n += (n >> 8); + n += (n >> 16); + return (n & 0x3f); +#else + return (((n >> 4) + n) & 0x0f0f0f0f) * 0x01010101 >> 24; +#endif +#endif /* HAVE___BUILTIN_POPCOUNT */ +} diff --git a/boot/freeldr/freeldr/arch/uefi/uefivid.c b/boot/freeldr/freeldr/arch/uefi/uefivid.c index 95304babe95..e00c9e875ef 100644 --- a/boot/freeldr/freeldr/arch/uefi/uefivid.c +++ b/boot/freeldr/freeldr/arch/uefi/uefivid.c @@ -21,10 +21,38 @@ EFI_GUID EfiGraphicsOutputProtocol = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; /* FUNCTIONS ******************************************************************/ +/* EFI 1.x */ +#ifdef EFI_UGA_DRAW_PROTOCOL_GUID + +/* NOTE: EFI UGA does not support any other format than 32-bit xRGB, and + * no direct access to the underlying hardware framebuffer is offered */ +C_ASSERT(sizeof(EFI_UGA_PIXEL) == sizeof(ULONG)); + +#endif /* EFI */ + +/* UEFI support, see efi/GraphicsOutput.h */ +#ifdef EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID // __GRAPHICS_OUTPUT_H__ + +C_ASSERT(sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) == sizeof(ULONG)); + +/** + * @brief Maps UEFI GOP pixel format to pixel masks. + * @see EFI_PIXEL_BITMASK + **/ +static EFI_PIXEL_BITMASK EfiPixelMasks[] = +{ /* Red, Green, Blue, Reserved */ + {0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000}, // PixelRedGreenBlueReserved8BitPerColor + {0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000}, // PixelBlueGreenRedReserved8BitPerColor + {0, 0, 0, 0} // PixelBitMask, PixelBltOnly, ... +}; + +#endif /* UEFI */ + EFI_STATUS UefiInitializeVideo(VOID) { EFI_GRAPHICS_PIXEL_FORMAT PixelFormat; + EFI_PIXEL_BITMASK* pPixelBitmask; ULONG BitsPerPixel; EFI_STATUS Status; @@ -47,15 +75,33 @@ UefiInitializeVideo(VOID) case PixelRedGreenBlueReserved8BitPerColor: case PixelBlueGreenRedReserved8BitPerColor: { + pPixelBitmask = &EfiPixelMasks[PixelFormat]; BitsPerPixel = RTL_BITS_OF(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); break; } case PixelBitMask: + { + /* + * When the GOP pixel format is given by PixelBitMask, the pixel + * element size _may be_ different from 4 bytes. + * See UEFI Spec Rev.2.10 Section 12.9 "Graphics Output Protocol": + * example code "GetPixelElementSize()" function. + */ + pPixelBitmask = &gop->Mode->Info->PixelInformation; + BitsPerPixel = + PixelBitmasksToBpp(pPixelBitmask->RedMask, + pPixelBitmask->GreenMask, + pPixelBitmask->BlueMask, + pPixelBitmask->ReservedMask); + break; + } + case PixelBltOnly: default: { ERR("Unsupported UFEI GOP format %lu\n", PixelFormat); + pPixelBitmask = NULL; BitsPerPixel = 0; break; } @@ -66,7 +112,8 @@ UefiInitializeVideo(VOID) gop->Mode->Info->HorizontalResolution, gop->Mode->Info->VerticalResolution, gop->Mode->Info->PixelsPerScanLine, - BitsPerPixel); + BitsPerPixel, + (PPIXEL_BITMASK)pPixelBitmask); return Status; } diff --git a/boot/freeldr/freeldr/arch/vidfb.c b/boot/freeldr/freeldr/arch/vidfb.c index 72d50e15d32..5d0b01442ca 100644 --- a/boot/freeldr/freeldr/arch/vidfb.c +++ b/boot/freeldr/freeldr/arch/vidfb.c @@ -31,6 +31,9 @@ typedef struct _FRAMEBUFFER_INFO ULONG PixelsPerScanLine; // aka. "Pitch" or "ScreenStride", but Stride is in bytes or bits... ULONG BitsPerPixel; // aka. "PixelStride". + /* Physical format of the pixel for BPP > 8, specified by bit-mask */ + PIXEL_BITMASK PixelMasks; + /** Calculated values */ ULONG BytesPerPixel; @@ -55,6 +58,11 @@ VidFbPrintFramebufferInfo(VOID) TRACE(" BitsPerPixel : %lu\n", framebufInfo.BitsPerPixel); TRACE(" BytesPerPixel : %lu\n", framebufInfo.BytesPerPixel); TRACE(" Delta : %lu\n", framebufInfo.Delta); + TRACE(" ARGB masks: : %08x/%08x/%08x/%08x\n", + framebufInfo.PixelMasks.ReservedMask, + framebufInfo.PixelMasks.RedMask, + framebufInfo.PixelMasks.GreenMask, + framebufInfo.PixelMasks.BlueMask); } #endif @@ -78,6 +86,10 @@ VidFbPrintFramebufferInfo(VOID) * @param[in] BitsPerPixel * The number of usable bits (not counting the reserved ones) per pixel. * + * @param[in] PixelMasks + * Optional pointer to a PIXEL_BITMASK structure describing the pixel + * format used by the framebuffer. + * * @return * TRUE if initialization is successful; FALSE if not. **/ @@ -88,8 +100,11 @@ VidFbInitializeVideo( _In_ UINT32 ScreenWidth, _In_ UINT32 ScreenHeight, _In_ UINT32 PixelsPerScanLine, - _In_ UINT32 BitsPerPixel) + _In_ UINT32 BitsPerPixel, + _In_opt_ PPIXEL_BITMASK PixelMasks) { + PPIXEL_BITMASK BitMasks = &framebufInfo.PixelMasks; + RtlZeroMemory(&framebufInfo, sizeof(framebufInfo)); framebufInfo.BaseAddress = BaseAddress; @@ -110,8 +125,66 @@ VidFbInitializeVideo( return FALSE; } + //ASSERT((BitsPerPixel <= 8 && !PixelMasks) || (BitsPerPixel > 8)); + if (BitsPerPixel > 8) + { + if (!PixelMasks || + (PixelMasks->RedMask == 0 && + PixelMasks->GreenMask == 0 && + PixelMasks->BlueMask == 0 /* && + PixelMasks->ReservedMask == 0 */)) + { + /* Determine pixel mask given color depth and color channel */ + switch (BitsPerPixel) + { + case 32: + case 24: /* 8:8:8 */ + BitMasks->RedMask = 0x00FF0000; // 0x00FF0000; + BitMasks->GreenMask = 0x0000FF00; // 0x00FF0000 >> 8; + BitMasks->BlueMask = 0x000000FF; // 0x00FF0000 >> 16; + BitMasks->ReservedMask = ((1 << (BitsPerPixel - 24)) - 1) << 24; + break; + case 16: /* 5:6:5 */ + BitMasks->RedMask = 0xF800; // 0xF800; + BitMasks->GreenMask = 0x07E0; // (0xF800 >> 5) | 0x20; + BitMasks->BlueMask = 0x001F; // 0xF800 >> 11; + BitMasks->ReservedMask = 0; + break; + case 15: /* 5:5:5 */ + BitMasks->RedMask = 0x7C00; // 0x7C00; + BitMasks->GreenMask = 0x03E0; // 0x7C00 >> 5; + BitMasks->BlueMask = 0x001F; // 0x7C00 >> 10; + BitMasks->ReservedMask = 0x8000; + break; + default: + /* Unsupported BPP */ + UNIMPLEMENTED; + RtlZeroMemory(BitMasks, sizeof(*BitMasks)); + } + } + else + { + /* Copy the pixel masks */ + RtlCopyMemory(BitMasks, PixelMasks, sizeof(*BitMasks)); + } + } + else + { + /* Palettized modes don't use masks */ + RtlZeroMemory(BitMasks, sizeof(*BitMasks)); + } + #if DBG VidFbPrintFramebufferInfo(); + { + ULONG BppFromMasks = + PixelBitmasksToBpp(BitMasks->RedMask, + BitMasks->GreenMask, + BitMasks->BlueMask, + BitMasks->ReservedMask); + TRACE("BitsPerPixel = %lu , BppFromMasks = %lu\n", BitsPerPixel, BppFromMasks); + //ASSERT(BitsPerPixel == BppFromMasks); + } #endif return TRUE; diff --git a/boot/freeldr/freeldr/arch/vidfb.h b/boot/freeldr/freeldr/arch/vidfb.h index b409692da7d..1540713b7a8 100644 --- a/boot/freeldr/freeldr/arch/vidfb.h +++ b/boot/freeldr/freeldr/arch/vidfb.h @@ -7,6 +7,49 @@ #pragma once +#include "twidbits.h" + +/** + * @brief + * Physical format of an RGB pixel, specified with per-component bit-masks. + * A bit being set defines those used for the given color component, such + * as Red, Green, Blue, or Reserved. + * + * @note + * Supports up to 32 bits-per-pixel deep pixels. + **/ +typedef struct _PIXEL_BITMASK +{ + ULONG RedMask; + ULONG GreenMask; + ULONG BlueMask; + ULONG ReservedMask; +} PIXEL_BITMASK, *PPIXEL_BITMASK; + + +/** + * @brief + * Calculates the number of bits per pixel ("PixelDepth") for + * the given pixel format, given by the pixel color masks. + * + * @remark + * See UEFI Spec Rev.2.10 Section 12.9 "Graphics Output Protocol": + * example code "GetPixelElementSize()" function. + **/ +FORCEINLINE +ULONG +PixelBitmasksToBpp( + _In_ ULONG RedMask, + _In_ ULONG GreenMask, + _In_ ULONG BlueMask, + _In_ ULONG ReservedMask) +{ + ULONG CompoundMask = (RedMask | GreenMask | BlueMask | ReservedMask); + /* Alternatively, the calculation could be done by finding the highest + * bit set in the combined pixel color masks, if they are packed together. */ + return CountNumberOfBits(CompoundMask); // FindHighestSetBit(CompoundMask); +} + BOOLEAN VidFbInitializeVideo( _In_ ULONG_PTR BaseAddress, @@ -14,7 +57,8 @@ VidFbInitializeVideo( _In_ UINT32 ScreenWidth, _In_ UINT32 ScreenHeight, _In_ UINT32 PixelsPerScanLine, - _In_ UINT32 BitsPerPixel); + _In_ UINT32 BitsPerPixel, + _In_opt_ PPIXEL_BITMASK PixelMasks); VOID VidFbClearScreenColor(