/** @file
AppleImageConversion protocol
Copyright (C) 2018 savvas. All rights reserved.
Portions copyright (C) 2016 slice. All rights reserved.
Portions copyright (C) 2018 vit9696. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include
#include
#include
#include
#include
#include
#include
#include
STATIC
CONST UINT8
mPngHeader[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
STATIC
EFI_STATUS
EFIAPI
RecognizeImageData (
IN VOID *ImageBuffer,
IN UINTN ImageSize
)
{
if ((ImageBuffer == NULL) || (ImageSize == 0)) {
return EFI_INVALID_PARAMETER;
}
if ( (ImageSize < sizeof (mPngHeader))
|| (CompareMem (ImageBuffer, mPngHeader, sizeof (mPngHeader)) != 0))
{
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
GetImageDims (
IN VOID *ImageBuffer,
IN UINTN ImageSize,
OUT UINT32 *ImageWidth,
OUT UINT32 *ImageHeight
)
{
EFI_STATUS Status;
if ( (ImageBuffer == NULL)
|| (ImageSize == 0)
|| (ImageWidth == NULL)
|| (ImageHeight == NULL))
{
return EFI_INVALID_PARAMETER;
}
Status = OcGetPngDims (ImageBuffer, ImageSize, ImageWidth, ImageHeight);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "OCIC: Failed to obtain image dimensions for image\n"));
}
return Status;
}
STATIC
EFI_STATUS
EFIAPI
DecodeImageData (
IN VOID *ImageBuffer,
IN UINTN ImageSize,
IN OUT EFI_UGA_PIXEL **RawImageData,
IN OUT UINTN *RawImageDataSize
)
{
EFI_STATUS Status;
UINTN Index;
UINTN PixelCount;
UINTN ByteCount;
VOID *RealImageData;
EFI_UGA_PIXEL *PixelWalker;
UINT32 Width;
UINT32 Height;
UINT8 TmpChannel;
STATIC_ASSERT (sizeof (EFI_UGA_PIXEL) == sizeof (UINT32), "Unsupported pixel size");
STATIC_ASSERT (OFFSET_OF (EFI_UGA_PIXEL, Blue) == 0, "Unsupported pixel format");
STATIC_ASSERT (OFFSET_OF (EFI_UGA_PIXEL, Green) == 1, "Unsupported pixel format");
STATIC_ASSERT (OFFSET_OF (EFI_UGA_PIXEL, Red) == 2, "Unsupported pixel format");
STATIC_ASSERT (OFFSET_OF (EFI_UGA_PIXEL, Reserved) == 3, "Unsupported pixel format");
if ( (ImageBuffer == NULL)
|| (ImageSize == 0)
|| (RawImageData == NULL)
|| (RawImageDataSize == NULL))
{
return EFI_INVALID_PARAMETER;
}
Status = OcDecodePng (
ImageBuffer,
ImageSize,
(VOID **)&RealImageData,
&Width,
&Height,
NULL
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
PixelCount = (UINTN)Width * Height;
ByteCount = PixelCount * sizeof (*RawImageData);
//
// The buffer can be callee or caller allocated.
// This is differentiated by passing non-null to *RawImageData.
// For now we always allocate our own data, since boot.efi lets caller do it anyway.
//
if (*RawImageData != NULL) {
if (*RawImageDataSize < ByteCount) {
FreePool (RealImageData);
*RawImageDataSize = ByteCount;
return EFI_BUFFER_TOO_SMALL;
}
CopyMem (*RawImageData, RealImageData, ByteCount);
FreePool (RealImageData);
*RawImageDataSize = ByteCount;
} else {
*RawImageData = RealImageData;
}
PixelWalker = *RawImageData;
for (Index = 0; Index < PixelCount; ++Index) {
TmpChannel = PixelWalker->Blue;
PixelWalker->Blue = PixelWalker->Red;
PixelWalker->Red = TmpChannel;
PixelWalker->Reserved = 0xFF - PixelWalker->Reserved;
++PixelWalker;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
GetImageDimsEx (
IN VOID *Buffer,
IN UINTN BufferSize,
IN UINTN Scale,
OUT UINT32 *Width,
OUT UINT32 *Height
)
{
if ( (Buffer == NULL)
|| (BufferSize == 0)
|| (Scale == 0)
|| (Width == NULL)
|| (Height == NULL))
{
return EFI_INVALID_PARAMETER;
}
if (Scale > 1) {
return EFI_UNSUPPORTED;
}
return GetImageDims (Buffer, BufferSize, Width, Height);
}
STATIC
EFI_STATUS
EFIAPI
DecodeImageDataEx (
IN VOID *Buffer,
IN UINTN BufferSize,
IN UINTN Scale,
IN OUT EFI_UGA_PIXEL **RawImageData,
IN OUT UINTN *RawImageDataSize
)
{
if ( (Buffer == NULL)
|| (BufferSize == 0)
|| (Scale == 0)
|| (RawImageData == NULL)
|| (RawImageDataSize == NULL))
{
return EFI_INVALID_PARAMETER;
}
if (Scale > 1) {
return EFI_UNSUPPORTED;
}
return DecodeImageData (Buffer, BufferSize, RawImageData, RawImageDataSize);
}
//
// Image codec protocol instance.
//
STATIC APPLE_IMAGE_CONVERSION_PROTOCOL mAppleImageConversion = {
APPLE_IMAGE_CONVERSION_PROTOCOL_REVISION,
APPLE_IMAGE_CONVERSION_PROTOCOL_ANY_EXTENSION,
RecognizeImageData,
GetImageDims,
DecodeImageData,
GetImageDimsEx,
DecodeImageDataEx
};
APPLE_IMAGE_CONVERSION_PROTOCOL *
OcAppleImageConversionInstallProtocol (
IN BOOLEAN Reinstall
)
{
EFI_STATUS Status;
APPLE_IMAGE_CONVERSION_PROTOCOL *AppleImageConversionInterface;
EFI_HANDLE NewHandle;
if (Reinstall) {
Status = OcUninstallAllProtocolInstances (&gAppleImageConversionProtocolGuid);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "OCIC: Uninstall failed: %r\n", Status));
return NULL;
}
} else {
Status = gBS->LocateProtocol (
&gAppleImageConversionProtocolGuid,
NULL,
(VOID **)&AppleImageConversionInterface
);
if (!EFI_ERROR (Status)) {
return AppleImageConversionInterface;
}
}
NewHandle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&NewHandle,
&gAppleImageConversionProtocolGuid,
&mAppleImageConversion,
NULL
);
if (EFI_ERROR (Status)) {
return NULL;
}
return &mAppleImageConversion;
}