/** @file This file is part of OpenCanopy, OpenCore GUI. Copyright (c) 2018-2019, Download-Fritz. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause **/ #include #include #include #include #include #include #include #include #include "OpenCanopy.h" // // Disk label palette. // STATIC CONST UINT8 gAppleDiskLabelImagePalette[256] = { [0x00] = 255, [0xf6] = 238, [0xf7] = 221, [0x2a] = 204, [0xf8] = 187, [0xf9] = 170, [0x55] = 153, [0xfa] = 136, [0xfb] = 119, [0x80] = 102, [0xfc] = 85, [0xfd] = 68, [0xab] = 51, [0xfe] = 34, [0xff] = 17, [0xd6] = 0 }; EFI_STATUS GuiIcnsToImageIcon ( OUT GUI_IMAGE *Image, IN VOID *IcnsImage, IN UINT32 IcnsImageSize, IN UINT8 Scale, IN UINT32 MatchWidth, IN UINT32 MatchHeight, IN BOOLEAN AllowLess ) { EFI_STATUS Status; UINT32 Offset; UINT32 RecordLength; UINT32 ImageSize; UINT32 DecodedBytes; APPLE_ICNS_RECORD *Record; APPLE_ICNS_RECORD *RecordIT32; APPLE_ICNS_RECORD *RecordT8MK; ASSERT (Scale == 1 || Scale == 2); // // We do not need to support 'it32' 128x128 icon format, // because Finder automatically converts the icons to PNG-based // when assigning volume icon. // if (IcnsImageSize < sizeof (APPLE_ICNS_RECORD)*2) { return EFI_INVALID_PARAMETER; } Record = IcnsImage; if ((Record->Type != APPLE_ICNS_MAGIC) || (SwapBytes32 (Record->Size) != IcnsImageSize)) { return EFI_SECURITY_VIOLATION; } RecordIT32 = NULL; RecordT8MK = NULL; Offset = sizeof (APPLE_ICNS_RECORD); while (Offset < IcnsImageSize - sizeof (APPLE_ICNS_RECORD)) { Record = (APPLE_ICNS_RECORD *)((UINT8 *)IcnsImage + Offset); RecordLength = SwapBytes32 (Record->Size); // // 1. Record smaller than its header and 1 32-bit word is invalid. // 32-bit is required by some entries like IT32 (see below). // 2. Record overflowing UINT32 is invalid. // 3. Record larger than file size is invalid. // if ( (RecordLength < sizeof (APPLE_ICNS_RECORD) + sizeof (UINT32)) || OcOverflowAddU32 (Offset, RecordLength, &Offset) || (Offset > IcnsImageSize)) { return EFI_SECURITY_VIOLATION; } if ( ((Scale == 1) && (Record->Type == APPLE_ICNS_IC07)) || ((Scale == 2) && (Record->Type == APPLE_ICNS_IC13))) { Status = GuiPngToImage ( Image, Record->Data, RecordLength - sizeof (APPLE_ICNS_RECORD), TRUE ); if (!EFI_ERROR (Status) && (MatchWidth > 0) && (MatchHeight > 0)) { if (AllowLess ? ( (Image->Width > MatchWidth * Scale) || (Image->Height > MatchWidth * Scale) || (Image->Width == 0) || (Image->Height == 0)) : ((Image->Width != MatchWidth * Scale) || (Image->Height != MatchHeight * Scale))) { FreePool (Image->Buffer); DEBUG (( DEBUG_INFO, "OCUI: Expected %dx%d, actual %dx%d, allow less: %d\n", MatchWidth * Scale, MatchHeight * Scale, Image->Width, Image->Height, AllowLess )); Status = EFI_UNSUPPORTED; } } return Status; } if (Scale == 1) { if (Record->Type == APPLE_ICNS_IT32) { RecordIT32 = Record; } else if (Record->Type == APPLE_ICNS_T8MK) { RecordT8MK = Record; } if ((RecordT8MK != NULL) && (RecordIT32 != NULL)) { Image->Width = MatchWidth; Image->Height = MatchHeight; ImageSize = (MatchWidth * MatchHeight) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); Image->Buffer = AllocateZeroPool (ImageSize); if (Image->Buffer == NULL) { return EFI_OUT_OF_RESOURCES; } // // We have to add an additional UINT32 for IT32, since it has a reserved field. // DecodedBytes = DecompressMaskedRLE24 ( (UINT8 *)Image->Buffer, ImageSize, RecordIT32->Data + sizeof (UINT32), SwapBytes32 (RecordIT32->Size) - sizeof (APPLE_ICNS_RECORD) - sizeof (UINT32), RecordT8MK->Data, SwapBytes32 (RecordT8MK->Size) - sizeof (APPLE_ICNS_RECORD), TRUE ); if (DecodedBytes != ImageSize) { FreePool (Image->Buffer); return EFI_UNSUPPORTED; } return EFI_SUCCESS; } } } return EFI_NOT_FOUND; } EFI_STATUS GuiLabelToImage ( OUT GUI_IMAGE *Image, IN VOID *RawData, IN UINT32 DataLength, IN UINT8 Scale, IN BOOLEAN Inverted ) { APPLE_DISK_LABEL *Label; UINT32 PixelIdx; ASSERT (RawData != NULL); ASSERT (Scale == 1 || Scale == 2); if (DataLength < sizeof (APPLE_DISK_LABEL)) { return EFI_INVALID_PARAMETER; } Label = RawData; Image->Width = SwapBytes16 (Label->Width); Image->Height = SwapBytes16 (Label->Height); if ( (Image->Width > APPLE_DISK_LABEL_MAX_WIDTH * Scale) || (Image->Height > APPLE_DISK_LABEL_MAX_HEIGHT * Scale) || (DataLength != sizeof (APPLE_DISK_LABEL) + Image->Width * Image->Height)) { DEBUG ((DEBUG_INFO, "OCUI: Invalid label has %dx%d dims at %u size\n", Image->Width, Image->Height, DataLength)); return EFI_SECURITY_VIOLATION; } Image->Buffer = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)AllocatePool ( Image->Width * Image->Height * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) ); if (Image->Buffer == NULL) { return EFI_OUT_OF_RESOURCES; } if (Inverted) { for (PixelIdx = 0; PixelIdx < Image->Width * Image->Height; PixelIdx++) { Image->Buffer[PixelIdx].Blue = 0; Image->Buffer[PixelIdx].Green = 0; Image->Buffer[PixelIdx].Red = 0; Image->Buffer[PixelIdx].Reserved = 255 - gAppleDiskLabelImagePalette[Label->Data[PixelIdx]]; } } else { for (PixelIdx = 0; PixelIdx < Image->Width * Image->Height; PixelIdx++) { Image->Buffer[PixelIdx].Blue = 255 - gAppleDiskLabelImagePalette[Label->Data[PixelIdx]]; Image->Buffer[PixelIdx].Green = 255 - gAppleDiskLabelImagePalette[Label->Data[PixelIdx]]; Image->Buffer[PixelIdx].Red = 255 - gAppleDiskLabelImagePalette[Label->Data[PixelIdx]]; Image->Buffer[PixelIdx].Reserved = 255 - gAppleDiskLabelImagePalette[Label->Data[PixelIdx]]; } } return EFI_SUCCESS; } EFI_STATUS GuiPngToImage ( OUT GUI_IMAGE *Image, IN VOID *ImageData, IN UINTN ImageDataSize, IN BOOLEAN PremultiplyAlpha ) { EFI_STATUS Status; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BufferWalker; UINTN Index; UINT8 TmpChannel; Status = OcDecodePng ( ImageData, ImageDataSize, (VOID **)&Image->Buffer, &Image->Width, &Image->Height, NULL ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCUI: DecodePNG - %r\n", Status)); return Status; } if (PremultiplyAlpha) { BufferWalker = Image->Buffer; for (Index = 0; Index < (UINTN)Image->Width * Image->Height; ++Index) { TmpChannel = (UINT8)((BufferWalker->Blue * BufferWalker->Reserved) / 0xFF); BufferWalker->Blue = (UINT8)((BufferWalker->Red * BufferWalker->Reserved) / 0xFF); BufferWalker->Green = (UINT8)((BufferWalker->Green * BufferWalker->Reserved) / 0xFF); BufferWalker->Red = TmpChannel; ++BufferWalker; } } return EFI_SUCCESS; } EFI_STATUS GuiCreateHighlightedImage ( OUT GUI_IMAGE *SelectedImage, IN CONST GUI_IMAGE *SourceImage, IN CONST EFI_GRAPHICS_OUTPUT_BLT_PIXEL *HighlightPixel ) { EFI_GRAPHICS_OUTPUT_BLT_PIXEL PremulPixel; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Buffer; UINT32 ColumnOffset; BOOLEAN OneSet; UINT32 FirstUnsetX; UINT32 IndexY; UINT32 RowOffset; ASSERT (SelectedImage != NULL); ASSERT (SourceImage != NULL); ASSERT (SourceImage->Buffer != NULL); ASSERT (HighlightPixel != NULL); // // The multiplication cannot wrap around because the original allocation sane. // Buffer = AllocateCopyPool ( SourceImage->Width * SourceImage->Height * sizeof (*SourceImage->Buffer), SourceImage->Buffer ); if (Buffer == NULL) { return EFI_OUT_OF_RESOURCES; } PremulPixel.Blue = (UINT8)((HighlightPixel->Blue * HighlightPixel->Reserved) / 0xFF); PremulPixel.Green = (UINT8)((HighlightPixel->Green * HighlightPixel->Reserved) / 0xFF); PremulPixel.Red = (UINT8)((HighlightPixel->Red * HighlightPixel->Reserved) / 0xFF); PremulPixel.Reserved = HighlightPixel->Reserved; for ( IndexY = 0, RowOffset = 0; IndexY < SourceImage->Height; ++IndexY, RowOffset += SourceImage->Width ) { FirstUnsetX = 0; OneSet = FALSE; for (ColumnOffset = 0; ColumnOffset < SourceImage->Width; ++ColumnOffset) { if (SourceImage->Buffer[RowOffset + ColumnOffset].Reserved != 0) { OneSet = TRUE; GuiBlendPixelSolid (&Buffer[RowOffset + ColumnOffset], &PremulPixel); if (FirstUnsetX != 0) { // // Set all fully transparent pixels between two not fully transparent // pixels to the highlighter pixel. // while (FirstUnsetX < ColumnOffset) { CopyMem ( &Buffer[RowOffset + FirstUnsetX], &PremulPixel, sizeof (*Buffer) ); ++FirstUnsetX; } FirstUnsetX = 0; } } else if ((FirstUnsetX == 0) && OneSet) { FirstUnsetX = ColumnOffset; } } } SelectedImage->Width = SourceImage->Width; SelectedImage->Height = SourceImage->Height; SelectedImage->Buffer = Buffer; return EFI_SUCCESS; }