/** @file Gif Converter application to covert Gif file to HII animation binary ;****************************************************************************** ;* Copyright (c) 2020, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include "Gif.h" #include STATIC EFI_STATUS RetrieveCompressedData ( IN UINT8 **GifFileImage, IN UINTN *GifFileSize, IN UINT8 *CompressedData, IN OUT UINTN *CompressedDataSize ); STATIC UINT16 GetCode ( IN UINT8 *BufIn, IN UINT8 CodeSize, OUT UINT32 *WhichBit ); STATIC UINT32 LZWDecoder ( IN UINT8 *BufIn, IN UINTN BufInSize, IN BOOLEAN Interlaced, IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ColorMap, IN UINT16 Width, IN UINT16 Height, IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ImageData ); /** Read data safely @param [in, out] FileImage Pointer to file image buffer @param [in, out] FileSize Pointer to file image size @param [out] Buffer Pointer to read buffer @param [in] ReadSize Size of read data @retval TRUE Read data success @retval FALSE No data to read **/ STATIC BOOLEAN ReadOk ( IN OUT UINT8 **FileImage, IN OUT UINTN *FileSize, OUT VOID *Buffer, IN UINT32 ReadSize ) { if (*FileSize < ReadSize) { return FALSE; } CopyMem (Buffer, *FileImage, ReadSize); *FileImage += ReadSize; *FileSize -= ReadSize; return TRUE; } /** Get data block, 1 byte block size and n byte data @param [in, out] GifFileImage Pointer to file image @param [in, out] GifFileSize Pointer to file image size @param [out] Buffer Pointer to read data @retval 0 No data to read @return others Read data size **/ STATIC UINT32 GetDataBlock ( IN OUT UINT8 **GifFileImage, IN OUT UINTN *GifFileSize, OUT UINT8 *Buffer ) { UINT8 BlockSize; if (!ReadOk (GifFileImage, GifFileSize, &BlockSize, 1)) { return 0; } if ((BlockSize != 0) && (!ReadOk(GifFileImage, GifFileSize, Buffer, BlockSize))) { return 0; } return BlockSize; } /** Get gif logical screen, include gif image information and color map @param [in, out] FileImage Pointer to file image @param [in, out] FileSize Pointer to file image size @param [out] GifScreen Gif image information @param [out] GlobalColorMap Global color map for gif imgae @retval EFI_SUCCESS read success @retval EFI_UNSUPPORTED format error @retval EFI_INVALID_PARAMETER Invalid parameter **/ EFI_STATUS GifDecoderGetLogicalScreen ( IN OUT UINT8 **FileImage, IN OUT UINTN *FileSize, OUT GIF_LOGICAL_SCREEN_DESCRIPTOR *GifScreen, OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlobalColorMap ) { UINT8 *ColorPtr; GIF_HEADER GifHeader; UINT32 MapSize; UINT16 Index; ASSERT (FileImage != NULL && *FileImage != NULL && FileSize != NULL); if ((FileImage == NULL) || (*FileImage == NULL) || (FileSize == NULL)) { return EFI_INVALID_PARAMETER; } ASSERT (GifScreen != NULL && GlobalColorMap != NULL); if ((GifScreen == NULL) || (GlobalColorMap == NULL)) { return EFI_INVALID_PARAMETER; } // // Gif Header // if ((!ReadOk (FileImage, FileSize, &GifHeader, sizeof(GIF_HEADER))) || (CompareMem (&GifHeader, "GIF87a", sizeof (GIF_HEADER)) != 0 && CompareMem (&GifHeader, "GIF89a", sizeof (GIF_HEADER)) != 0)) { return EFI_UNSUPPORTED; } // // Gif Logical Screen Descriptor // if (!ReadOk (FileImage, FileSize, GifScreen, sizeof (GIF_LOGICAL_SCREEN_DESCRIPTOR))) { return EFI_UNSUPPORTED; } // // Gif Global Color Map // MapSize = 1 << (GifScreen->PackedFields.SizeOfGlobalColorTable + 1); if (GifScreen->PackedFields.GlobalColorTableFlag) { if (*FileSize < (UINT32)(MapSize * 3)) { return EFI_UNSUPPORTED; } ColorPtr = *FileImage; for (Index = 0; Index < MapSize; ++Index) { GlobalColorMap[Index].Red = *ColorPtr; GlobalColorMap[Index].Green = *(ColorPtr + 1); GlobalColorMap[Index].Blue = *(ColorPtr + 2); GlobalColorMap[Index].Reserved = 0; ColorPtr += 3; } *FileImage += MapSize * 3; *FileSize -= MapSize * 3; } else { for (Index = 0; Index < MapSize; ++Index) { GlobalColorMap[Index].Red = (UINT8)Index; GlobalColorMap[Index].Green = (UINT8)Index; GlobalColorMap[Index].Blue = (UINT8)Index; GlobalColorMap[Index].Reserved = 0; } } return EFI_SUCCESS; } /** Get single image description (offset x, y), local color map, and graphic control (delay time, and transparent) @retval EFI_SUCCESS read success, has one signle image @retval EFI_NOT_FOUND no image to read @retval EFI_UNSUPPORTED format error @retval EFI_INVALID_PARAMETER Invalid parameter. **/ EFI_STATUS GifDecoderGetImageDesc ( IN UINT8 **GifFileImage, IN UINTN *GifFileSize, OUT GIF_IMAGE_DESCRIPTOR *ImageDesc, OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *LocalColorMap, OUT GIF_GRAPHIC_CONTROL_EXTENSION *GraphicControl ) { UINT8 Ch; UINT8 Label; UINT8 Buf[256]; UINT32 Count; UINT8 *ColorPtr; UINT8 *ImagePtr; UINTN FileSize; UINT32 MapSize; UINT32 Index; ASSERT (GifFileImage != NULL); if (GifFileImage == NULL) { return EFI_INVALID_PARAMETER; } ImagePtr = *GifFileImage; FileSize = *GifFileSize; if (FileSize == 0) { return EFI_NOT_FOUND; } Ch = *ImagePtr; while (Ch != GIF_TRAILER) { if (Ch == IMAGE_SEPARATOR) { if (!ReadOk (&ImagePtr, &FileSize, ImageDesc, sizeof (GIF_IMAGE_DESCRIPTOR))) { return EFI_UNSUPPORTED; } if (ImageDesc->PackedFields.LocalColorTableFlag) { MapSize = 1 << (ImageDesc->PackedFields.SizeOfLocalColorTable + 1); if (FileSize < (UINT32)(MapSize * 3)) { return EFI_UNSUPPORTED; } ColorPtr = ImagePtr; for (Index = 0; Index < MapSize; ++Index) { LocalColorMap[Index].Red = *ColorPtr; LocalColorMap[Index].Green = *(ColorPtr + 1); LocalColorMap[Index].Blue = *(ColorPtr + 2); LocalColorMap[Index].Reserved = 0; ColorPtr += 3; } ImagePtr += MapSize * 3; FileSize -= MapSize * 3; } *GifFileImage = ImagePtr; *GifFileSize = FileSize; return EFI_SUCCESS; } else if (Ch == EXTENSION_INTRODUCER) { if (FileSize < 2) { return EFI_UNSUPPORTED; } Label = *(ImagePtr + 1); ImagePtr += 2; FileSize -= 2; if (Label == GRAPHIC_CONTROL_LABEL) { Count = GetDataBlock (&ImagePtr, &FileSize, Buf); if (Count != 4) { return EFI_UNSUPPORTED; } else { CopyMem (&GraphicControl->PackedFields, Buf, 4); while (GetDataBlock (&ImagePtr, &FileSize, Buf) != 0); } } else { while (GetDataBlock (&ImagePtr, &FileSize, Buf) != 0); } } else { ImagePtr++; FileSize--; } if (FileSize == 0) { return EFI_UNSUPPORTED; } else { Ch = *ImagePtr; } } *GifFileImage = ImagePtr; *GifFileSize = FileSize; return EFI_NOT_FOUND; } /** Decompress LZW data to graphics output pixel format image data @param [in] GifFileImage Pointer to file imag @param [in] GifFileSize Pointer to file image size @param [in] ImageDesc Gif image description @param [in] GlobalColorMap Global color map @param [in] LocalColorMap Local color map @param [in] GraphicControl @param [out] ImageData Graphics Output Pixel format image data @param [in] ImageSize @param [in] CompressedData Temporary buffer for fragmentary image data block @param [in, out] CompressedDataSize Temporary buffer size @retval EFI_SUCCESS read success, has one signle image @retval EFI_BUFFER_TOO_SMALL buffer too small @retval EFI_UNSUPPORTED format error **/ EFI_STATUS GifDecoderGetImageData ( IN UINT8 **GifFileImage, IN UINTN *GifFileSize, IN GIF_IMAGE_DESCRIPTOR *ImageDesc, IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlobalColorMap, IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *LocalColorMap, IN GIF_GRAPHIC_CONTROL_EXTENSION *GraphicControl, OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ImageData, IN UINT32 ImageSize, IN UINT8 *CompressedData, IN OUT UINTN *CompressedDataSize ) { EFI_STATUS Status; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ColorMap; UINT8 *ImagePtr; UINTN FileSize; // // allocate compressed data // ImagePtr = *GifFileImage; FileSize = *GifFileSize; Status = RetrieveCompressedData ( &ImagePtr, &FileSize, CompressedData, CompressedDataSize ); if (EFI_ERROR (Status)) { return Status; } *GifFileImage = ImagePtr; *GifFileSize = FileSize; if (ImageDesc->PackedFields.LocalColorTableFlag) { ColorMap = LocalColorMap; } else { ColorMap = GlobalColorMap; } LZWDecoder ( CompressedData, *CompressedDataSize, ImageDesc->PackedFields.InterlaceFlag, ColorMap, ImageDesc->ImageWidth, ImageDesc->ImageHeight, ImageData ); return EFI_SUCCESS; } /** Convert fragmentary image data block to a continuous block @param [in] GifFileImage Pointer to file imag @param [in] GifFileSize Pointer to file image size @param [in] CompressedData Temporary buffer for fragmentary image data block @param [in, out] CompressedDataSize Temporary buffer size @retval EFI_SUCCESS read success, has one signle image @retval EFI_BUFFER_TOO_SMALL buffer too small @retval EFI_UNSUPPORTED format error **/ EFI_STATUS RetrieveCompressedData ( IN UINT8 **GifFileImage, IN UINTN *GifFileSize, IN UINT8 *CompressedData, IN OUT UINTN *CompressedDataSize ) { UINT8 LzwCodeSize; UINT8 BlockLength; UINTN TotalSize; UINTN AvailableSize; UINT8 Buf[256]; UINT8 *ImagePtr; UINTN FileSize; ImagePtr = *GifFileImage; FileSize = *GifFileSize; AvailableSize = *CompressedDataSize; // // calculate lzwcodesize compressed data size, then copy data // if (!ReadOk (&ImagePtr, &FileSize, &LzwCodeSize, 1)) { return EFI_UNSUPPORTED; } TotalSize = 1; if (AvailableSize >= 1) { *CompressedData = LzwCodeSize; AvailableSize -= 1; } for (;;) { if (!ReadOk (&ImagePtr, &FileSize, &BlockLength, 1)) { return EFI_UNSUPPORTED; } if ((BlockLength == 0) || (AvailableSize < BlockLength)) { break; } if (!ReadOk (&ImagePtr, &FileSize, CompressedData + TotalSize, BlockLength)) { return EFI_UNSUPPORTED; } AvailableSize -= BlockLength; TotalSize += BlockLength; } if (BlockLength != 0) { do { if (!ReadOk (&ImagePtr, &FileSize, Buf, BlockLength)) { return EFI_UNSUPPORTED; } TotalSize += BlockLength; if (!ReadOk (&ImagePtr, &FileSize, &BlockLength, 1)) { return EFI_UNSUPPORTED; } } while (BlockLength != 0); } if (*CompressedDataSize < TotalSize) { *CompressedDataSize = TotalSize; return EFI_BUFFER_TOO_SMALL; } *CompressedDataSize = TotalSize; *GifFileImage = ImagePtr; *GifFileSize = FileSize; return EFI_SUCCESS; } /** Get code data by code size @param [in] BufIn Buffer to read @param [in] CodeSize Size (bits) of Read @param [out] WhichBit Record already read bits @retval Data read data **/ STATIC UINT16 GetCode ( IN UINT8 *BufIn, IN UINT8 CodeSize, OUT UINT32 *WhichBit ) { UINT32 Bit; UINT32 Data; Bit = *WhichBit; Data = *(UINT32 *) (BufIn + Bit / 8); Data >>= (Bit & 7); Data = Data & ((1 << CodeSize) - 1); *WhichBit += CodeSize; return (UINT16)Data; } /** LZW decoder @param [in] BufIn Pointer to compressed data @param [in] BufInSize Pointer to compressed data size @param [in] Interlaced Data store is interlaced @param [in] ColorMap Image color map @param [in] Width Image width @param [in] Height Image Height @param [in, out] Blt Output image data @return ReadBits Read image bits **/ STATIC UINT32 LZWDecoder ( IN UINT8 *BufIn, IN UINTN BufInSize, IN BOOLEAN Interlaced, IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ColorMap, IN UINT16 Width, IN UINT16 Height, IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt ) { UINT16 PrevCode; UINT16 Code; UINT16 ClearCode; UINT16 EndCode; UINT16 TmpCode; UINT8 InitCodeSize; UINT8 CodeSize; UINT16 FirstIndex; UINT16 EntryIndex; UINT32 ReadBits; INTN OutIndex; UINT16 Index; INT16 StackIndex; UINT16 X; UINT16 Y; InitCodeSize = *BufIn; BufIn++; CodeSize = InitCodeSize + 1; ClearCode = (1 << InitCodeSize); EndCode = ClearCode + 1; FirstIndex = EndCode + 1; EntryIndex = FirstIndex; ReadBits = 0; OutIndex = 0; PrevCode = NONCODE; X = 0; Y = 0; // // init lzw table // for (Index = 0; Index < ClearCode; ++Index) { mLzwTable[Index].Prefix = NONCODE; mLzwTable[Index].Suffix = (UINT8)Index; } for (;;) { if (((ReadBits + CodeSize) / 8) > BufInSize) { // // no data // //Print (L"nodata"); break; } else { Code = GetCode (BufIn, CodeSize, &ReadBits); } if (Code == EndCode) { break; } if (Code == ClearCode) { CodeSize = InitCodeSize + 1; EntryIndex = FirstIndex; PrevCode = ClearCode; } else { if (Y == Height) { //Print (L"bad code"); break; } StackIndex = -1; if (Code < EntryIndex) { // // code in table // convert [code] to string, and output [code] // add new entry [prevcode][first char of code] // TmpCode = Code; while (TmpCode != NONCODE) { mOutStack[++StackIndex] = mLzwTable[TmpCode].Suffix; TmpCode = mLzwTable[TmpCode].Prefix; } } else { if (PrevCode == ClearCode) { //Print (L"bad code"); continue; } // // code isn't in table // convert [prevcode] to string, and output [prevcode][first char of prevcode] // add new entry [prevcode][first char of prevcode] // StackIndex++; TmpCode = PrevCode; while (TmpCode != NONCODE) { mOutStack[++StackIndex] = mLzwTable[TmpCode].Suffix; TmpCode = mLzwTable[TmpCode].Prefix; } mOutStack[0] = mOutStack[StackIndex]; } if (PrevCode != ClearCode) { mLzwTable[EntryIndex].Prefix = PrevCode; mLzwTable[EntryIndex].Suffix = mOutStack[StackIndex]; EntryIndex++; } // // output outstack data // while (StackIndex >= 0) { Blt[X + Y * Width] = ColorMap[mOutStack[StackIndex--]]; X++; if (X == Width) { X = 0; if (Interlaced) { if ((Y & 7) == 0) { Y += 8; if (Y >= Height) Y = 4; } else if ((Y & 3) == 0) { Y += 8; if (Y >= Height) Y = 2; } else if ((Y & 1) == 0) { Y += 4; if (Y >= Height) Y = 1; } else Y += 2; } else { Y++; } } if (Y == Height) { break; } } } if (EntryIndex >= (1 << CodeSize)) { if (CodeSize < 12) { CodeSize++; } else { //Print (L"bad code"); continue; } } PrevCode = Code; } return ReadBits; }