/** @file OcBlitLib - Library to perform blt operations on a frame buffer. Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
Copyright (c) 2021, vit9696. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include "BlitInternal.h" STATIC CONST EFI_PIXEL_BITMASK mRgbPixelMasks = { 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; STATIC CONST EFI_PIXEL_BITMASK mBgrPixelMasks = { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }; /** Initialize the bit mask in frame buffer configure. @param BitMask The bit mask of pixel. @param BytesPerPixel Size in bytes of pixel. @param PixelShl Left shift array. @param PixelShr Right shift array. **/ STATIC VOID BlitLibConfigurePixelFormat ( IN CONST EFI_PIXEL_BITMASK *BitMask, OUT UINT32 *BytesPerPixel, OUT INT8 *PixelShl, OUT INT8 *PixelShr ) { UINT8 Index; UINT32 *Masks; UINT32 MergedMasks; ASSERT (BytesPerPixel != NULL); MergedMasks = 0; Masks = (UINT32 *)BitMask; for (Index = 0; Index < 3; Index++) { ASSERT ((MergedMasks & Masks[Index]) == 0); PixelShl[Index] = (INT8)HighBitSet32 (Masks[Index]) - 23 + (Index * 8); if (PixelShl[Index] < 0) { PixelShr[Index] = -PixelShl[Index]; PixelShl[Index] = 0; } else { PixelShr[Index] = 0; } DEBUG (( DEBUG_INFO, "OCBLT: %d: shl:%d shr:%d mask:%x\n", Index, PixelShl[Index], PixelShr[Index], Masks[Index] )); MergedMasks = (UINT32)(MergedMasks | Masks[Index]); } MergedMasks = (UINT32)(MergedMasks | Masks[3]); ASSERT (MergedMasks != 0); *BytesPerPixel = (UINT32)((HighBitSet32 (MergedMasks) + 7) / 8); DEBUG ((DEBUG_INFO, "OCBLT: Bytes per pixel: %d\n", *BytesPerPixel)); } /** Performs a UEFI Graphics Output Protocol Blt Video Fill. @param[in] Configure Pointer to a configuration which was successfully created by FrameBufferBltConfigure (). @param[in] Color Color to fill the region with. @param[in] DestinationX X location to start fill operation. @param[in] DestinationY Y location to start fill operation. @param[in] Width Width (in pixels) to fill. @param[in] Height Height to fill. @retval RETURN_INVALID_PARAMETER Invalid parameter was passed in. @retval RETURN_SUCCESS The video was filled successfully. **/ STATIC EFI_STATUS BlitLibVideoFill ( IN OC_BLIT_CONFIGURE *Configure, IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Color, IN UINTN DestinationX, IN UINTN DestinationY, IN UINTN Width, IN UINTN Height ) { UINTN IndexX; UINTN IndexY; UINTN Tmp; UINT8 *Destination; UINT32 Uint32; UINT64 WideFill; BOOLEAN LineBufferReady; UINTN Offset; UINTN WidthInBytes; UINTN SizeInBytes; // // BltBuffer to Video: Source is BltBuffer, destination is Video // if (DestinationY + Height > Configure->RotatedHeight) { DEBUG ((DEBUG_VERBOSE, "OCBLT: Past screen (Y)\n")); return RETURN_INVALID_PARAMETER; } if (DestinationX + Width > Configure->RotatedWidth) { DEBUG ((DEBUG_VERBOSE, "OCBLT: Past screen (X)\n")); return RETURN_INVALID_PARAMETER; } if ((Width == 0) || (Height == 0)) { DEBUG ((DEBUG_VERBOSE, "OCBLT: Width or Height is 0\n")); return RETURN_INVALID_PARAMETER; } if (Configure->Rotation == 90) { // // Perform -90 rotation. // Tmp = Width; Width = Height; Height = Tmp; Tmp = DestinationX; DestinationX = Configure->Width - DestinationY - Width; DestinationY = Tmp; } else if (Configure->Rotation == 180) { // // Perform -180 rotation. // DestinationX = Configure->Width - DestinationX - Width; DestinationY = Configure->Height - DestinationY - Height; } else if (Configure->Rotation == 270) { // // Perform +90 rotation. // Tmp = Width; Width = Height; Height = Tmp; Tmp = DestinationX; DestinationX = DestinationY; DestinationY = Configure->Height - Tmp - Height; } WidthInBytes = Width * BYTES_PER_PIXEL; Uint32 = *(UINT32 *)Color; WideFill = (UINT32)( (((Uint32 << Configure->PixelShl[0]) >> Configure->PixelShr[0]) & Configure->PixelMasks.RedMask) | (((Uint32 << Configure->PixelShl[1]) >> Configure->PixelShr[1]) & Configure->PixelMasks.GreenMask) | (((Uint32 << Configure->PixelShl[2]) >> Configure->PixelShr[2]) & Configure->PixelMasks.BlueMask) ); DEBUG ((DEBUG_VERBOSE, "OCBLT: color=0x%x, wide-fill=0x%x\n", Uint32, WideFill)); // // If the size of the pixel data evenly divides the sizeof // WideFill, then a wide fill operation can be used // for (IndexX = BYTES_PER_PIXEL; IndexX < sizeof (WideFill); IndexX++) { ((UINT8 *)&WideFill)[IndexX] = ((UINT8 *)&WideFill)[IndexX % BYTES_PER_PIXEL]; } if ((DestinationX == 0) && (Width == Configure->PixelsPerScanLine)) { DEBUG ((DEBUG_VERBOSE, "OCBLT: VideoFill (wide, one-shot)\n")); Offset = DestinationY * Configure->PixelsPerScanLine; Offset = BYTES_PER_PIXEL * Offset; Destination = Configure->FrameBuffer + Offset; SizeInBytes = WidthInBytes * Height; if (SizeInBytes >= 8) { SetMem32 (Destination, SizeInBytes & ~3, (UINT32)WideFill); Destination += SizeInBytes & ~3; SizeInBytes &= 3; } if (SizeInBytes > 0) { SetMem (Destination, SizeInBytes, (UINT8)(UINTN)WideFill); } } else { LineBufferReady = FALSE; for (IndexY = DestinationY; IndexY < (Height + DestinationY); IndexY++) { Offset = (IndexY * Configure->PixelsPerScanLine) + DestinationX; Offset = BYTES_PER_PIXEL * Offset; Destination = Configure->FrameBuffer + Offset; if (((UINTN)Destination & 7) == 0) { DEBUG ((DEBUG_VERBOSE, "OCBLT: VideoFill (wide)\n")); SizeInBytes = WidthInBytes; if (SizeInBytes >= 8) { SetMem64 (Destination, SizeInBytes & ~7, WideFill); Destination += SizeInBytes & ~7; SizeInBytes &= 7; } if (SizeInBytes > 0) { CopyMem (Destination, &WideFill, SizeInBytes); } } else { DEBUG ((DEBUG_VERBOSE, "OCBLT: VideoFill (not wide)\n")); if (!LineBufferReady) { CopyMem (Configure->LineBuffer, &WideFill, BYTES_PER_PIXEL); for (IndexX = 1; IndexX < Width; ) { CopyMem ( (Configure->LineBuffer + (IndexX * BYTES_PER_PIXEL)), Configure->LineBuffer, MIN (IndexX, Width - IndexX) * BYTES_PER_PIXEL ); IndexX += MIN (IndexX, Width - IndexX); } LineBufferReady = TRUE; } CopyMem (Destination, Configure->LineBuffer, WidthInBytes); } } } return RETURN_SUCCESS; } /** Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation with extended parameters. @param[in] Configure Pointer to a configuration which was successfully created by FrameBufferBltConfigure (). @param[out] BltBuffer Output buffer for pixel color data. @param[in] SourceX X location within video. @param[in] SourceY Y location within video. @param[in] DestinationX X location within BltBuffer. @param[in] DestinationY Y location within BltBuffer. @param[in] Width Width (in pixels). @param[in] Height Height. @param[in] Delta Number of bytes in a row of BltBuffer. @retval RETURN_INVALID_PARAMETER Invalid parameter were passed in. @retval RETURN_SUCCESS The Blt operation was performed successfully. **/ STATIC RETURN_STATUS BlitLibVideoToBuffer ( IN OC_BLIT_CONFIGURE *Configure, OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, IN UINTN SourceX, IN UINTN SourceY, IN UINTN DestinationX, IN UINTN DestinationY, IN UINTN Width, IN UINTN Height, IN UINTN Delta ) { // // Video to BltBuffer: Source is Video, destination is BltBuffer // if (SourceY + Height > Configure->RotatedHeight) { return RETURN_INVALID_PARAMETER; } if (SourceX + Width > Configure->RotatedWidth) { return RETURN_INVALID_PARAMETER; } if ((Width == 0) || (Height == 0)) { return RETURN_INVALID_PARAMETER; } // // If Delta is zero, then the entire BltBuffer is being used, so Delta is // the number of bytes in each row of BltBuffer. Since BltBuffer is Width // pixels size, the number of bytes in each row can be computed. // if (Delta == 0) { Delta = Width; } else { Delta /= BYTES_PER_PIXEL; } switch (Configure->Rotation) { case 0: return BlitLibVideoToBuffer0 ( Configure, BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta ); case 90: return BlitLibVideoToBuffer90 ( Configure, BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta ); case 180: return BlitLibVideoToBuffer180 ( Configure, BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta ); case 270: return BlitLibVideoToBuffer270 ( Configure, BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta ); default: ASSERT (FALSE); break; } return RETURN_SUCCESS; } /** Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation with extended parameters. @param[in] Configure Pointer to a configuration which was successfully created by FrameBufferBltConfigure (). @param[in] BltBuffer Output buffer for pixel color data. @param[in] SourceX X location within BltBuffer. @param[in] SourceY Y location within BltBuffer. @param[in] DestinationX X location within video. @param[in] DestinationY Y location within video. @param[in] Width Width (in pixels). @param[in] Height Height. @param[in] Delta Number of bytes in a row of BltBuffer. @retval RETURN_INVALID_PARAMETER Invalid parameter were passed in. @retval RETURN_SUCCESS The Blt operation was performed successfully. **/ STATIC RETURN_STATUS BlitLibBufferToVideo ( IN OC_BLIT_CONFIGURE *Configure, IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, IN UINTN SourceX, IN UINTN SourceY, IN UINTN DestinationX, IN UINTN DestinationY, IN UINTN Width, IN UINTN Height, IN UINTN Delta ) { // // BltBuffer to Video: Source is BltBuffer, destination is Video // if (DestinationY + Height > Configure->RotatedHeight) { return RETURN_INVALID_PARAMETER; } if (DestinationX + Width > Configure->RotatedWidth) { return RETURN_INVALID_PARAMETER; } if ((Width == 0) || (Height == 0)) { return RETURN_INVALID_PARAMETER; } // // If Delta is zero, then the entire BltBuffer is being used, so Delta is // the number of bytes in each row of BltBuffer. Since BltBuffer is Width // pixels size, the number of bytes in each row can be computed. // if (Delta == 0) { Delta = Width; } else { Delta /= BYTES_PER_PIXEL; } switch (Configure->Rotation) { case 0: return BlitLibBufferToVideo0 ( Configure, BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta ); case 90: return BlitLibBufferToVideo90 ( Configure, BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta ); case 180: return BlitLibBufferToVideo180 ( Configure, BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta ); case 270: return BlitLibBufferToVideo270 ( Configure, BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta ); default: ASSERT (FALSE); break; } return RETURN_SUCCESS; } /** Performs a UEFI Graphics Output Protocol Blt Video to Video operation @param[in] Configure Pointer to a configuration which was successfully created by FrameBufferBltConfigure (). @param[in] SourceX X location within video. @param[in] SourceY Y location within video. @param[in] DestinationX X location within video. @param[in] DestinationY Y location within video. @param[in] Width Width (in pixels). @param[in] Height Height. @retval RETURN_INVALID_PARAMETER Invalid parameter were passed in. @retval RETURN_SUCCESS The Blt operation was performed successfully. **/ STATIC RETURN_STATUS BlitLibVideoToVideo ( IN OC_BLIT_CONFIGURE *Configure, IN UINTN SourceX, IN UINTN SourceY, IN UINTN DestinationX, IN UINTN DestinationY, IN UINTN Width, IN UINTN Height ) { UINT8 *Source; UINT8 *Destination; UINTN Offset; UINTN WidthInBytes; UINTN Tmp; INTN LineStride; // // Video to Video: Source is Video, destination is Video // if (SourceY + Height > Configure->RotatedHeight) { return RETURN_INVALID_PARAMETER; } if (SourceX + Width > Configure->RotatedWidth) { return RETURN_INVALID_PARAMETER; } if (DestinationY + Height > Configure->RotatedHeight) { return RETURN_INVALID_PARAMETER; } if (DestinationX + Width > Configure->RotatedWidth) { return RETURN_INVALID_PARAMETER; } if ((Width == 0) || (Height == 0)) { return RETURN_INVALID_PARAMETER; } if (Configure->Rotation == 90) { // // Perform -90 rotation. // Tmp = Width; Width = Height; Height = Tmp; Tmp = DestinationX; DestinationX = Configure->Width - DestinationY - Width; DestinationY = Tmp; Tmp = SourceX; SourceX = Configure->Width - SourceY - Width; SourceY = Tmp; } else if (Configure->Rotation == 180) { // // Perform -180 rotation. // DestinationX = Configure->Width - DestinationX - Width; DestinationY = Configure->Height - DestinationY - Height; SourceX = Configure->Width - SourceX - Width; SourceY = Configure->Height - SourceY - Height; } else if (Configure->Rotation == 270) { // // Perform +90 rotation. // Tmp = Width; Width = Height; Height = Tmp; Tmp = DestinationX; DestinationX = DestinationY; DestinationY = Configure->Height - Tmp - Height; Tmp = SourceX; SourceX = SourceY; SourceY = Configure->Height - Tmp - Height; } WidthInBytes = Width * BYTES_PER_PIXEL; Offset = (SourceY * Configure->PixelsPerScanLine) + SourceX; Offset = BYTES_PER_PIXEL * Offset; Source = Configure->FrameBuffer + Offset; Offset = (DestinationY * Configure->PixelsPerScanLine) + DestinationX; Offset = BYTES_PER_PIXEL * Offset; Destination = Configure->FrameBuffer + Offset; LineStride = BYTES_PER_PIXEL * Configure->PixelsPerScanLine; if (Destination > Source) { // // Copy from last line to avoid source is corrupted by copying // Source += Height * LineStride; Destination += Height * LineStride; LineStride = -LineStride; } while (Height-- > 0) { CopyMem (Destination, Source, WidthInBytes); Source += LineStride; Destination += LineStride; } return RETURN_SUCCESS; } RETURN_STATUS EFIAPI OcBlitConfigure ( IN VOID *FrameBuffer, IN EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *FrameBufferInfo, IN UINT32 Rotation, IN OUT OC_BLIT_CONFIGURE *Configure, IN OUT UINTN *ConfigureSize ) { CONST EFI_PIXEL_BITMASK *BitMask; UINT32 BytesPerPixel; INT8 PixelShl[4]; INT8 PixelShr[4]; if (ConfigureSize == NULL) { return RETURN_INVALID_PARAMETER; } switch (FrameBufferInfo->PixelFormat) { case PixelRedGreenBlueReserved8BitPerColor: BitMask = &mRgbPixelMasks; break; case PixelBlueGreenRedReserved8BitPerColor: BitMask = &mBgrPixelMasks; break; case PixelBitMask: BitMask = &FrameBufferInfo->PixelInformation; break; case PixelBltOnly: ASSERT (FrameBufferInfo->PixelFormat != PixelBltOnly); return RETURN_UNSUPPORTED; default: ASSERT (FALSE); return RETURN_INVALID_PARAMETER; } if (FrameBufferInfo->PixelsPerScanLine < FrameBufferInfo->HorizontalResolution) { return RETURN_UNSUPPORTED; } BlitLibConfigurePixelFormat (BitMask, &BytesPerPixel, PixelShl, PixelShr); if (BytesPerPixel != sizeof (UINT32)) { return RETURN_UNSUPPORTED; } if (*ConfigureSize < sizeof (OC_BLIT_CONFIGURE) + FrameBufferInfo->HorizontalResolution * sizeof (UINT32)) { *ConfigureSize = sizeof (OC_BLIT_CONFIGURE) + FrameBufferInfo->HorizontalResolution * sizeof (UINT32); return RETURN_BUFFER_TOO_SMALL; } if (Configure == NULL) { return RETURN_INVALID_PARAMETER; } CopyMem (&Configure->PixelMasks, BitMask, sizeof (*BitMask)); CopyMem (Configure->PixelShl, PixelShl, sizeof (PixelShl)); CopyMem (Configure->PixelShr, PixelShr, sizeof (PixelShr)); Configure->PixelFormat = FrameBufferInfo->PixelFormat; Configure->FrameBuffer = (UINT8 *)FrameBuffer; Configure->Width = FrameBufferInfo->HorizontalResolution; Configure->Height = FrameBufferInfo->VerticalResolution; Configure->PixelsPerScanLine = FrameBufferInfo->PixelsPerScanLine; Configure->Rotation = Rotation; if ((Rotation == 90) || (Rotation == 270)) { Configure->RotatedWidth = FrameBufferInfo->VerticalResolution; Configure->RotatedHeight = FrameBufferInfo->HorizontalResolution; } else { Configure->RotatedWidth = FrameBufferInfo->HorizontalResolution; Configure->RotatedHeight = FrameBufferInfo->VerticalResolution; } return RETURN_SUCCESS; } RETURN_STATUS EFIAPI OcBlitRender ( IN OC_BLIT_CONFIGURE *Configure, IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer OPTIONAL, IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, IN UINTN SourceX, IN UINTN SourceY, IN UINTN DestinationX, IN UINTN DestinationY, IN UINTN Width, IN UINTN Height, IN UINTN Delta ) { ASSERT (Configure != NULL); switch (BltOperation) { case EfiBltVideoToBltBuffer: return BlitLibVideoToBuffer ( Configure, BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta ); case EfiBltVideoToVideo: return BlitLibVideoToVideo ( Configure, SourceX, SourceY, DestinationX, DestinationY, Width, Height ); case EfiBltVideoFill: return BlitLibVideoFill ( Configure, BltBuffer, DestinationX, DestinationY, Width, Height ); case EfiBltBufferToVideo: return BlitLibBufferToVideo ( Configure, BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta ); default: return RETURN_INVALID_PARAMETER; } }