From b87fc6b140772ba3017de311c7063c259424264c Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 15 Aug 2016 17:11:31 +0200 Subject: First public release. Used by VeraCrypt 1.18. --- Library/GraphLib/EfiGraph.c | 670 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 670 insertions(+) create mode 100644 Library/GraphLib/EfiGraph.c (limited to 'Library/GraphLib/EfiGraph.c') diff --git a/Library/GraphLib/EfiGraph.c b/Library/GraphLib/EfiGraph.c new file mode 100644 index 0000000..b8e8e2e --- /dev/null +++ b/Library/GraphLib/EfiGraph.c @@ -0,0 +1,670 @@ +/** @file +Graph library + +Copyright (c) 2016. Disk Cryptography Services for EFI (DCS), Alex Kolotnikov +Copyright (c) 2016. VeraCrypt, Mounir IDRASSI + +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the GNU Lesser General Public License, version 3.0 (LGPL-3.0). + +The full text of the license may be found at +https://opensource.org/licenses/LGPL-3.0 +**/ + +#include +#include +#include +#include +#include +#include +#include + +EFI_HANDLE* gGraphHandles = NULL; +UINTN gGraphCount = 0; + +EFI_GRAPHICS_OUTPUT_PROTOCOL* gGraphOut = NULL; + +EFI_STATUS +InitGraph() { + EFI_STATUS res; + res = EfiGetHandles(ByProtocol, &gEfiGraphicsOutputProtocolGuid, 0, &gGraphHandles, &gGraphCount); + if (gGraphCount > 0) { + GraphGetIO(gGraphHandles[gGraphCount - 1], &gGraphOut); + } + return res; +} + +EFI_STATUS +GraphGetIO( + IN EFI_HANDLE Handle, + OUT EFI_GRAPHICS_OUTPUT_PROTOCOL** io + ) { + if (!io) { + return EFI_INVALID_PARAMETER; + } + return gBS->HandleProtocol(Handle, &gEfiGraphicsOutputProtocolGuid, (VOID**)io); +} + +EFI_STATUS +GraphGetModeInfo( + IN UINTN mode, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **info, + OUT UINTN* szInfo + ) { + if (!info || !gGraphOut || mode > gGraphOut->Mode->MaxMode) { + return EFI_INVALID_PARAMETER; + } + return gGraphOut->QueryMode(gGraphOut, (UINT32)mode, szInfo, info); +} + + +////////////////////////////////////////////////////////////////////////// +// Screen +////////////////////////////////////////////////////////////////////////// +EFI_STATUS +ScreenGetSize( + OUT UINTN *Height, + OUT UINTN *Width + ) +{ + if (gGraphOut == NULL) return EFI_INVALID_PARAMETER; + *Height = gGraphOut->Mode->Info->VerticalResolution; + *Width = gGraphOut->Mode->Info->HorizontalResolution; + return EFI_SUCCESS; +} + +EFI_STATUS ScreenFillRect( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, + IN UINTN x, + IN UINTN y, + IN UINTN width, + IN UINTN height + ) +{ + if (gGraphOut == NULL) return EFI_INVALID_PARAMETER; + + return gGraphOut->Blt(gGraphOut, color, EfiBltVideoFill, 0, 0, x, y, width, height, width * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); +} + +EFI_STATUS ScreenDrawBlt( + IN BLT_HEADER *blt, + IN UINTN x, + IN UINTN y + ) +{ + return gGraphOut->Blt(gGraphOut, blt->Pixels, EfiBltBufferToVideo, 0, 0, x, y, blt->Width, blt->Height, blt->Width * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); +} + +EFI_STATUS ScreenUpdateDirty( + IN BLT_HEADER *bltScreen + ) +{ + EFI_STATUS res = EFI_SUCCESS; + if (bltScreen->Dirty.top != bltScreen->Dirty.bottom || bltScreen->Dirty.left != bltScreen->Dirty.right) { + res = gGraphOut->Blt(gGraphOut, bltScreen->Pixels, EfiBltBufferToVideo, + bltScreen->Dirty.top, bltScreen->Dirty.left, // Source x,y + bltScreen->Dirty.top, bltScreen->Dirty.left, // Dest x,y + bltScreen->Dirty.right - bltScreen->Dirty.left + 1, bltScreen->Dirty.bottom - bltScreen->Dirty.top + 1, // width , height + bltScreen->Width * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + SetMem(&bltScreen->Dirty, sizeof(bltScreen->Dirty), 0); + } + return res; +} + +EFI_STATUS +ScreenSaveBlt( + OUT BLT_HEADER **bltScreen + ) +{ + UINTN height; + UINTN width; + EFI_STATUS res; + BLT_HEADER* blt; + if (!bltScreen) return EFI_INVALID_PARAMETER; + res = ScreenGetSize(&height, &width); + if (EFI_ERROR(res)) return res; + blt = (BLT_HEADER*)MEM_ALLOC(sizeof(BLT_HEADER) + height * width * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + blt->Height = (UINT32)height; + blt->Width = (UINT32)width; + *bltScreen = blt; + return gGraphOut->Blt(gGraphOut, blt->Pixels, EfiBltVideoToBltBuffer, 0, 0, 0, 0, blt->Width, blt->Height, blt->Width * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); +} + +////////////////////////////////////////////////////////////////////////// +// Colors +////////////////////////////////////////////////////////////////////////// + +EFI_GRAPHICS_OUTPUT_BLT_PIXEL gColorBlack = { 0,0,0,0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL gColorWhite = { 255,255,255,0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL gColorBlue = { 255,0,0,0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL gColorGreen = { 0,255,0,0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL gColorRed = { 0,0,255,0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL gColorGray = { 128,128,128,0 }; +DRAW_CONTEXT gDrawContext = { { 255, 255, 255, 0 }, DrawOpSet, 0xFFFFFFFF, 128, { 128,128,128, 0}, NULL }; +INT32 gBrush3[5*2] = { -1,0, 1,0, 0,1, 0,-1, 0,0 }; + +////////////////////////////////////////////////////////////////////////// +// Blt +////////////////////////////////////////////////////////////////////////// +EFI_STATUS +BltDrawBlt( + IN OUT BLT_HEADER* canvas, + IN BLT_HEADER* blt, + IN UINTN x, + IN UINTN y + ) { + UINTN row, col; + DRAW_CONTEXT ctx; + ctx.Op = DrawOpSet; + ctx.Brush = NULL; + for (row = 0; row < blt->Height; ++row) { + for (col = 0; col < blt->Width; ++col) { + ctx.Color = blt->Pixels[col + row * blt->Width]; + BltPoint(canvas, &ctx, x + col, y + row); + } + } + return EFI_SUCCESS; +} + +EFI_STATUS +RectMarkDirty( + IN OUT PRECT rect, + IN UINTN x, + IN UINTN y + ) { + if (!rect) return EFI_INVALID_PARAMETER; + if (rect->top > y) rect->top = (UINT32)y; + if (rect->bottom < y) rect->bottom = (UINT32)y; + if (rect->left > x) rect->left = (UINT32)x; + if (rect->right < x) rect->right = (UINT32)x; + return EFI_SUCCESS; +} + + +EFI_STATUS +BltPointSingle( + IN BLT_HEADER* blt, + IN PDRAW_CONTEXT draw, + IN UINTN x, + IN UINTN y + ) { + UINTN pos; + if (!blt || x >= blt->Width || y >= blt->Height) return EFI_INVALID_PARAMETER; + RectMarkDirty(&blt->Dirty, x, y); + pos = x + y * blt->Width; + if (!draw) draw = &gDrawContext; + switch (draw->Op) + { + case DrawOpClear: + *(UINT32*)&blt->Pixels[pos] &= ~(*(UINT32*)&draw->Color); + break; + case DrawOpXor: + *(UINT32*)&blt->Pixels[pos] ^= *(UINT32*)&draw->Color; + break; + case DrawOpOr: + *(UINT32*)&blt->Pixels[pos] |= *(UINT32*)&draw->Color; + break; + case DrawOpSet: + blt->Pixels[pos] = draw->Color; + break; + case DrawOpAlpha: + { + UINT8 val; + val = blt->Pixels[pos].Red; + blt->Pixels[pos].Red = (UINT8)(val + (((draw->AlphaColor.Red - val) * draw->Alpha) >> 8)); + + val = blt->Pixels[pos].Green; + blt->Pixels[pos].Green = (UINT8)(val + (((draw->AlphaColor.Green - val) * draw->Alpha) >> 8)); + + val = blt->Pixels[pos].Blue; + blt->Pixels[pos].Blue = (UINT8)(val + (((draw->AlphaColor.Blue - val) * draw->Alpha) >> 8)); + + break; + } + default: + break; + } + return EFI_SUCCESS; +} + +EFI_STATUS +BltPoint( + IN BLT_HEADER* blt, + IN PDRAW_CONTEXT draw, + IN UINTN x, + IN UINTN y + ) { + if (draw->Brush == NULL) return BltPointSingle(blt, draw, x, y); + else + { + INT32* offset = draw->Brush; + do { + BltPointSingle(blt, draw, x + offset[0], y + +offset[1]); + offset += 2; + } while (!(offset[0] == 0 && offset[1] == 0)); + return BltPointSingle(blt, draw, x, y); + } +} + +VOID +BltBox( + IN BLT_HEADER* blt, + IN PDRAW_CONTEXT draw, + IN INT32 x0, + IN INT32 y0, + IN INT32 x1, + IN INT32 y1) +{ + BltLine(blt, draw, x0, y0, x1, y0); + BltLine(blt, draw, x0, y0, x0, y1); + BltLine(blt, draw, x0, y1, x1, y1); + BltLine(blt, draw, x1, y0, x1, y1); +} + +VOID +BltFill( + IN BLT_HEADER* blt, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL fill, + IN INT32 x0, + IN INT32 y0, + IN INT32 x1, + IN INT32 y1) +{ + INT32 x; + INT32 y; + DRAW_CONTEXT ctx; + ctx.Op = DrawOpSet; + ctx.Brush = NULL; + ctx.Color = fill; + for (y = y0; y < y1; ++y) { + for (x = x0; x < x1; ++x) { + BltPoint(blt, &ctx, x, y); + } + } +} + +VOID +BltLine( + IN BLT_HEADER* blt, + IN PDRAW_CONTEXT draw, + IN INT32 x0, + IN INT32 y0, + IN INT32 x1, + IN INT32 y1) +{ + int dx = ABS(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = -ABS(y1 - y0), sy = y0 < y1 ? 1 : -1; + UINT32 mask, dmask, cmask; + int err = dx + dy, e2; /* error value e_xy */ + mask = draw ? draw->DashLine : gDrawContext.DashLine; + dmask = mask; + cmask = 32; + for (;;) { /* loop */ + // Dash + if ((dmask & 1) == 1) { + BltPoint(blt, draw, x0, y0); + } + dmask >>= 1; + cmask--; + if (cmask == 0) { + dmask = mask; + cmask = 32; + } + // next point + e2 = 2 * err; + if (e2 >= dy) { /* e_xy+e_x > 0 */ + if (x0 == x1) break; + err += dy; x0 += sx; + } + if (e2 <= dx) { /* e_xy+e_y < 0 */ + if (y0 == y1) break; + err += dx; y0 += sy; + } + } +} + + + +VOID +BltCircle( + IN BLT_HEADER* blt, + IN PDRAW_CONTEXT draw, + IN INT32 xm, + IN INT32 ym, + IN INT32 r, + IN BOOLEAN fill) +{ + int sx = 1; + int x = -r, y = 0, err = 2 - 2 * r; /* bottom left to top right */ + UINT32 mask, dmask, cmask; + mask = draw ? draw->DashLine : gDrawContext.DashLine; + dmask = mask; + cmask = 32; + do { + if (fill) { + if (sx != x) { + int i; + for (i = ym - y; i <= ym + y; i++) { + BltPoint(blt, draw, xm + x, i); + if( x != 0 ) BltPoint(blt, draw, xm - x, i); + } + sx = x; + } + } else { + if ((dmask & 1) == 1) { + BltPoint(blt, draw, xm - x, ym + y); /* I. Quadrant +x +y */ + BltPoint(blt, draw, xm - y, ym - x); /* II. Quadrant -x +y */ + BltPoint(blt, draw, xm + x, ym - y); /* III. Quadrant -x -y */ + BltPoint(blt, draw, xm + y, ym + x); /* IV. Quadrant +x -y */ + } + dmask >>= 1; + cmask--; + } + if (cmask == 0) { + dmask = mask; + cmask = 32; + } + r = err; + if (r <= y) err += ++y * 2 + 1; /* e_xy+e_y < 0 */ + if (r >= x || err > y) /* e_xy+e_x > 0 or no 2nd y-step */ + err += ++x * 2 + 1; /* -> x-step now */ + } while (x <= 0); +} + +extern __int8 gSimplex_ascii_32_126[95][112]; +VOID +BltText( + IN BLT_HEADER* blt, + IN PDRAW_CONTEXT draw, + IN INT32 x, + IN INT32 y, + IN INT32 scale, // 0..256 reduce 256... enlarge + IN CONST CHAR8 *text) +{ + INT32 posX = x; + INT32 posY = y; + const char *c; + for (c = text; *c; ++c) + { + INT8 ch = *c; + if (ch >= 32 && ch <= 126) { + INT8 *it = gSimplex_ascii_32_126[ch - 32]; + INT32 nvtcs = *it++; + INT32 spacing = *it++; + INT32 fromX = -1; + INT32 fromY = -1; + INTN i; + for (i = 0; i < nvtcs; ++i) { + INT32 toX = *it++; + INT32 toY = *it++; + if ((fromX != -1 || fromY != -1) && (toX != -1 || toY != -1)) { + BltLine( + blt, draw, + posX + ((fromX * scale) >> 8), posY + (((25 - fromY) * scale) >> 8), + posX + ((toX * scale) >> 8), posY + (((25 - toY) * scale) >> 8)); + } + fromX = toX; + fromY = toY; + } + posX += (spacing * scale) >> 8; + } + // Next line + if (ch == '\n') { + posX = x; + posY += 30 * scale >> 8; + } + } +} + +////////////////////////////////////////////////////////////////////////// +// Bmp +////////////////////////////////////////////////////////////////////////// + +#pragma pack(1) +typedef struct { + UINT8 Blue; + UINT8 Green; + UINT8 Red; + UINT8 Reserved; +} BMP_COLOR_MAP; + +typedef struct { + CHAR8 CharB; + CHAR8 CharM; + UINT32 Size; + UINT16 Reserved[2]; + UINT32 ImageOffset; + UINT32 HeaderSize; + UINT32 PixelWidth; + UINT32 PixelHeight; + UINT16 Planes; // Must be 1 + UINT16 BitPerPixel; // 1, 4, 8, or 24 + UINT32 CompressionType; + UINT32 ImageSize; // Compressed image size in bytes + UINT32 XPixelsPerMeter; + UINT32 YPixelsPerMeter; + UINT32 NumberOfColors; + UINT32 ImportantColors; +} BMP_IMAGE_HEADER; +#pragma pack() + +EFI_STATUS BmpGetSize( + IN const unsigned char* BmpImage, + IN UINTN BmpImageSize, + OUT UINTN *Height, + OUT UINTN *Width + ) +{ + BMP_IMAGE_HEADER *BmpHeader; + + if (sizeof(BMP_IMAGE_HEADER) > BmpImageSize) { + return EFI_INVALID_PARAMETER; + } + + BmpHeader = (BMP_IMAGE_HEADER *)BmpImage; + + if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M') { + return EFI_UNSUPPORTED; + } + + // + // Only support BITMAPINFOHEADER format. + // BITMAPFILEHEADER + BITMAPINFOHEADER = BMP_IMAGE_HEADER + // + if (BmpHeader->HeaderSize != sizeof(BMP_IMAGE_HEADER) - ((UINTN)&(((BMP_IMAGE_HEADER *)0)->HeaderSize))) { + return EFI_UNSUPPORTED; + } + + *Height = BmpHeader->PixelHeight; + *Width = BmpHeader->PixelWidth; + return EFI_SUCCESS; +} + +EFI_STATUS +BmpToBlt( + IN CONST VOID *BmpImage, + IN UINTN BmpImageSize, + OUT BLT_HEADER **blt + ) +{ + UINT8 *Image; + UINT8 *ImageHeader; + BMP_IMAGE_HEADER *BmpHeader; + BMP_COLOR_MAP *BmpColorMap; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt_; + UINT64 BltBufferSize; + UINTN Index; + UINTN Height; + UINTN Width; + UINTN ImageIndex; + UINTN DataSizePerLine; + UINT32 ColorMapNum; + + if (sizeof(BMP_IMAGE_HEADER) > BmpImageSize) { + return EFI_INVALID_PARAMETER; + } + + BmpHeader = (BMP_IMAGE_HEADER *)BmpImage; + + if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M') { + return EFI_UNSUPPORTED; + } + + // + // Doesn't support compress. + // + if (BmpHeader->CompressionType != 0) { + return EFI_UNSUPPORTED; + } + + // + // Only support BITMAPINFOHEADER format. + // BITMAPFILEHEADER + BITMAPINFOHEADER = BMP_IMAGE_HEADER + // + if (BmpHeader->HeaderSize != sizeof(BMP_IMAGE_HEADER) - ((UINTN)&(((BMP_IMAGE_HEADER *)0)->HeaderSize))) { + return EFI_UNSUPPORTED; + } + + // + // The data size in each line must be 4 byte alignment. + // + DataSizePerLine = ((BmpHeader->PixelWidth * BmpHeader->BitPerPixel + 31) >> 3) & (~0x3); + BltBufferSize = MultU64x32(DataSizePerLine, BmpHeader->PixelHeight); + if (BltBufferSize > (UINT32)~0) { + return EFI_INVALID_PARAMETER; + } + + if ((BmpHeader->Size != BmpImageSize) || + (BmpHeader->Size < BmpHeader->ImageOffset) || + (BmpHeader->Size - BmpHeader->ImageOffset != BmpHeader->PixelHeight * DataSizePerLine)) { + return EFI_INVALID_PARAMETER; + } + + // + // Calculate Color Map offset in the image. + // + Image = (UINT8 *)BmpImage; + BmpColorMap = (BMP_COLOR_MAP *)(Image + sizeof(BMP_IMAGE_HEADER)); + if (BmpHeader->ImageOffset < sizeof(BMP_IMAGE_HEADER)) { + return EFI_INVALID_PARAMETER; + } + + if (BmpHeader->ImageOffset > sizeof(BMP_IMAGE_HEADER)) { + switch (BmpHeader->BitPerPixel) { + case 1: + ColorMapNum = 2; + break; + case 4: + ColorMapNum = 16; + break; + case 8: + ColorMapNum = 256; + break; + default: + ColorMapNum = 0; + break; + } + if (BmpHeader->ImageOffset - sizeof(BMP_IMAGE_HEADER) != sizeof(BMP_COLOR_MAP) * ColorMapNum) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Calculate graphics image data address in the image + // + Image = ((UINT8 *)BmpImage) + BmpHeader->ImageOffset; + ImageHeader = Image; + + BltBufferSize = MultU64x32((UINT64)BmpHeader->PixelWidth, BmpHeader->PixelHeight); + // + // Ensure the BltBufferSize * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) doesn't overflow + // + if (BltBufferSize > DivU64x32((UINTN)~0, sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL))) { + return EFI_UNSUPPORTED; + } + BltBufferSize = MultU64x32(BltBufferSize, sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + + *blt = MEM_ALLOC((UINTN)BltBufferSize + sizeof(**blt)); + + (*blt)->Width = BmpHeader->PixelWidth; + (*blt)->Height = BmpHeader->PixelHeight; + + // + // Convert image from BMP to Blt buffer format + // + BltBuffer = (*blt)->Pixels; + for (Height = 0; Height < BmpHeader->PixelHeight; Height++) { + Blt_ = &BltBuffer[(BmpHeader->PixelHeight - Height - 1) * BmpHeader->PixelWidth]; + for (Width = 0; Width < BmpHeader->PixelWidth; Width++, Image++, Blt_++) { + switch (BmpHeader->BitPerPixel) { + case 1: + // + // Convert 1bit BMP to 24-bit color + // + for (Index = 0; Index < 8 && Width < BmpHeader->PixelWidth; Index++) { + Blt_->Red = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Red; + Blt_->Green = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Green; + Blt_->Blue = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Blue; + Blt_++; + Width++; + } + + Blt_--; + Width--; + break; + + case 4: + // + // Convert BMP Palette to 24-bit color + // + Index = (*Image) >> 4; + Blt_->Red = BmpColorMap[Index].Red; + Blt_->Green = BmpColorMap[Index].Green; + Blt_->Blue = BmpColorMap[Index].Blue; + if (Width < (BmpHeader->PixelWidth - 1)) { + Blt_++; + Width++; + Index = (*Image) & 0x0f; + Blt_->Red = BmpColorMap[Index].Red; + Blt_->Green = BmpColorMap[Index].Green; + Blt_->Blue = BmpColorMap[Index].Blue; + } + break; + + case 8: + // + // Convert BMP Palette to 24-bit color + // + Blt_->Red = BmpColorMap[*Image].Red; + Blt_->Green = BmpColorMap[*Image].Green; + Blt_->Blue = BmpColorMap[*Image].Blue; + break; + + case 24: + Blt_->Blue = *Image++; + Blt_->Green = *Image++; + Blt_->Red = *Image; + break; + + default: + MEM_FREE(*blt); + *blt = NULL; + return EFI_UNSUPPORTED; + break; + }; + + } + + ImageIndex = (UINTN)(Image - ImageHeader); + if ((ImageIndex % 4) != 0) { + // + // Bmp Image starts each row on a 32-bit boundary! + // + Image = Image + (4 - (ImageIndex % 4)); + } + } + + return EFI_SUCCESS; +} -- cgit v1.2.3