/** @file
Implementation for EFI_HII_IMAGE_PROTOCOL.


Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "HiiDatabase.h"

#define MAX_UINT24  0xFFFFFF

/**
  Get the imageid of last image block: EFI_HII_IIBT_END_BLOCK when input
  ImageId is zero, otherwise return the address of the
  corresponding image block with identifier specified by ImageId.

  This is a internal function.

  @param ImageBlocks     Points to the beginning of a series of image blocks stored in order.
  @param ImageId         If input ImageId is 0, output the image id of the EFI_HII_IIBT_END_BLOCK;
                         else use this id to find its corresponding image block address.

  @return The image block address when input ImageId is not zero; otherwise return NULL.

**/
EFI_HII_IMAGE_BLOCK *
GetImageIdOrAddress (
  IN EFI_HII_IMAGE_BLOCK  *ImageBlocks,
  IN OUT EFI_IMAGE_ID     *ImageId
  )
{
  EFI_IMAGE_ID         ImageIdCurrent;
  EFI_HII_IMAGE_BLOCK  *CurrentImageBlock;
  UINTN                Length;

  ASSERT (ImageBlocks != NULL && ImageId != NULL);
  CurrentImageBlock = ImageBlocks;
  ImageIdCurrent    = 1;

  while (CurrentImageBlock->BlockType != EFI_HII_IIBT_END) {
    if (*ImageId != 0) {
      if (*ImageId == ImageIdCurrent) {
        //
        // If the found image block is a duplicate block, update the ImageId to
        // find the previous defined image block.
        //
        if (CurrentImageBlock->BlockType == EFI_HII_IIBT_DUPLICATE) {
          *ImageId = ReadUnaligned16 ((VOID *)&((EFI_HII_IIBT_DUPLICATE_BLOCK *)CurrentImageBlock)->ImageId);
          ASSERT (*ImageId != ImageIdCurrent);
          ASSERT (*ImageId != 0);
          CurrentImageBlock = ImageBlocks;
          ImageIdCurrent    = 1;
          continue;
        }

        return CurrentImageBlock;
      }

      if (*ImageId < ImageIdCurrent) {
        //
        // Can not find the specified image block in this image.
        //
        return NULL;
      }
    }

    switch (CurrentImageBlock->BlockType) {
      case EFI_HII_IIBT_EXT1:
        Length = ((EFI_HII_IIBT_EXT1_BLOCK *)CurrentImageBlock)->Length;
        break;
      case EFI_HII_IIBT_EXT2:
        Length = ReadUnaligned16 (&((EFI_HII_IIBT_EXT2_BLOCK *)CurrentImageBlock)->Length);
        break;
      case EFI_HII_IIBT_EXT4:
        Length = ReadUnaligned32 ((VOID *)&((EFI_HII_IIBT_EXT4_BLOCK *)CurrentImageBlock)->Length);
        break;

      case EFI_HII_IIBT_IMAGE_1BIT:
      case EFI_HII_IIBT_IMAGE_1BIT_TRANS:
        Length = sizeof (EFI_HII_IIBT_IMAGE_1BIT_BLOCK) - sizeof (UINT8) +
                 BITMAP_LEN_1_BIT (
                   ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_1BIT_BLOCK *)CurrentImageBlock)->Bitmap.Width),
                   ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_1BIT_BLOCK *)CurrentImageBlock)->Bitmap.Height)
                   );
        ImageIdCurrent++;
        break;

      case EFI_HII_IIBT_IMAGE_4BIT:
      case EFI_HII_IIBT_IMAGE_4BIT_TRANS:
        Length = sizeof (EFI_HII_IIBT_IMAGE_4BIT_BLOCK) - sizeof (UINT8) +
                 BITMAP_LEN_4_BIT (
                   ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_4BIT_BLOCK *)CurrentImageBlock)->Bitmap.Width),
                   ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_4BIT_BLOCK *)CurrentImageBlock)->Bitmap.Height)
                   );
        ImageIdCurrent++;
        break;

      case EFI_HII_IIBT_IMAGE_8BIT:
      case EFI_HII_IIBT_IMAGE_8BIT_TRANS:
        Length = sizeof (EFI_HII_IIBT_IMAGE_8BIT_BLOCK) - sizeof (UINT8) +
                 BITMAP_LEN_8_BIT (
                   (UINT32)ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_8BIT_BLOCK *)CurrentImageBlock)->Bitmap.Width),
                   ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_8BIT_BLOCK *)CurrentImageBlock)->Bitmap.Height)
                   );
        ImageIdCurrent++;
        break;

      case EFI_HII_IIBT_IMAGE_24BIT:
      case EFI_HII_IIBT_IMAGE_24BIT_TRANS:
        Length = sizeof (EFI_HII_IIBT_IMAGE_24BIT_BLOCK) - sizeof (EFI_HII_RGB_PIXEL) +
                 BITMAP_LEN_24_BIT (
                   (UINT32)ReadUnaligned16 ((VOID *)&((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)CurrentImageBlock)->Bitmap.Width),
                   ReadUnaligned16 ((VOID *)&((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)CurrentImageBlock)->Bitmap.Height)
                   );
        ImageIdCurrent++;
        break;

      case EFI_HII_IIBT_DUPLICATE:
        Length = sizeof (EFI_HII_IIBT_DUPLICATE_BLOCK);
        ImageIdCurrent++;
        break;

      case EFI_HII_IIBT_IMAGE_JPEG:
        Length = OFFSET_OF (EFI_HII_IIBT_JPEG_BLOCK, Data) + ReadUnaligned32 ((VOID *)&((EFI_HII_IIBT_JPEG_BLOCK *)CurrentImageBlock)->Size);
        ImageIdCurrent++;
        break;

      case EFI_HII_IIBT_IMAGE_PNG:
        Length = OFFSET_OF (EFI_HII_IIBT_PNG_BLOCK, Data) + ReadUnaligned32 ((VOID *)&((EFI_HII_IIBT_PNG_BLOCK *)CurrentImageBlock)->Size);
        ImageIdCurrent++;
        break;

      case EFI_HII_IIBT_SKIP1:
        Length          = sizeof (EFI_HII_IIBT_SKIP1_BLOCK);
        ImageIdCurrent += ((EFI_HII_IIBT_SKIP1_BLOCK *)CurrentImageBlock)->SkipCount;
        break;

      case EFI_HII_IIBT_SKIP2:
        Length          = sizeof (EFI_HII_IIBT_SKIP2_BLOCK);
        ImageIdCurrent += ReadUnaligned16 ((VOID *)&((EFI_HII_IIBT_SKIP2_BLOCK *)CurrentImageBlock)->SkipCount);
        break;

      default:
        //
        // Unknown image blocks can not be skipped, processing halts.
        //
        ASSERT (FALSE);
        Length = 0;
        break;
    }

    CurrentImageBlock = (EFI_HII_IMAGE_BLOCK *)((UINT8 *)CurrentImageBlock + Length);
  }

  //
  // When ImageId is zero, return the imageid of last image block: EFI_HII_IIBT_END_BLOCK.
  //
  if (*ImageId == 0) {
    *ImageId = ImageIdCurrent;
    return CurrentImageBlock;
  }

  return NULL;
}

/**
  Convert pixels from EFI_GRAPHICS_OUTPUT_BLT_PIXEL to EFI_HII_RGB_PIXEL style.

  This is a internal function.


  @param  BitMapOut              Pixels in EFI_HII_RGB_PIXEL format.
  @param  BitMapIn               Pixels in EFI_GRAPHICS_OUTPUT_BLT_PIXEL format.
  @param  PixelNum               The number of pixels to be converted.


**/
VOID
CopyGopToRgbPixel (
  OUT EFI_HII_RGB_PIXEL              *BitMapOut,
  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BitMapIn,
  IN  UINTN                          PixelNum
  )
{
  UINTN  Index;

  ASSERT (BitMapOut != NULL && BitMapIn != NULL);

  for (Index = 0; Index < PixelNum; Index++) {
    CopyMem (BitMapOut + Index, BitMapIn + Index, sizeof (EFI_HII_RGB_PIXEL));
  }
}

/**
  Convert pixels from EFI_HII_RGB_PIXEL to EFI_GRAPHICS_OUTPUT_BLT_PIXEL style.

  This is a internal function.


  @param  BitMapOut              Pixels in EFI_GRAPHICS_OUTPUT_BLT_PIXEL format.
  @param  BitMapIn               Pixels in EFI_HII_RGB_PIXEL format.
  @param  PixelNum               The number of pixels to be converted.


**/
VOID
CopyRgbToGopPixel (
  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BitMapOut,
  IN  EFI_HII_RGB_PIXEL              *BitMapIn,
  IN  UINTN                          PixelNum
  )
{
  UINTN  Index;

  ASSERT (BitMapOut != NULL && BitMapIn != NULL);

  for (Index = 0; Index < PixelNum; Index++) {
    CopyMem (BitMapOut + Index, BitMapIn + Index, sizeof (EFI_HII_RGB_PIXEL));
  }
}

/**
  Output pixels in "1 bit per pixel" format to an image.

  This is a internal function.


  @param  Image                  Points to the image which will store the pixels.
  @param  Data                   Stores the value of output pixels, 0 or 1.
  @param  PaletteInfo            PaletteInfo which stores the color of the output
                                 pixels. First entry corresponds to color 0 and
                                 second one to color 1.


**/
VOID
Output1bitPixel (
  IN OUT EFI_IMAGE_INPUT         *Image,
  IN UINT8                       *Data,
  IN EFI_HII_IMAGE_PALETTE_INFO  *PaletteInfo
  )
{
  UINT16                         Xpos;
  UINT16                         Ypos;
  UINTN                          OffsetY;
  UINT8                          Index;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BitMapPtr;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  PaletteValue[2];
  EFI_HII_IMAGE_PALETTE_INFO     *Palette;
  UINTN                          PaletteSize;
  UINT8                          Byte;

  ASSERT (Image != NULL && Data != NULL && PaletteInfo != NULL);

  BitMapPtr = Image->Bitmap;

  //
  // First entry corresponds to color 0 and second entry corresponds to color 1.
  //
  PaletteSize = 0;
  CopyMem (&PaletteSize, PaletteInfo, sizeof (UINT16));
  PaletteSize += sizeof (UINT16);
  Palette      = AllocateZeroPool (PaletteSize);
  ASSERT (Palette != NULL);
  if (Palette == NULL) {
    return;
  }

  CopyMem (Palette, PaletteInfo, PaletteSize);

  ZeroMem (PaletteValue, sizeof (PaletteValue));
  CopyRgbToGopPixel (&PaletteValue[0], &Palette->PaletteValue[0], 1);
  CopyRgbToGopPixel (&PaletteValue[1], &Palette->PaletteValue[1], 1);
  FreePool (Palette);

  //
  // Convert the pixel from one bit to corresponding color.
  //
  for (Ypos = 0; Ypos < Image->Height; Ypos++) {
    OffsetY = BITMAP_LEN_1_BIT (Image->Width, Ypos);
    //
    // All bits in these bytes are meaningful
    //
    for (Xpos = 0; Xpos < Image->Width / 8; Xpos++) {
      Byte = *(Data + OffsetY + Xpos);
      for (Index = 0; Index < 8; Index++) {
        if ((Byte & (1 << Index)) != 0) {
          BitMapPtr[Ypos * Image->Width + Xpos * 8 + (8 - Index - 1)] = PaletteValue[1];
        } else {
          BitMapPtr[Ypos * Image->Width + Xpos * 8 + (8 - Index - 1)] = PaletteValue[0];
        }
      }
    }

    if (Image->Width % 8 != 0) {
      //
      // Padding bits in this byte should be ignored.
      //
      Byte = *(Data + OffsetY + Xpos);
      for (Index = 0; Index < Image->Width % 8; Index++) {
        if ((Byte & (1 << (8 - Index - 1))) != 0) {
          BitMapPtr[Ypos * Image->Width + Xpos * 8 + Index] = PaletteValue[1];
        } else {
          BitMapPtr[Ypos * Image->Width + Xpos * 8 + Index] = PaletteValue[0];
        }
      }
    }
  }
}

/**
  Output pixels in "4 bit per pixel" format to an image.

  This is a internal function.


  @param  Image                  Points to the image which will store the pixels.
  @param  Data                   Stores the value of output pixels, 0 ~ 15.
  @param[in]  PaletteInfo            PaletteInfo which stores the color of the output
                                 pixels. Each entry corresponds to a color within
                                 [0, 15].


**/
VOID
Output4bitPixel (
  IN OUT EFI_IMAGE_INPUT         *Image,
  IN UINT8                       *Data,
  IN EFI_HII_IMAGE_PALETTE_INFO  *PaletteInfo
  )
{
  UINT16                         Xpos;
  UINT16                         Ypos;
  UINTN                          OffsetY;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BitMapPtr;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  PaletteValue[16];
  EFI_HII_IMAGE_PALETTE_INFO     *Palette;
  UINTN                          PaletteSize;
  UINT16                         PaletteNum;
  UINT8                          Byte;

  ASSERT (Image != NULL && Data != NULL && PaletteInfo != NULL);

  BitMapPtr = Image->Bitmap;

  //
  // The bitmap should allocate each color index starting from 0.
  //
  PaletteSize = 0;
  CopyMem (&PaletteSize, PaletteInfo, sizeof (UINT16));
  PaletteSize += sizeof (UINT16);
  Palette      = AllocateZeroPool (PaletteSize);
  ASSERT (Palette != NULL);
  if (Palette == NULL) {
    return;
  }

  CopyMem (Palette, PaletteInfo, PaletteSize);
  PaletteNum = (UINT16)(Palette->PaletteSize / sizeof (EFI_HII_RGB_PIXEL));

  ZeroMem (PaletteValue, sizeof (PaletteValue));
  CopyRgbToGopPixel (PaletteValue, Palette->PaletteValue, MIN (PaletteNum, ARRAY_SIZE (PaletteValue)));
  FreePool (Palette);

  //
  // Convert the pixel from 4 bit to corresponding color.
  //
  for (Ypos = 0; Ypos < Image->Height; Ypos++) {
    OffsetY = BITMAP_LEN_4_BIT (Image->Width, Ypos);
    //
    // All bits in these bytes are meaningful
    //
    for (Xpos = 0; Xpos < Image->Width / 2; Xpos++) {
      Byte                                          = *(Data + OffsetY + Xpos);
      BitMapPtr[Ypos * Image->Width + Xpos * 2]     = PaletteValue[Byte >> 4];
      BitMapPtr[Ypos * Image->Width + Xpos * 2 + 1] = PaletteValue[Byte & 0x0F];
    }

    if (Image->Width % 2 != 0) {
      //
      // Padding bits in this byte should be ignored.
      //
      Byte                                      = *(Data + OffsetY + Xpos);
      BitMapPtr[Ypos * Image->Width + Xpos * 2] = PaletteValue[Byte >> 4];
    }
  }
}

/**
  Output pixels in "8 bit per pixel" format to an image.

  This is a internal function.


  @param  Image                  Points to the image which will store the pixels.
  @param  Data                   Stores the value of output pixels, 0 ~ 255.
  @param[in]  PaletteInfo        PaletteInfo which stores the color of the output
                                 pixels. Each entry corresponds to a color within
                                 [0, 255].


**/
VOID
Output8bitPixel (
  IN OUT EFI_IMAGE_INPUT         *Image,
  IN UINT8                       *Data,
  IN EFI_HII_IMAGE_PALETTE_INFO  *PaletteInfo
  )
{
  UINT16                         Xpos;
  UINT16                         Ypos;
  UINTN                          OffsetY;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BitMapPtr;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  PaletteValue[256];
  EFI_HII_IMAGE_PALETTE_INFO     *Palette;
  UINTN                          PaletteSize;
  UINT16                         PaletteNum;
  UINT8                          Byte;

  ASSERT (Image != NULL && Data != NULL && PaletteInfo != NULL);

  BitMapPtr = Image->Bitmap;

  //
  // The bitmap should allocate each color index starting from 0.
  //
  PaletteSize = 0;
  CopyMem (&PaletteSize, PaletteInfo, sizeof (UINT16));
  PaletteSize += sizeof (UINT16);
  Palette      = AllocateZeroPool (PaletteSize);
  ASSERT (Palette != NULL);
  if (Palette == NULL) {
    return;
  }

  CopyMem (Palette, PaletteInfo, PaletteSize);
  PaletteNum = (UINT16)(Palette->PaletteSize / sizeof (EFI_HII_RGB_PIXEL));
  ZeroMem (PaletteValue, sizeof (PaletteValue));
  CopyRgbToGopPixel (PaletteValue, Palette->PaletteValue, MIN (PaletteNum, ARRAY_SIZE (PaletteValue)));
  FreePool (Palette);

  //
  // Convert the pixel from 8 bits to corresponding color.
  //
  for (Ypos = 0; Ypos < Image->Height; Ypos++) {
    OffsetY = BITMAP_LEN_8_BIT ((UINT32)Image->Width, Ypos);
    //
    // All bits are meaningful since the bitmap is 8 bits per pixel.
    //
    for (Xpos = 0; Xpos < Image->Width; Xpos++) {
      Byte                      = *(Data + OffsetY + Xpos);
      BitMapPtr[OffsetY + Xpos] = PaletteValue[Byte];
    }
  }
}

/**
  Output pixels in "24 bit per pixel" format to an image.

  This is a internal function.


  @param  Image                  Points to the image which will store the pixels.
  @param  Data                   Stores the color of output pixels, allowing 16.8
                                 millions colors.


**/
VOID
Output24bitPixel (
  IN OUT EFI_IMAGE_INPUT  *Image,
  IN EFI_HII_RGB_PIXEL    *Data
  )
{
  UINT16                         Ypos;
  UINTN                          OffsetY;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BitMapPtr;

  ASSERT (Image != NULL && Data != NULL);

  BitMapPtr = Image->Bitmap;

  for (Ypos = 0; Ypos < Image->Height; Ypos++) {
    OffsetY = BITMAP_LEN_8_BIT ((UINT32)Image->Width, Ypos);
    CopyRgbToGopPixel (&BitMapPtr[OffsetY], &Data[OffsetY], Image->Width);
  }
}

/**
  Convert the image from EFI_IMAGE_INPUT to EFI_IMAGE_OUTPUT format.

  This is a internal function.


  @param  BltBuffer              Buffer points to bitmap data of incoming image.
  @param  BltX                   Specifies the offset from the left and top edge of
                                  the output image of the first pixel in the image.
  @param  BltY                   Specifies the offset from the left and top edge of
                                  the output image of the first pixel in the image.
  @param  Width                  Width of the incoming image, in pixels.
  @param  Height                 Height of the incoming image, in pixels.
  @param  Transparent            If TRUE, all "off" pixels in the image will be
                                 drawn using the pixel value from blt and all other
                                 pixels will be copied.
  @param  Blt                    Buffer points to bitmap data of output image.

  @retval EFI_SUCCESS            The image was successfully converted.
  @retval EFI_INVALID_PARAMETER  Any incoming parameter is invalid.

**/
EFI_STATUS
ImageToBlt (
  IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BltBuffer,
  IN UINTN                          BltX,
  IN UINTN                          BltY,
  IN UINTN                          Width,
  IN UINTN                          Height,
  IN BOOLEAN                        Transparent,
  IN OUT EFI_IMAGE_OUTPUT           **Blt
  )
{
  EFI_IMAGE_OUTPUT               *ImageOut;
  UINTN                          Xpos;
  UINTN                          Ypos;
  UINTN                          OffsetY1;     // src buffer
  UINTN                          OffsetY2;     // dest buffer
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  SrcPixel;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  ZeroPixel;

  if ((BltBuffer == NULL) || (Blt == NULL) || (*Blt == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  ImageOut = *Blt;

  if (Width + BltX > ImageOut->Width) {
    return EFI_INVALID_PARAMETER;
  }

  if (Height + BltY > ImageOut->Height) {
    return EFI_INVALID_PARAMETER;
  }

  ZeroMem (&ZeroPixel, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));

  for (Ypos = 0; Ypos < Height; Ypos++) {
    OffsetY1 = Width * Ypos;
    OffsetY2 = ImageOut->Width * (BltY + Ypos);
    for (Xpos = 0; Xpos < Width; Xpos++) {
      SrcPixel = BltBuffer[OffsetY1 + Xpos];
      if (Transparent) {
        if (CompareMem (&SrcPixel, &ZeroPixel, 3) != 0) {
          ImageOut->Image.Bitmap[OffsetY2 + BltX + Xpos] = SrcPixel;
        }
      } else {
        ImageOut->Image.Bitmap[OffsetY2 + BltX + Xpos] = SrcPixel;
      }
    }
  }

  return EFI_SUCCESS;
}

/**
  Return the HII package list identified by PackageList HII handle.

  @param Database    Pointer to HII database list header.
  @param PackageList HII handle of the package list to locate.

  @retval The HII package list instance.
**/
HII_DATABASE_PACKAGE_LIST_INSTANCE *
LocatePackageList (
  IN  LIST_ENTRY      *Database,
  IN  EFI_HII_HANDLE  PackageList
  )
{
  LIST_ENTRY           *Link;
  HII_DATABASE_RECORD  *Record;

  //
  // Get the specified package list and image package.
  //
  for (Link = GetFirstNode (Database);
       !IsNull (Database, Link);
       Link = GetNextNode (Database, Link)
       )
  {
    Record = CR (Link, HII_DATABASE_RECORD, DatabaseEntry, HII_DATABASE_RECORD_SIGNATURE);
    if (Record->Handle == PackageList) {
      return Record->PackageList;
    }
  }

  return NULL;
}

/**
  This function adds the image Image to the group of images owned by PackageList, and returns
  a new image identifier (ImageId).

  @param  This                   A pointer to the EFI_HII_IMAGE_PROTOCOL instance.
  @param  PackageList            Handle of the package list where this image will
                                 be added.
  @param  ImageId                On return, contains the new image id, which is
                                 unique within PackageList.
  @param  Image                  Points to the image.

  @retval EFI_SUCCESS            The new image was added successfully.
  @retval EFI_NOT_FOUND          The specified PackageList could not be found in
                                 database.
  @retval EFI_OUT_OF_RESOURCES   Could not add the image due to lack of resources.
  @retval EFI_INVALID_PARAMETER  Image is NULL or ImageId is NULL.

**/
EFI_STATUS
EFIAPI
HiiNewImage (
  IN  CONST EFI_HII_IMAGE_PROTOCOL  *This,
  IN  EFI_HII_HANDLE                PackageList,
  OUT EFI_IMAGE_ID                  *ImageId,
  IN  CONST EFI_IMAGE_INPUT         *Image
  )
{
  HII_DATABASE_PRIVATE_DATA           *Private;
  HII_DATABASE_PACKAGE_LIST_INSTANCE  *PackageListNode;
  HII_IMAGE_PACKAGE_INSTANCE          *ImagePackage;
  EFI_HII_IMAGE_BLOCK                 *ImageBlocks;
  UINT32                              NewBlockSize;

  if ((This == NULL) || (ImageId == NULL) || (Image == NULL) || (Image->Bitmap == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  Private         = HII_IMAGE_DATABASE_PRIVATE_DATA_FROM_THIS (This);
  PackageListNode = LocatePackageList (&Private->DatabaseList, PackageList);
  if (PackageListNode == NULL) {
    return EFI_NOT_FOUND;
  }

  EfiAcquireLock (&mHiiDatabaseLock);

  //
  // Calcuate the size of new image.
  // Make sure the size doesn't overflow UINT32.
  // Note: 24Bit BMP occpuies 3 bytes per pixel.
  //
  NewBlockSize = (UINT32)Image->Width * Image->Height;
  if (NewBlockSize > (MAX_UINT32 - (sizeof (EFI_HII_IIBT_IMAGE_24BIT_BLOCK) - sizeof (EFI_HII_RGB_PIXEL))) / 3) {
    EfiReleaseLock (&mHiiDatabaseLock);
    return EFI_OUT_OF_RESOURCES;
  }

  NewBlockSize = NewBlockSize * 3 + (sizeof (EFI_HII_IIBT_IMAGE_24BIT_BLOCK) - sizeof (EFI_HII_RGB_PIXEL));

  //
  // Get the image package in the package list,
  // or create a new image package if image package does not exist.
  //
  if (PackageListNode->ImagePkg != NULL) {
    ImagePackage = PackageListNode->ImagePkg;

    //
    // Output the image id of the incoming image being inserted, which is the
    // image id of the EFI_HII_IIBT_END block of old image package.
    //
    *ImageId = 0;
    GetImageIdOrAddress (ImagePackage->ImageBlock, ImageId);

    //
    // Update the package's image block by appending the new block to the end.
    //

    //
    // Make sure the final package length doesn't overflow.
    // Length of the package header is represented using 24 bits. So MAX length is MAX_UINT24.
    //
    if (NewBlockSize > MAX_UINT24 - ImagePackage->ImagePkgHdr.Header.Length) {
      EfiReleaseLock (&mHiiDatabaseLock);
      return EFI_OUT_OF_RESOURCES;
    }

    //
    // Because ImagePackage->ImageBlockSize < ImagePackage->ImagePkgHdr.Header.Length,
    // So (ImagePackage->ImageBlockSize + NewBlockSize) <= MAX_UINT24
    //
    ImageBlocks = AllocatePool (ImagePackage->ImageBlockSize + NewBlockSize);
    if (ImageBlocks == NULL) {
      EfiReleaseLock (&mHiiDatabaseLock);
      return EFI_OUT_OF_RESOURCES;
    }

    //
    // Copy the original content.
    //
    CopyMem (
      ImageBlocks,
      ImagePackage->ImageBlock,
      ImagePackage->ImageBlockSize - sizeof (EFI_HII_IIBT_END_BLOCK)
      );
    FreePool (ImagePackage->ImageBlock);
    ImagePackage->ImageBlock = ImageBlocks;

    //
    // Point to the very last block.
    //
    ImageBlocks = (EFI_HII_IMAGE_BLOCK *)(
                                          (UINT8 *)ImageBlocks + ImagePackage->ImageBlockSize - sizeof (EFI_HII_IIBT_END_BLOCK)
                                          );
    //
    // Update the length record.
    //
    ImagePackage->ImageBlockSize                  += NewBlockSize;
    ImagePackage->ImagePkgHdr.Header.Length       += NewBlockSize;
    PackageListNode->PackageListHdr.PackageLength += NewBlockSize;
  } else {
    //
    // Make sure the final package length doesn't overflow.
    // Length of the package header is represented using 24 bits. So MAX length is MAX_UINT24.
    //
    if (NewBlockSize > MAX_UINT24 - (sizeof (EFI_HII_IMAGE_PACKAGE_HDR) + sizeof (EFI_HII_IIBT_END_BLOCK))) {
      EfiReleaseLock (&mHiiDatabaseLock);
      return EFI_OUT_OF_RESOURCES;
    }

    //
    // The specified package list does not contain image package.
    // Create one to add this image block.
    //
    ImagePackage = (HII_IMAGE_PACKAGE_INSTANCE *)AllocateZeroPool (sizeof (HII_IMAGE_PACKAGE_INSTANCE));
    if (ImagePackage == NULL) {
      EfiReleaseLock (&mHiiDatabaseLock);
      return EFI_OUT_OF_RESOURCES;
    }

    //
    // Output the image id of the incoming image being inserted, which is the
    // first image block so that id is initially to one.
    //
    *ImageId = 1;
    //
    // Fill in image package header.
    //
    ImagePackage->ImagePkgHdr.Header.Length     = sizeof (EFI_HII_IMAGE_PACKAGE_HDR) + NewBlockSize + sizeof (EFI_HII_IIBT_END_BLOCK);
    ImagePackage->ImagePkgHdr.Header.Type       = EFI_HII_PACKAGE_IMAGES;
    ImagePackage->ImagePkgHdr.ImageInfoOffset   = sizeof (EFI_HII_IMAGE_PACKAGE_HDR);
    ImagePackage->ImagePkgHdr.PaletteInfoOffset = 0;

    //
    // Fill in palette info.
    //
    ImagePackage->PaletteBlock    = NULL;
    ImagePackage->PaletteInfoSize = 0;

    //
    // Fill in image blocks.
    //
    ImagePackage->ImageBlockSize = NewBlockSize + sizeof (EFI_HII_IIBT_END_BLOCK);
    ImagePackage->ImageBlock     = AllocateZeroPool (NewBlockSize + sizeof (EFI_HII_IIBT_END_BLOCK));
    if (ImagePackage->ImageBlock == NULL) {
      FreePool (ImagePackage);
      EfiReleaseLock (&mHiiDatabaseLock);
      return EFI_OUT_OF_RESOURCES;
    }

    ImageBlocks = ImagePackage->ImageBlock;

    //
    // Insert this image package.
    //
    PackageListNode->ImagePkg                      = ImagePackage;
    PackageListNode->PackageListHdr.PackageLength += ImagePackage->ImagePkgHdr.Header.Length;
  }

  //
  // Append the new block here
  //
  if (Image->Flags == EFI_IMAGE_TRANSPARENT) {
    ImageBlocks->BlockType = EFI_HII_IIBT_IMAGE_24BIT_TRANS;
  } else {
    ImageBlocks->BlockType = EFI_HII_IIBT_IMAGE_24BIT;
  }

  WriteUnaligned16 ((VOID *)&((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)ImageBlocks)->Bitmap.Width, Image->Width);
  WriteUnaligned16 ((VOID *)&((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)ImageBlocks)->Bitmap.Height, Image->Height);
  CopyGopToRgbPixel (((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)ImageBlocks)->Bitmap.Bitmap, Image->Bitmap, (UINT32)Image->Width * Image->Height);

  //
  // Append the block end
  //
  ImageBlocks            = (EFI_HII_IMAGE_BLOCK *)((UINT8 *)ImageBlocks + NewBlockSize);
  ImageBlocks->BlockType = EFI_HII_IIBT_END;

  //
  // Check whether need to get the contents of HiiDataBase.
  // Only after ReadyToBoot to do the export.
  //
  if (gExportAfterReadyToBoot) {
    HiiGetDatabaseInfo (&Private->HiiDatabase);
  }

  EfiReleaseLock (&mHiiDatabaseLock);

  return EFI_SUCCESS;
}

/**
  This function retrieves the image specified by ImageId which is associated with
  the specified PackageList and copies it into the buffer specified by Image.

  @param  Database               A pointer to the database list header.
  @param  PackageList            Handle of the package list where this image will
                                 be searched.
  @param  ImageId                The image's id,, which is unique within
                                 PackageList.
  @param  Image                  Points to the image.
  @param  BitmapOnly             TRUE to only return the bitmap type image.
                                 FALSE to locate image decoder instance to decode image.

  @retval EFI_SUCCESS            The new image was returned successfully.
  @retval EFI_NOT_FOUND          The image specified by ImageId is not in the
                                 database. The specified PackageList is not in the database.
  @retval EFI_BUFFER_TOO_SMALL   The buffer specified by ImageSize is too small to
                                 hold the image.
  @retval EFI_INVALID_PARAMETER  The Image or ImageSize was NULL.
  @retval EFI_OUT_OF_RESOURCES   The bitmap could not be retrieved because there was not
                                 enough memory.
**/
EFI_STATUS
IGetImage (
  IN  LIST_ENTRY       *Database,
  IN  EFI_HII_HANDLE   PackageList,
  IN  EFI_IMAGE_ID     ImageId,
  OUT EFI_IMAGE_INPUT  *Image,
  IN  BOOLEAN          BitmapOnly
  )
{
  EFI_STATUS                          Status;
  HII_DATABASE_PACKAGE_LIST_INSTANCE  *PackageListNode;
  HII_IMAGE_PACKAGE_INSTANCE          *ImagePackage;
  EFI_HII_IMAGE_BLOCK                 *CurrentImageBlock;
  EFI_HII_IIBT_IMAGE_1BIT_BLOCK       Iibt1bit;
  UINT16                              Width;
  UINT16                              Height;
  UINTN                               ImageLength;
  UINT8                               *PaletteInfo;
  UINT8                               PaletteIndex;
  UINT16                              PaletteSize;
  EFI_HII_IMAGE_DECODER_PROTOCOL      *Decoder;
  EFI_IMAGE_OUTPUT                    *ImageOut;

  if ((Image == NULL) || (ImageId == 0)) {
    return EFI_INVALID_PARAMETER;
  }

  PackageListNode = LocatePackageList (Database, PackageList);
  if (PackageListNode == NULL) {
    return EFI_NOT_FOUND;
  }

  ImagePackage = PackageListNode->ImagePkg;
  if (ImagePackage == NULL) {
    return EFI_NOT_FOUND;
  }

  //
  // Find the image block specified by ImageId
  //
  CurrentImageBlock = GetImageIdOrAddress (ImagePackage->ImageBlock, &ImageId);
  if (CurrentImageBlock == NULL) {
    return EFI_NOT_FOUND;
  }

  Image->Flags = 0;
  switch (CurrentImageBlock->BlockType) {
    case EFI_HII_IIBT_IMAGE_JPEG:
    case EFI_HII_IIBT_IMAGE_PNG:
      if (BitmapOnly) {
        return EFI_UNSUPPORTED;
      }

      ImageOut = NULL;
      Decoder  = LocateHiiImageDecoder (CurrentImageBlock->BlockType);
      if (Decoder == NULL) {
        return EFI_UNSUPPORTED;
      }

      //
      // Use the common block code since the definition of two structures is the same.
      //
      ASSERT (OFFSET_OF (EFI_HII_IIBT_JPEG_BLOCK, Data) == OFFSET_OF (EFI_HII_IIBT_PNG_BLOCK, Data));
      ASSERT (
        sizeof (((EFI_HII_IIBT_JPEG_BLOCK *)CurrentImageBlock)->Data) ==
        sizeof (((EFI_HII_IIBT_PNG_BLOCK *)CurrentImageBlock)->Data)
        );
      ASSERT (OFFSET_OF (EFI_HII_IIBT_JPEG_BLOCK, Size) == OFFSET_OF (EFI_HII_IIBT_PNG_BLOCK, Size));
      ASSERT (
        sizeof (((EFI_HII_IIBT_JPEG_BLOCK *)CurrentImageBlock)->Size) ==
        sizeof (((EFI_HII_IIBT_PNG_BLOCK *)CurrentImageBlock)->Size)
        );
      Status = Decoder->DecodeImage (
                          Decoder,
                          ((EFI_HII_IIBT_JPEG_BLOCK *)CurrentImageBlock)->Data,
                          ((EFI_HII_IIBT_JPEG_BLOCK *)CurrentImageBlock)->Size,
                          &ImageOut,
                          FALSE
                          );

      //
      // Spec requires to use the first capable image decoder instance.
      // The first image decoder instance may fail to decode the image.
      //
      if (!EFI_ERROR (Status)) {
        Image->Bitmap = ImageOut->Image.Bitmap;
        Image->Height = ImageOut->Height;
        Image->Width  = ImageOut->Width;
        FreePool (ImageOut);
      }

      return Status;

    case EFI_HII_IIBT_IMAGE_1BIT_TRANS:
    case EFI_HII_IIBT_IMAGE_4BIT_TRANS:
    case EFI_HII_IIBT_IMAGE_8BIT_TRANS:
      Image->Flags = EFI_IMAGE_TRANSPARENT;
    //
    // fall through
    //
    case EFI_HII_IIBT_IMAGE_1BIT:
    case EFI_HII_IIBT_IMAGE_4BIT:
    case EFI_HII_IIBT_IMAGE_8BIT:
      //
      // Use the common block code since the definition of these structures is the same.
      //
      CopyMem (&Iibt1bit, CurrentImageBlock, sizeof (EFI_HII_IIBT_IMAGE_1BIT_BLOCK));
      ImageLength = (UINTN)Iibt1bit.Bitmap.Width * Iibt1bit.Bitmap.Height;
      if (ImageLength > MAX_UINTN / sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) {
        return EFI_OUT_OF_RESOURCES;
      }

      ImageLength  *= sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
      Image->Bitmap = AllocateZeroPool (ImageLength);
      if (Image->Bitmap == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }

      Image->Width  = Iibt1bit.Bitmap.Width;
      Image->Height = Iibt1bit.Bitmap.Height;

      PaletteInfo = ImagePackage->PaletteBlock + sizeof (EFI_HII_IMAGE_PALETTE_INFO_HEADER);
      for (PaletteIndex = 1; PaletteIndex < Iibt1bit.PaletteIndex; PaletteIndex++) {
        CopyMem (&PaletteSize, PaletteInfo, sizeof (UINT16));
        PaletteInfo += PaletteSize + sizeof (UINT16);
      }

      ASSERT (PaletteIndex == Iibt1bit.PaletteIndex);

      //
      // Output bitmap data
      //
      if ((CurrentImageBlock->BlockType == EFI_HII_IIBT_IMAGE_1BIT) ||
          (CurrentImageBlock->BlockType == EFI_HII_IIBT_IMAGE_1BIT_TRANS))
      {
        Output1bitPixel (
          Image,
          ((EFI_HII_IIBT_IMAGE_1BIT_BLOCK *)CurrentImageBlock)->Bitmap.Data,
          (EFI_HII_IMAGE_PALETTE_INFO *)PaletteInfo
          );
      } else if ((CurrentImageBlock->BlockType == EFI_HII_IIBT_IMAGE_4BIT) ||
                 (CurrentImageBlock->BlockType == EFI_HII_IIBT_IMAGE_4BIT_TRANS))
      {
        Output4bitPixel (
          Image,
          ((EFI_HII_IIBT_IMAGE_4BIT_BLOCK *)CurrentImageBlock)->Bitmap.Data,
          (EFI_HII_IMAGE_PALETTE_INFO *)PaletteInfo
          );
      } else {
        Output8bitPixel (
          Image,
          ((EFI_HII_IIBT_IMAGE_8BIT_BLOCK *)CurrentImageBlock)->Bitmap.Data,
          (EFI_HII_IMAGE_PALETTE_INFO *)PaletteInfo
          );
      }

      return EFI_SUCCESS;

    case EFI_HII_IIBT_IMAGE_24BIT_TRANS:
      Image->Flags = EFI_IMAGE_TRANSPARENT;
    //
    // fall through
    //
    case EFI_HII_IIBT_IMAGE_24BIT:
      Width       = ReadUnaligned16 ((VOID *)&((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)CurrentImageBlock)->Bitmap.Width);
      Height      = ReadUnaligned16 ((VOID *)&((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)CurrentImageBlock)->Bitmap.Height);
      ImageLength = (UINTN)Width * Height;
      if (ImageLength > MAX_UINTN / sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) {
        return EFI_OUT_OF_RESOURCES;
      }

      ImageLength  *= sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
      Image->Bitmap = AllocateZeroPool (ImageLength);
      if (Image->Bitmap == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }

      Image->Width  = Width;
      Image->Height = Height;

      //
      // Output the bitmap data directly.
      //
      Output24bitPixel (
        Image,
        ((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)CurrentImageBlock)->Bitmap.Bitmap
        );
      return EFI_SUCCESS;

    default:
      return EFI_NOT_FOUND;
  }
}

/**
  This function retrieves the image specified by ImageId which is associated with
  the specified PackageList and copies it into the buffer specified by Image.

  @param  This                   A pointer to the EFI_HII_IMAGE_PROTOCOL instance.
  @param  PackageList            Handle of the package list where this image will
                                 be searched.
  @param  ImageId                The image's id,, which is unique within
                                 PackageList.
  @param  Image                  Points to the image.

  @retval EFI_SUCCESS            The new image was returned successfully.
  @retval EFI_NOT_FOUND           The image specified by ImageId is not in the
                                                database. The specified PackageList is not in the database.
  @retval EFI_BUFFER_TOO_SMALL   The buffer specified by ImageSize is too small to
                                 hold the image.
  @retval EFI_INVALID_PARAMETER  The Image or ImageSize was NULL.
  @retval EFI_OUT_OF_RESOURCES   The bitmap could not be retrieved because there was not
                                 enough memory.

**/
EFI_STATUS
EFIAPI
HiiGetImage (
  IN  CONST EFI_HII_IMAGE_PROTOCOL  *This,
  IN  EFI_HII_HANDLE                PackageList,
  IN  EFI_IMAGE_ID                  ImageId,
  OUT EFI_IMAGE_INPUT               *Image
  )
{
  HII_DATABASE_PRIVATE_DATA  *Private;

  Private = HII_IMAGE_DATABASE_PRIVATE_DATA_FROM_THIS (This);
  return IGetImage (&Private->DatabaseList, PackageList, ImageId, Image, TRUE);
}

/**
  This function updates the image specified by ImageId in the specified PackageListHandle to
  the image specified by Image.

  @param  This                   A pointer to the EFI_HII_IMAGE_PROTOCOL instance.
  @param  PackageList            The package list containing the images.
  @param  ImageId                The image's id,, which is unique within
                                 PackageList.
  @param  Image                  Points to the image.

  @retval EFI_SUCCESS            The new image was updated successfully.
  @retval EFI_NOT_FOUND          The image specified by ImageId is not in the
                                                database. The specified PackageList is not in the database.
  @retval EFI_INVALID_PARAMETER  The Image was NULL.

**/
EFI_STATUS
EFIAPI
HiiSetImage (
  IN CONST EFI_HII_IMAGE_PROTOCOL  *This,
  IN EFI_HII_HANDLE                PackageList,
  IN EFI_IMAGE_ID                  ImageId,
  IN CONST EFI_IMAGE_INPUT         *Image
  )
{
  HII_DATABASE_PRIVATE_DATA           *Private;
  HII_DATABASE_PACKAGE_LIST_INSTANCE  *PackageListNode;
  HII_IMAGE_PACKAGE_INSTANCE          *ImagePackage;
  EFI_HII_IMAGE_BLOCK                 *CurrentImageBlock;
  EFI_HII_IMAGE_BLOCK                 *ImageBlocks;
  EFI_HII_IMAGE_BLOCK                 *NewImageBlock;
  UINT32                              NewBlockSize;
  UINT32                              OldBlockSize;
  UINT32                              Part1Size;
  UINT32                              Part2Size;

  if ((This == NULL) || (Image == NULL) || (ImageId == 0) || (Image->Bitmap == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  Private         = HII_IMAGE_DATABASE_PRIVATE_DATA_FROM_THIS (This);
  PackageListNode = LocatePackageList (&Private->DatabaseList, PackageList);
  if (PackageListNode == NULL) {
    return EFI_NOT_FOUND;
  }

  ImagePackage = PackageListNode->ImagePkg;
  if (ImagePackage == NULL) {
    return EFI_NOT_FOUND;
  }

  //
  // Find the image block specified by ImageId
  //
  CurrentImageBlock = GetImageIdOrAddress (ImagePackage->ImageBlock, &ImageId);
  if (CurrentImageBlock == NULL) {
    return EFI_NOT_FOUND;
  }

  EfiAcquireLock (&mHiiDatabaseLock);

  //
  // Get the size of original image block. Use some common block code here
  // since the definition of some structures is the same.
  //
  switch (CurrentImageBlock->BlockType) {
    case EFI_HII_IIBT_IMAGE_JPEG:
      OldBlockSize = OFFSET_OF (EFI_HII_IIBT_JPEG_BLOCK, Data) + ReadUnaligned32 ((VOID *)&((EFI_HII_IIBT_JPEG_BLOCK *)CurrentImageBlock)->Size);
      break;
    case EFI_HII_IIBT_IMAGE_PNG:
      OldBlockSize = OFFSET_OF (EFI_HII_IIBT_PNG_BLOCK, Data) + ReadUnaligned32 ((VOID *)&((EFI_HII_IIBT_PNG_BLOCK *)CurrentImageBlock)->Size);
      break;
    case EFI_HII_IIBT_IMAGE_1BIT:
    case EFI_HII_IIBT_IMAGE_1BIT_TRANS:
      OldBlockSize = sizeof (EFI_HII_IIBT_IMAGE_1BIT_BLOCK) - sizeof (UINT8) +
                     BITMAP_LEN_1_BIT (
                       ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_1BIT_BLOCK *)CurrentImageBlock)->Bitmap.Width),
                       ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_1BIT_BLOCK *)CurrentImageBlock)->Bitmap.Height)
                       );
      break;
    case EFI_HII_IIBT_IMAGE_4BIT:
    case EFI_HII_IIBT_IMAGE_4BIT_TRANS:
      OldBlockSize = sizeof (EFI_HII_IIBT_IMAGE_4BIT_BLOCK) - sizeof (UINT8) +
                     BITMAP_LEN_4_BIT (
                       ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_4BIT_BLOCK *)CurrentImageBlock)->Bitmap.Width),
                       ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_4BIT_BLOCK *)CurrentImageBlock)->Bitmap.Height)
                       );
      break;
    case EFI_HII_IIBT_IMAGE_8BIT:
    case EFI_HII_IIBT_IMAGE_8BIT_TRANS:
      OldBlockSize = sizeof (EFI_HII_IIBT_IMAGE_8BIT_BLOCK) - sizeof (UINT8) +
                     BITMAP_LEN_8_BIT (
                       (UINT32)ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_8BIT_BLOCK *)CurrentImageBlock)->Bitmap.Width),
                       ReadUnaligned16 (&((EFI_HII_IIBT_IMAGE_8BIT_BLOCK *)CurrentImageBlock)->Bitmap.Height)
                       );
      break;
    case EFI_HII_IIBT_IMAGE_24BIT:
    case EFI_HII_IIBT_IMAGE_24BIT_TRANS:
      OldBlockSize = sizeof (EFI_HII_IIBT_IMAGE_24BIT_BLOCK) - sizeof (EFI_HII_RGB_PIXEL) +
                     BITMAP_LEN_24_BIT (
                       (UINT32)ReadUnaligned16 ((VOID *)&((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)CurrentImageBlock)->Bitmap.Width),
                       ReadUnaligned16 ((VOID *)&((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)CurrentImageBlock)->Bitmap.Height)
                       );
      break;
    default:
      EfiReleaseLock (&mHiiDatabaseLock);
      return EFI_NOT_FOUND;
  }

  //
  // Create the new image block according to input image.
  //

  //
  // Make sure the final package length doesn't overflow.
  // Length of the package header is represented using 24 bits. So MAX length is MAX_UINT24.
  // 24Bit BMP occpuies 3 bytes per pixel.
  //
  NewBlockSize = (UINT32)Image->Width * Image->Height;
  if (NewBlockSize > (MAX_UINT32 - (sizeof (EFI_HII_IIBT_IMAGE_24BIT_BLOCK) - sizeof (EFI_HII_RGB_PIXEL))) / 3) {
    EfiReleaseLock (&mHiiDatabaseLock);
    return EFI_OUT_OF_RESOURCES;
  }

  NewBlockSize = NewBlockSize * 3 + (sizeof (EFI_HII_IIBT_IMAGE_24BIT_BLOCK) - sizeof (EFI_HII_RGB_PIXEL));
  if ((NewBlockSize > OldBlockSize) &&
      (NewBlockSize - OldBlockSize > MAX_UINT24 - ImagePackage->ImagePkgHdr.Header.Length)
      )
  {
    EfiReleaseLock (&mHiiDatabaseLock);
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Adjust the image package to remove the original block firstly then add the new block.
  //
  ImageBlocks = AllocateZeroPool (ImagePackage->ImageBlockSize + NewBlockSize - OldBlockSize);
  if (ImageBlocks == NULL) {
    EfiReleaseLock (&mHiiDatabaseLock);
    return EFI_OUT_OF_RESOURCES;
  }

  Part1Size = (UINT32)((UINTN)CurrentImageBlock - (UINTN)ImagePackage->ImageBlock);
  Part2Size = ImagePackage->ImageBlockSize - Part1Size - OldBlockSize;
  CopyMem (ImageBlocks, ImagePackage->ImageBlock, Part1Size);

  //
  // Set the new image block
  //
  NewImageBlock = (EFI_HII_IMAGE_BLOCK *)((UINT8 *)ImageBlocks + Part1Size);
  if ((Image->Flags & EFI_IMAGE_TRANSPARENT) == EFI_IMAGE_TRANSPARENT) {
    NewImageBlock->BlockType = EFI_HII_IIBT_IMAGE_24BIT_TRANS;
  } else {
    NewImageBlock->BlockType = EFI_HII_IIBT_IMAGE_24BIT;
  }

  WriteUnaligned16 ((VOID *)&((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)NewImageBlock)->Bitmap.Width, Image->Width);
  WriteUnaligned16 ((VOID *)&((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)NewImageBlock)->Bitmap.Height, Image->Height);
  CopyGopToRgbPixel (
    ((EFI_HII_IIBT_IMAGE_24BIT_BLOCK *)NewImageBlock)->Bitmap.Bitmap,
    Image->Bitmap,
    (UINT32)Image->Width * Image->Height
    );

  CopyMem ((UINT8 *)NewImageBlock + NewBlockSize, (UINT8 *)CurrentImageBlock + OldBlockSize, Part2Size);

  FreePool (ImagePackage->ImageBlock);
  ImagePackage->ImageBlock                       = ImageBlocks;
  ImagePackage->ImageBlockSize                  += NewBlockSize - OldBlockSize;
  ImagePackage->ImagePkgHdr.Header.Length       += NewBlockSize - OldBlockSize;
  PackageListNode->PackageListHdr.PackageLength += NewBlockSize - OldBlockSize;

  //
  // Check whether need to get the contents of HiiDataBase.
  // Only after ReadyToBoot to do the export.
  //
  if (gExportAfterReadyToBoot) {
    HiiGetDatabaseInfo (&Private->HiiDatabase);
  }

  EfiReleaseLock (&mHiiDatabaseLock);
  return EFI_SUCCESS;
}

/**
  This function renders an image to a bitmap or the screen using the specified
  color and options. It draws the image on an existing bitmap, allocates a new
  bitmap or uses the screen. The images can be clipped.

  @param  This                   A pointer to the EFI_HII_IMAGE_PROTOCOL instance.
  @param  Flags                  Describes how the image is to be drawn.
  @param  Image                  Points to the image to be displayed.
  @param  Blt                    If this points to a non-NULL on entry, this points
                                 to the image, which is Width pixels wide and
                                 Height pixels high.  The image will be drawn onto
                                 this image and  EFI_HII_DRAW_FLAG_CLIP is implied.
                                 If this points to a  NULL on entry, then a buffer
                                 will be allocated to hold  the generated image and
                                 the pointer updated on exit. It is the caller's
                                 responsibility to free this buffer.
  @param  BltX                   Specifies the offset from the left and top edge of
                                 the  output image of the first pixel in the image.
  @param  BltY                   Specifies the offset from the left and top edge of
                                 the  output image of the first pixel in the image.

  @retval EFI_SUCCESS            The image was successfully drawn.
  @retval EFI_OUT_OF_RESOURCES   Unable to allocate an output buffer for Blt.
  @retval EFI_INVALID_PARAMETER  The Image or Blt was NULL.
  @retval EFI_INVALID_PARAMETER  Any combination of Flags is invalid.

**/
EFI_STATUS
EFIAPI
HiiDrawImage (
  IN CONST EFI_HII_IMAGE_PROTOCOL  *This,
  IN EFI_HII_DRAW_FLAGS            Flags,
  IN CONST EFI_IMAGE_INPUT         *Image,
  IN OUT EFI_IMAGE_OUTPUT          **Blt,
  IN UINTN                         BltX,
  IN UINTN                         BltY
  )
{
  EFI_STATUS                     Status;
  HII_DATABASE_PRIVATE_DATA      *Private;
  BOOLEAN                        Transparent;
  EFI_IMAGE_OUTPUT               *ImageOut;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BltBuffer;
  UINTN                          BufferLen;
  UINT16                         Width;
  UINT16                         Height;
  UINTN                          Xpos;
  UINTN                          Ypos;
  UINTN                          OffsetY1;
  UINTN                          OffsetY2;
  EFI_FONT_DISPLAY_INFO          *FontInfo;
  UINTN                          Index;

  if ((This == NULL) || (Image == NULL) || (Blt == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if (((Flags & EFI_HII_DRAW_FLAG_CLIP) == EFI_HII_DRAW_FLAG_CLIP) && (*Blt == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if ((Flags & EFI_HII_DRAW_FLAG_TRANSPARENT) == EFI_HII_DRAW_FLAG_TRANSPARENT) {
    return EFI_INVALID_PARAMETER;
  }

  FontInfo = NULL;

  //
  // Check whether the image will be drawn transparently or opaquely.
  //
  Transparent = FALSE;
  if ((Flags & EFI_HII_DRAW_FLAG_TRANSPARENT) == EFI_HII_DRAW_FLAG_FORCE_TRANS) {
    Transparent = TRUE;
  } else if ((Flags & EFI_HII_DRAW_FLAG_TRANSPARENT) == EFI_HII_DRAW_FLAG_FORCE_OPAQUE) {
    Transparent = FALSE;
  } else {
    //
    // Now EFI_HII_DRAW_FLAG_DEFAULT is set, whether image will be drawn depending
    // on the image's transparency setting.
    //
    if ((Image->Flags & EFI_IMAGE_TRANSPARENT) == EFI_IMAGE_TRANSPARENT) {
      Transparent = TRUE;
    }
  }

  //
  // Image cannot be drawn transparently if Blt points to NULL on entry.
  // Currently output to Screen transparently is not supported, either.
  //
  if (Transparent) {
    if (*Blt == NULL) {
      return EFI_INVALID_PARAMETER;
    } else if ((Flags & EFI_HII_DIRECT_TO_SCREEN) == EFI_HII_DIRECT_TO_SCREEN) {
      return EFI_INVALID_PARAMETER;
    }
  }

  Private = HII_IMAGE_DATABASE_PRIVATE_DATA_FROM_THIS (This);

  //
  // When Blt points to a non-NULL on entry, this image will be drawn onto
  // this bitmap or screen pointed by "*Blt" and EFI_HII_DRAW_FLAG_CLIP is implied.
  // Otherwise a new bitmap will be allocated to hold this image.
  //
  if (*Blt != NULL) {
    //
    // Make sure the BltX and BltY is inside the Blt area.
    //
    if ((BltX >= (*Blt)->Width) || (BltY >= (*Blt)->Height)) {
      return EFI_INVALID_PARAMETER;
    }

    //
    // Clip the image by (Width, Height)
    //

    Width  = Image->Width;
    Height = Image->Height;

    if (Width > (*Blt)->Width - (UINT16)BltX) {
      Width = (*Blt)->Width - (UINT16)BltX;
    }

    if (Height > (*Blt)->Height - (UINT16)BltY) {
      Height = (*Blt)->Height - (UINT16)BltY;
    }

    //
    // Prepare the buffer for the temporary image.
    // Make sure the buffer size doesn't overflow UINTN.
    //
    BufferLen = Width * Height;
    if (BufferLen > MAX_UINTN / sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) {
      return EFI_OUT_OF_RESOURCES;
    }

    BufferLen *= sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
    BltBuffer  = AllocateZeroPool (BufferLen);
    if (BltBuffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    if ((Width == Image->Width) && (Height == Image->Height)) {
      CopyMem (BltBuffer, Image->Bitmap, BufferLen);
    } else {
      for (Ypos = 0; Ypos < Height; Ypos++) {
        OffsetY1 = Image->Width * Ypos;
        OffsetY2 = Width * Ypos;
        for (Xpos = 0; Xpos < Width; Xpos++) {
          BltBuffer[OffsetY2 + Xpos] = Image->Bitmap[OffsetY1 + Xpos];
        }
      }
    }

    //
    // Draw the image to existing bitmap or screen depending on flag.
    //
    if ((Flags & EFI_HII_DIRECT_TO_SCREEN) == EFI_HII_DIRECT_TO_SCREEN) {
      //
      // Caller should make sure the current UGA console is grarphic mode.
      //

      //
      // Write the image directly to the output device specified by Screen.
      //
      Status = (*Blt)->Image.Screen->Blt (
                                       (*Blt)->Image.Screen,
                                       BltBuffer,
                                       EfiBltBufferToVideo,
                                       0,
                                       0,
                                       BltX,
                                       BltY,
                                       Width,
                                       Height,
                                       0
                                       );
    } else {
      //
      // Draw the image onto the existing bitmap specified by Bitmap.
      //
      Status = ImageToBlt (
                 BltBuffer,
                 BltX,
                 BltY,
                 Width,
                 Height,
                 Transparent,
                 Blt
                 );
    }

    FreePool (BltBuffer);
    return Status;
  } else {
    //
    // Allocate a new bitmap to hold the incoming image.
    //

    //
    // Make sure the final width and height doesn't overflow UINT16.
    //
    if ((BltX > (UINTN)MAX_UINT16 - Image->Width) || (BltY > (UINTN)MAX_UINT16 - Image->Height)) {
      return EFI_INVALID_PARAMETER;
    }

    Width  = Image->Width  + (UINT16)BltX;
    Height = Image->Height + (UINT16)BltY;

    //
    // Make sure the output image size doesn't overflow UINTN.
    //
    BufferLen = Width * Height;
    if (BufferLen > MAX_UINTN / sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) {
      return EFI_OUT_OF_RESOURCES;
    }

    BufferLen *= sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
    BltBuffer  = AllocateZeroPool (BufferLen);
    if (BltBuffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    ImageOut = (EFI_IMAGE_OUTPUT *)AllocateZeroPool (sizeof (EFI_IMAGE_OUTPUT));
    if (ImageOut == NULL) {
      FreePool (BltBuffer);
      return EFI_OUT_OF_RESOURCES;
    }

    ImageOut->Width        = Width;
    ImageOut->Height       = Height;
    ImageOut->Image.Bitmap = BltBuffer;

    //
    // BUGBUG: Now all the "blank" pixels are filled with system default background
    // color. Not sure if it need to be updated or not.
    //
    Status = GetSystemFont (Private, &FontInfo, NULL);
    if (EFI_ERROR (Status)) {
      FreePool (BltBuffer);
      FreePool (ImageOut);
      return Status;
    }

    ASSERT (FontInfo != NULL);
    for (Index = 0; Index < (UINTN)Width * Height; Index++) {
      BltBuffer[Index] = FontInfo->BackgroundColor;
    }

    FreePool (FontInfo);

    //
    // Draw the incoming image to the new created image.
    //
    *Blt = ImageOut;
    return ImageToBlt (
             Image->Bitmap,
             BltX,
             BltY,
             Image->Width,
             Image->Height,
             Transparent,
             Blt
             );
  }
}

/**
  This function renders an image to a bitmap or the screen using the specified
  color and options. It draws the image on an existing bitmap, allocates a new
  bitmap or uses the screen. The images can be clipped.

  @param  This                   A pointer to the EFI_HII_IMAGE_PROTOCOL instance.
  @param  Flags                  Describes how the image is to be drawn.
  @param  PackageList            The package list in the HII database to search for
                                 the  specified image.
  @param  ImageId                The image's id, which is unique within
                                 PackageList.
  @param  Blt                    If this points to a non-NULL on entry, this points
                                 to the image, which is Width pixels wide and
                                 Height pixels high. The image will be drawn onto
                                 this image and
                                 EFI_HII_DRAW_FLAG_CLIP is implied. If this points
                                 to a  NULL on entry, then a buffer will be
                                 allocated to hold  the generated image and the
                                 pointer updated on exit. It is the caller's
                                 responsibility to free this buffer.
  @param  BltX                   Specifies the offset from the left and top edge of
                                 the  output image of the first pixel in the image.
  @param  BltY                   Specifies the offset from the left and top edge of
                                 the  output image of the first pixel in the image.

  @retval EFI_SUCCESS            The image was successfully drawn.
  @retval EFI_OUT_OF_RESOURCES   Unable to allocate an output buffer for Blt.
  @retval EFI_INVALID_PARAMETER  The Blt was NULL.
  @retval EFI_NOT_FOUND          The image specified by ImageId is not in the database.
                           The specified PackageList is not in the database.

**/
EFI_STATUS
EFIAPI
HiiDrawImageId (
  IN CONST EFI_HII_IMAGE_PROTOCOL  *This,
  IN EFI_HII_DRAW_FLAGS            Flags,
  IN EFI_HII_HANDLE                PackageList,
  IN EFI_IMAGE_ID                  ImageId,
  IN OUT EFI_IMAGE_OUTPUT          **Blt,
  IN UINTN                         BltX,
  IN UINTN                         BltY
  )
{
  EFI_STATUS       Status;
  EFI_IMAGE_INPUT  Image;

  //
  // Check input parameter.
  //
  if ((This == NULL) || (Blt == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Get the specified Image.
  //
  Status = HiiGetImage (This, PackageList, ImageId, &Image);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Draw this image.
  //
  Status = HiiDrawImage (This, Flags, &Image, Blt, BltX, BltY);
  if (Image.Bitmap != NULL) {
    FreePool (Image.Bitmap);
  }

  return Status;
}
