/** @file The instance of GIF Decoder Library ;****************************************************************************** ;* 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 "H2OImageDecoderLibCommon.h" #include #include #include #include "Gif.h" UINT8 *mOutStack = NULL; LZW_ENTRY *mLzwTable = NULL; STATIC VOID FixColorMapForTransparent( UINT32 SizeOfColorTable, EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ColorMap ) { UINT32 Index; UINT32 MapSize; // // in 24bit mode, (0,0,0) is transparent; (0,0,1) drak red is black // MapSize = 1 << (SizeOfColorTable + 1); for (Index = 0; Index < MapSize; Index++) { if (ColorMap[Index].Blue == 0 && ColorMap[Index].Green == 0 && ColorMap[Index].Red == 0) { ColorMap[Index].Red = 1; } } } /** Calculate the frequency. @param Frequency @retval EFI_SUCCESS The frequency is calculated. @return other Some error occurs. **/ EFI_STATUS GetFrequency ( UINT64 *Frequency ) { *Frequency = GetPerformanceCounterProperties (NULL, NULL); *Frequency = DivU64x32 (*Frequency, 1000); return EFI_SUCCESS; } /** Create animation from file image buffer @param [in] FileData @param [in] FileSize @param [in] Data Save private data information. @param [out] Animation Contain all decompress images which are BLT format in GIF image data. @retval EFI_SUCCESS create success @retval EFI_INVALID_PARAMETER invalid parameter @retval EFI_OUT_OF_RESOURCES allocate memory fail @retval EFI_UNSUPPORTED format error **/ EFI_STATUS EFIAPI H2OHiiCreateAnimationFromMem ( IN UINT8 *FileData, IN UINTN FileSize, IN VOID *Data, OUT ANIMATION **Animation ) { EFI_STATUS Status; ANIMATION *AnimationEntry; ANIMATION_FRAME *AnimationFrame; GIF_LOGICAL_SCREEN_DESCRIPTOR GifScreen; GIF_IMAGE_DESCRIPTOR ImageDesc; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ImageData; UINT32 ImageSize; GIF_GRAPHIC_CONTROL_EXTENSION GraphicControl; EFI_GRAPHICS_OUTPUT_BLT_PIXEL GlobalColorMap[256]; EFI_GRAPHICS_OUTPUT_BLT_PIXEL LocalColorMap[256]; UINT8 *CompressedData; UINTN CompressedDataSize; EFI_GRAPHICS_OUTPUT_BLT_PIXEL BackupColor; ANIMATION_FRAME *CurrentFrame; Status = EFI_SUCCESS; CompressedData = NULL; CompressedDataSize = 0; CurrentFrame = NULL; ZeroMem (&BackupColor, sizeof (BackupColor)); ASSERT (Animation != NULL); if (Animation == NULL) { return EFI_INVALID_PARAMETER; } if (mOutStack == NULL) { mOutStack = H2OImageDecoderLibAllocateZeroMem (4100 * sizeof (UINT8)); } if (mLzwTable == NULL) { mLzwTable = H2OImageDecoderLibAllocateZeroMem (4096 * sizeof (LZW_ENTRY)); } AnimationEntry = (ANIMATION *) H2OImageDecoderLibAllocateZeroMem (sizeof (ANIMATION)); if (AnimationEntry == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Err; } // // Verify Gif Header and Get Logical Screen Descriptor and Global Color Map // Status = GifDecoderGetLogicalScreen ( &FileData, &FileSize, &GifScreen, GlobalColorMap ); if (EFI_ERROR (Status)) { goto Err; } FixColorMapForTransparent (GifScreen.PackedFields.SizeOfGlobalColorTable, GlobalColorMap); AnimationEntry->Data = Data; AnimationEntry->Width = GifScreen.LogicalScreenWidth; AnimationEntry->Height = GifScreen.LogicalScreenHeight; AnimationEntry->BkColor = GlobalColorMap[GifScreen.BackGroundColorIndex]; // // default graphic control // GraphicControl.ExtensionIntroducer = EXTENSION_INTRODUCER; GraphicControl.GraphicControlLabel = GRAPHIC_CONTROL_LABEL; GraphicControl.BlockSize = 4; GraphicControl.PackedFields.DisposalMethod = 0; GraphicControl.PackedFields.UserInputFlag = 0; GraphicControl.PackedFields.TransparentColorFlag = 0; GraphicControl.DelayTime = 10; GraphicControl.TransparentColorIndex = 0; for (;;) { // // Get single image descriptor // Status = GifDecoderGetImageDesc ( &FileData, &FileSize, &ImageDesc, LocalColorMap, &GraphicControl ); if (Status == EFI_NOT_FOUND) { break; } if (EFI_ERROR (Status)) { goto Err; } if (ImageDesc.PackedFields.LocalColorTableFlag) { FixColorMapForTransparent (ImageDesc.PackedFields.SizeOfLocalColorTable, LocalColorMap); } // // transparent, set color to (0,0,0) // if (GraphicControl.PackedFields.TransparentColorFlag) { if (!ImageDesc.PackedFields.LocalColorTableFlag) { BackupColor = GlobalColorMap[GraphicControl.TransparentColorIndex]; GlobalColorMap[GraphicControl.TransparentColorIndex].Blue = 0; GlobalColorMap[GraphicControl.TransparentColorIndex].Green = 0; GlobalColorMap[GraphicControl.TransparentColorIndex].Red = 0; } else { LocalColorMap[GraphicControl.TransparentColorIndex].Blue = 0; LocalColorMap[GraphicControl.TransparentColorIndex].Green = 0; LocalColorMap[GraphicControl.TransparentColorIndex].Red = 0; } } // // Get single image data // ImageSize = (UINTN) ImageDesc.ImageWidth * ImageDesc.ImageHeight * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); ImageData = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)H2OImageDecoderLibAllocateMem (ImageSize); if (ImageData == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Err; } CompressedData = NULL; CompressedDataSize = 0; Status = GifDecoderGetImageData ( &FileData, &FileSize, &ImageDesc, GlobalColorMap, LocalColorMap, &GraphicControl, ImageData, ImageSize, CompressedData, &CompressedDataSize ); if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { goto Err; } CompressedData = (UINT8 *)H2OImageDecoderLibAllocateMem (CompressedDataSize); if (CompressedData == NULL) { goto Err; } Status = GifDecoderGetImageData ( &FileData, &FileSize, &ImageDesc, GlobalColorMap, LocalColorMap, &GraphicControl, ImageData, ImageSize, CompressedData, &CompressedDataSize ); if (EFI_ERROR (Status)) { goto Err; } H2OImageDecoderLibFreeMem (CompressedData, CompressedDataSize); CompressedData = NULL; // // restore color map // if (GraphicControl.PackedFields.TransparentColorFlag) { if (!ImageDesc.PackedFields.LocalColorTableFlag) { GlobalColorMap[GraphicControl.TransparentColorIndex] = BackupColor; } } // // process animation frame // AnimationFrame = (ANIMATION_FRAME *) H2OImageDecoderLibAllocateZeroMem (sizeof (ANIMATION_FRAME)); if (AnimationFrame == NULL) { goto Err; } AnimationFrame->Bitmap = ImageData; AnimationFrame->Disposal = GraphicControl.PackedFields.DisposalMethod; AnimationFrame->OffsetX = ImageDesc.ImageLeftPosition; AnimationFrame->OffsetY = ImageDesc.ImageTopPosition; AnimationFrame->Width = ImageDesc.ImageWidth; AnimationFrame->Height = ImageDesc.ImageHeight; AnimationFrame->Delay = GraphicControl.DelayTime; if (GraphicControl.PackedFields.TransparentColorFlag) { AnimationFrame->Transparent = TRUE; } else { AnimationFrame->Transparent = FALSE; } // // add frame to animation frame linked list // AnimationFrame->Next = NULL; if (AnimationEntry->Frames == NULL) { AnimationEntry->Frames = AnimationFrame; AnimationFrame->Prev = NULL; } else if (CurrentFrame != NULL) { CurrentFrame->Next = AnimationFrame; AnimationFrame->Prev = CurrentFrame; } CurrentFrame = AnimationFrame; } *Animation = AnimationEntry; return EFI_SUCCESS; Err: if (AnimationEntry != NULL) { H2OHiiDestroyAnimation (AnimationEntry); } if (CompressedData != NULL) { H2OImageDecoderLibFreeMem (CompressedData, CompressedDataSize); } return Status; } /** Draw one animation frame @param [in] Animation Animation contain all frame list @param [in] Frame Frame for draw @param [in] Gop Graphics output protocol interface @param [in] BltWidth Blt width limit @param [in] BltHeight Blt height limit @param [in] BltX Image start x @param [in] BltY Image start y @retval EFI_SUCCESS draw frame success @return other call blt fail **/ STATIC EFI_STATUS DrawAnimationFrame ( IN ANIMATION *Animation, IN ANIMATION_FRAME *Frame, IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop, IN UINTN BltWidth, IN UINTN BltHeight, IN UINTN BltX, IN UINTN BltY ) { EFI_STATUS Status; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuf; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltPtr; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BitmapPtr; UINTN Width; UINTN Height; UINTN X; UINTN Y; ASSERT (Animation != NULL && Gop != NULL); if ((Animation == NULL) || (Gop == NULL)) { return EFI_INVALID_PARAMETER; } if (Frame == NULL) { return EFI_SUCCESS; } // // out of display range, do nothing // if ((((UINT32)BltX + Frame->OffsetX) > BltWidth) || (((UINT32)BltY + Frame->OffsetY) > BltHeight)) { return EFI_SUCCESS; } Width = Frame->Width; Height = Frame->Height; if (Width > (BltWidth - BltX)) { Width = BltWidth - BltX; } if (Height > (BltHeight - BltY)) { Height = (BltHeight - BltY); } // // draw frame image // if (Frame->Transparent) { BltBuf = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)H2OImageDecoderLibAllocateMem (Width * Height * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); Status = Gop->Blt ( Gop, BltBuf, EfiBltVideoToBltBuffer, BltX + Frame->OffsetX, BltY + Frame->OffsetY, 0, 0, Width, Height, 0 ); } else { BltBuf = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)H2OImageDecoderLibAllocateZeroMem (Width * Height * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); } ASSERT (BltBuf != NULL); if (BltBuf == NULL) { return EFI_OUT_OF_RESOURCES; } for (Y = 0; Y < Height; Y++) { BltPtr = &BltBuf[Y * Width]; BitmapPtr = &Frame->Bitmap[Y * Frame->Width]; for (X = 0; X < Width; X++) { if ((BitmapPtr->Blue != 0) || (BitmapPtr->Green != 0) || (BitmapPtr->Red != 0)) { *BltPtr = *BitmapPtr; } BltPtr++; BitmapPtr++; } } Status = Gop->Blt ( Gop, BltBuf, EfiBltBufferToVideo, 0, 0, BltX + Frame->OffsetX, BltY + Frame->OffsetY, Width, Height, 0 ); H2OImageDecoderLibFreeMem (BltBuf, Width * Height * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); return EFI_SUCCESS; } /** Draw one animation frame for "Restore Previous Frame" disposal @param [in] Animation Animation contain all frame list @param [in] Frame Frame for draw @param [in] Gop Graphics output protocol interface @param [in] BltWidth Blt width limit @param [in] BltHeight Blt height limit @param [in] BltX Image start x @param [in] BltY Image start y @retval EFI_SUCCESS draw frame success @return other call blt fail **/ STATIC EFI_STATUS RestorePrevFrame ( IN ANIMATION *Animation, IN ANIMATION_FRAME *Frame, IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop, IN UINTN BltWidth, IN UINTN BltHeight, IN UINTN BltX, IN UINTN BltY ) { UINT16 Disposal; EFI_STATUS Status; EFI_GRAPHICS_OUTPUT_BLT_PIXEL BkColor; UINTN Width; UINTN Height; UINTN X; UINTN Y; Disposal = 256; if (Frame != NULL) { // // out of display range, do nothing // if ((((UINT32)BltX + Frame->OffsetX) > BltWidth) || (((UINT32)BltY + Frame->OffsetY) > BltHeight)) { return EFI_SUCCESS; } Disposal = Frame->Disposal; Width = Frame->Width; Height = Frame->Height; X = BltX + Frame->OffsetX; Y = BltX + Frame->OffsetY; } else { Width = Animation->Width; Height = Animation->Height; X = BltX; Y = BltY; } BkColor = Animation->BkColor; if (Width > (BltWidth - BltX)) { Width = BltWidth - BltX; } if (Height > (BltHeight - BltY)) { Height = (BltHeight - BltY); } switch (Disposal) { case 2: // background color case 256: // no frame Status = Gop->Blt ( Gop, &BkColor, EfiBltVideoFill, 0, 0, X, Y, Width, Height, 0 ); if (Frame == NULL) { return EFI_SUCCESS; } break; case 3: // restore RestorePrevFrame (Animation, Frame->Prev, Gop, BltWidth, BltHeight, BltX, BltY); return EFI_SUCCESS; default: break; } DrawAnimationFrame (Animation, Frame, Gop, BltWidth, BltHeight, BltX, BltY); return EFI_SUCCESS; } /** Draw one animation frame by disposal case 0, 1: do nothing case 2: fill background color case 3: restore previous frame @param [in] Animation Animation contain all frame list @param [in] Frame Frame for draw @param [in] Gop Graphics output protocol interface @param [in] BltWidth Blt width limit @param [in] BltHeight Blt height limit @param [in] BltX Image start x @param [in] BltY Image start y @retval EFI_SUCCESS draw frame success @return other call blt fail **/ STATIC EFI_STATUS DrawAnimationFrameDisposal ( IN ANIMATION *Animation, IN ANIMATION_FRAME *Frame, IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop, IN UINTN BltWidth, IN UINTN BltHeight, IN UINTN BltX, IN UINTN BltY ) { UINT16 Disposal; EFI_STATUS Status; EFI_GRAPHICS_OUTPUT_BLT_PIXEL BkColor; UINTN Width; UINTN Height; ASSERT (Animation->Frames != NULL); Disposal = 256; if (Frame != NULL) { Disposal = Frame->Disposal; } else { ASSERT (Animation->Frames != NULL); if (Animation->Frames == NULL) { return EFI_INVALID_PARAMETER; } Frame = Animation->Frames; } BkColor = Animation->BkColor; // // out of display range, do nothing // if ((((UINT32)BltX + Frame->OffsetX) > BltWidth) || (((UINT32)BltY + Frame->OffsetY) > BltHeight)) { return EFI_SUCCESS; } Width = Frame->Width; Height = Frame->Height; if (Width > (BltWidth - BltX)) { Width = BltWidth - BltX; } if (Height > (BltHeight - BltY)) { Height = (BltHeight - BltY); } switch (Disposal) { case 2: // background color //case 256: // no frame Status = Gop->Blt ( Gop, &BkColor, EfiBltVideoFill, 0, 0, BltX + Frame->OffsetX, BltY + Frame->OffsetY, Width, Height, 0 ); break; case 3: // restore RestorePrevFrame (Animation, Frame->Prev, Gop, BltWidth, BltHeight, BltX, BltY); break; default: break; } return EFI_SUCCESS; } /** Draw next animation frame @param [in] Entry Animation Refresh Entry for animation information in screen @param [in] Gop Graphics output protocol interface @retval EFI_SUCCESS draw frame success @retval EFI_NOT_FOUND no next frame to draw @retval EFI_INVALID_PARAMETER Invalid parameter **/ EFI_STATUS EFIAPI H2OHiiNextAnimationFrame ( IN ANIMATION_REFRESH_ENTRY *Entry, IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop ) { ASSERT (Entry != NULL && Entry->Animation != NULL && Entry->Animation->Frames != NULL); if (Entry == NULL || Entry->Animation == NULL || Entry->Animation->Frames == NULL) { return EFI_INVALID_PARAMETER; } if (Entry->Current == NULL) { Entry->Current = Entry->Animation->Frames; } else { if (Entry->Current->Next == NULL) { // // if autoloop is true, play animaiton from first image again, // else stop animation // if (Entry->AutoLoop) { Entry->Current = Entry->Animation->Frames; } else { Entry->Current = NULL; Entry->Status = ANIM_STATUS_STOP; return EFI_NOT_FOUND; } } else { Entry->Current = Entry->Current->Next; } } if (Entry->Current != NULL) { // disposal DrawAnimationFrameDisposal (Entry->Animation, Entry->Current->Prev, Gop, Entry->BltWidth, Entry->BltHeight, Entry->X, Entry->Y); DrawAnimationFrame (Entry->Animation, Entry->Current, Gop, Entry->BltWidth, Entry->BltHeight, Entry->X, Entry->Y); } return EFI_SUCCESS; } /** By record tick and delay time to determinate draw image @param [in] AnimationRefrshEntry Pointer to animation refresh entry @param [in] Gop Point to graphics output protocol @param [in] CpuFrequency Cpu Frequency for calculate tick elapsed time @retval EFI_SUCCESS animation is playing @retval EFI_NOT_FOUND animation is stopping @retval EFI_INVALID_PARAMETER Invalid parameter **/ EFI_STATUS EFIAPI H2OHiiRefreshAnimation ( IN ANIMATION_REFRESH_ENTRY *AnimationRefrshEntry, IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop, IN UINT64 CpuFrequency ) { UINT32 DelayTime; UINT32 ElapsedTime; UINT64 CurrentTick; ASSERT (AnimationRefrshEntry != NULL); if (AnimationRefrshEntry == NULL) { return EFI_INVALID_PARAMETER; } if (AnimationRefrshEntry->Status == ANIM_STATUS_PLAY) { if (CpuFrequency == 0) { GetFrequency (&CpuFrequency); } if (AnimationRefrshEntry->Current != NULL) { DelayTime = AnimationRefrshEntry->Current->Delay; DelayTime = (DelayTime < 3) ? 3 : DelayTime; } else { DelayTime = 0; } // // calculate elapsed time is 1/1000, but delay time is 1/100 // CurrentTick = GetPerformanceCounter (); ElapsedTime = (UINT32) DivU64x32 (CurrentTick - AnimationRefrshEntry->RecordTick, (UINT32) (CpuFrequency)); if (ElapsedTime > DelayTime * 10) { H2OHiiNextAnimationFrame (AnimationRefrshEntry, Gop); AnimationRefrshEntry->RecordTick = CurrentTick; } return EFI_SUCCESS; } return EFI_NOT_FOUND; } /** Draw next animation frame @param [in] Animation Contain all decompress images which are BLT format in GIF image data. @retval EFI_SUCCESS free memory success @retval EFI_INVALID_PARAMETER Invalid parameter **/ EFI_STATUS EFIAPI H2OHiiDestroyAnimation ( IN ANIMATION *Animation ) { ANIMATION_FRAME *Next; ANIMATION_FRAME *Frame; ASSERT (Animation != NULL); if (Animation == NULL) { return EFI_INVALID_PARAMETER; } Frame = Animation->Frames; while (Frame != NULL) { Next = Frame->Next; if (Frame->Bitmap != NULL) { H2OImageDecoderLibFreeMem (Frame->Bitmap, (UINTN) Frame->Height * Frame->Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); } H2OImageDecoderLibFreeMem (Frame, sizeof (ANIMATION_FRAME)); Frame = Next; }; H2OImageDecoderLibFreeMem (Animation, sizeof (ANIMATION)); return EFI_SUCCESS; }