text: /** @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;
  }
  
  