/** @file The instance of JPEG Decode 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 "JfifDecode.h" #include "McuDecode.h" EFI_JPEG_JFIF_DATA mJfifData; EFI_JPEG_DECODER_DATA mDecoderData; /** Get the SOF0(Start Of Frame, Baseline sequential DCT mode) marker's data. And initialize the relative globle parameters. @retval EFI_JPEG_DECODE_SUCCESS Get the markers data successfully. @retval EFI_JPEG_SOF_ERROR Not have the three components info, The Sample precision is not 8 bits, Does not have a image components in the frame, The sample rate Hi, Vi is neither 1 nor 2, The blocks number in a MCU is more than 10. **/ EFI_JPEG_DECODER_STATUS GetSof0Data ( IN UINT8 *ImagePtr ) { UINT8 Index; UINT8 Blocks; // // ImagePtr[4]: sample precision, baseline sequential just support 8 bits. // ImagePtr[9]: number of image components in frame. // if (ImagePtr[4] != 8 || ImagePtr[9] == 0 || ImagePtr[9] > 3) { return EFI_JPEG_SOF_ERROR; } mJfifData.Sof0Data.Height = (ImagePtr[5] << 8) + ImagePtr[6]; mJfifData.Sof0Data.Width = (ImagePtr[7] << 8) + ImagePtr[8]; mJfifData.Sof0Data.Components = ImagePtr[9]; Blocks = 0; for (Index = 0; Index < ImagePtr[9]; Index++) { mJfifData.Sof0Data.Samples[Index].Hi = ImagePtr[11 + Index * 3] >> 4; mJfifData.Sof0Data.Samples[Index].Vi = ImagePtr[11 + Index * 3] & 0x0F; mJfifData.Sof0Data.Samples[Index].Blocks = mJfifData.Sof0Data.Samples[Index].Hi * mJfifData.Sof0Data.Samples[Index].Vi; mJfifData.Sof0Data.Samples[Index].QuanTable = ImagePtr[11 + Index * 3 + 1]; // // Just support 4:1:1,4:2:2, 4:2:1, 1:1:1, 2:1:1 // if (mJfifData.Sof0Data.Samples[Index].Blocks != 1 && mJfifData.Sof0Data.Samples[Index].Blocks != 2 && mJfifData.Sof0Data.Samples[Index].Blocks != 4) { return EFI_JPEG_SOF_ERROR; } Blocks = Blocks + mJfifData.Sof0Data.Samples[Index].Blocks; } // // In a MCU, the blocks' number must not more than 10. // if (Blocks > 10) { return EFI_JPEG_SOF_ERROR; } return EFI_JPEG_DECODE_SUCCESS; } /** Get the HuffmanTable data. And initialize the relative globle parameters. @param [in] ImagePtr The pointer of the DHT (Definintion of Huffman Table) marker @retval EFI_JPEG_DECODE_SUCCESS Get the Huffman table data successfully. @retval EFI_JPEG_HUFFMANTABLE_ERROR The data of Huffman table is error. **/ EFI_JPEG_DECODER_STATUS GetHuffmanTable ( IN UINT8 *ImagePtr ) { UINT8 index1; UINT8 index2; UINT8 TableIndex; UINT8 ValIndex; UINT8 *CodesPtr; UINT8 *ValPtr; UINT8 *TablePtr; BOOLEAN IsZero; UINT16 HuffmanCode; TablePtr = ImagePtr + 4; while(TablePtr < (ImagePtr + ((UINTN)ImagePtr[2] << 8) + ImagePtr[3])) { TableIndex = (TablePtr[0] >> 3) + (TablePtr[0] & 0x0F); if (TableIndex > 3) { return EFI_JPEG_HUFFMANTABLE_ERROR; } mJfifData.HuffTable[TableIndex].Ptr = ImagePtr; mJfifData.HuffTable[TableIndex].Number = TablePtr[0] & 0x0F; mJfifData.HuffTable[TableIndex].Type = TablePtr[0] >> 4; // //Initial the layer's ptr of the Huffman tree, CodesPtr[1] is the first Huffman tree's layer. // then the index of the layer is just the bits' number of the layer's huffman code. //Initial the value's ptr of Huffman tree, ValPtr[0] is the first Huffman tree's value // IsZero = TRUE; ValIndex = 0; HuffmanCode = 0; CodesPtr = TablePtr; ValPtr = TablePtr + 17; for (index1 = 1; index1 < 17; index1++) { mJfifData.HuffTable[TableIndex].Codes[index1] = CodesPtr[index1]; } for (index1 = 1; index1 < 17; index1++) { if (IsZero && CodesPtr[index1] == 0) { continue; } else if (IsZero) { IsZero = FALSE; } HuffmanCode = HuffmanCode << 1; mJfifData.HuffTable[TableIndex].MinCode[index1] = HuffmanCode; for ( index2 = 0; index2 < CodesPtr[index1]; index2++, HuffmanCode++) { mJfifData.HuffTable[TableIndex].HuffmanVal[ValIndex] = ValPtr[ValIndex]; ValIndex++; } mJfifData.HuffTable[TableIndex].MaxCode[index1] = mJfifData.HuffTable[TableIndex].MinCode[index1] + CodesPtr[index1] - 1; mJfifData.HuffTable[TableIndex].FirstCode[index1] = ValIndex - CodesPtr[index1]; } TablePtr = ValPtr + ValIndex; } return EFI_JPEG_DECODE_SUCCESS; } /** Get the important marker data, and store them into the globle parameters, which will be used in the decoding. @param [in] ImageData @param [in] ImageDataSize The length in byte of the JFIF image @retval EFI_JPEG_DECODE_SUCCESS Get the markers data successfully. @retval EFI_JPEG_INVALID_PARAMETER Not have the start marker of SOI, @retval EFI_JPEG_SOF_ERROR The data of SOF is error, @retval EFI_JPEG_SOS_ERROR The data of SOS is error, @retval EFI_JPEG_QUANTIZATIONTABLE_ERROR The data of Quantization table is error, @retval EFI_JPEG_HUFFMANTABLE_ERROR The data of Huffman table is error. **/ EFI_JPEG_DECODER_STATUS InitJfifData ( IN UINT8 *ImageData, IN UINTN ImageDataSize ) { EFI_JPEG_DECODER_STATUS DecodeStatus; UINT32 Index; UINT8 DqtCount; UINT8 *TempPtr; UINT8 *ImagePtr; UINT8 DqtPtrIndex; Index = 0; ImagePtr = ImageData; SetMem (&mJfifData, sizeof(mJfifData), 0); if (ImagePtr[0] != 0xFF || ImagePtr[1] != (UINT8) JPEG_SOI) { return EFI_JPEG_INVALID_PARAMETER; } else { mJfifData.SoiPtr = ImagePtr; ImagePtr += 2; } // // Initialize the value of mJfifData // while (ImagePtr < (ImageData + ImageDataSize)) { if (ImagePtr[0] != 0xFF ||(ImagePtr[0] == 0xFF && ImagePtr[1] == 0x00)) { ImagePtr++; continue; } // // Check this marker is the supporting SOFn (n>0) or not // if (ImagePtr[1] > 0xc0 && ImagePtr[1] != 0xc4 && ImagePtr[1] <= 0xcf) { return EFI_JPEG_SOF_ERROR; } switch (ImagePtr[1]) { case JPEG_SOF0: mJfifData.Sof0Data.Ptr = ImagePtr; DecodeStatus = GetSof0Data (ImagePtr); if (DecodeStatus != EFI_JPEG_DECODE_SUCCESS) { return DecodeStatus; } break; case JPEG_SOS: mJfifData.SosPtr = ImagePtr; // // ImagePtr[4]: interleaving, have three component(Y,Cb,Cr) in the scan or grayscale // ImagePtr[Index - 1]: Ss(Start of spectral or predictior selection) // ImagePtr[Index]: Se(End of spetral selection) // ImagePtr[Index + 1]: Ah:Al(high and low of Successive approximation bit position) // Index = (ImagePtr[2] << 8) + ImagePtr[3]; if ((ImagePtr[4] != 3 && ImagePtr[4] != 1) || ImagePtr[Index - 1] != 0 || ImagePtr[Index] != 63 || ImagePtr[Index + 1] != 0) { return EFI_JPEG_SOS_ERROR; } // // For the checking of the components of this scan. // ImagePtr[4 + Index * 2]: the index of the three component // ImagePtr[4 + Index * 2 + 1]: Td:Ta(the HuffmanTable index of DC and AC ) // for (Index = 0; Index < ImagePtr[4]; Index++) { if (ImagePtr[4 + Index * 2 + 1] > 3 || (ImagePtr[4 + Index * 2 + 2] >> 4) > 1 || (ImagePtr[4 + Index * 2 + 2] & 0x0F) > 1) { return EFI_JPEG_SOS_ERROR; } } break; case JPEG_DHT: DecodeStatus = GetHuffmanTable (ImagePtr); if (DecodeStatus != EFI_JPEG_DECODE_SUCCESS) { return DecodeStatus; } break; case JPEG_DQT: // // There will be several Quantization tables in one DQT segment // About the first Quantization table in the DQT: // ImagePtr[4]: the bits of [0:3], Pq(Quantization table element precision) // ImagePtr[4]: the bits of [4:7], Tq(Quantization table destination identifier) // DqtCount = ((ImagePtr[2] << 8) + ImagePtr[3] - 2)/65; TempPtr = ImagePtr + 4; for (Index = 0; Index < DqtCount; Index++) { DqtPtrIndex = TempPtr[0] & 0x0F; if (DqtPtrIndex > 3 || (TempPtr[0] >> 4) != 0) { return EFI_JPEG_QUANTIZATIONTABLE_ERROR; } mJfifData.DqtPtr[DqtPtrIndex] = TempPtr + 1; TempPtr += 65; } break; case JPEG_APP0: mJfifData.App0Ptr = ImagePtr; break; case JPEG_APP1: case JPEG_APP2: case JPEG_APP3: case JPEG_APP4: case JPEG_APP5: case JPEG_APP6: case JPEG_APP7: case JPEG_APP8: case JPEG_APP9: case JPEG_APP10: case JPEG_APP11: case JPEG_APP12: case JPEG_APP13: case JPEG_APP14: case JPEG_APP15: break; case JPEG_COM: mJfifData.ComPtr = ImagePtr; break; case JPEG_DRI: mJfifData.McuRestart = (ImagePtr[4] << 8) + ImagePtr[5]; break; case JPEG_RST0: case JPEG_RST1: case JPEG_RST2: case JPEG_RST3: case JPEG_RST4: case JPEG_RST5: case JPEG_RST6: case JPEG_RST7: break; default: break; } if (*(ImagePtr + 2) == 0xFF) { ImagePtr += 2; } else { ImagePtr += 2 + (ImagePtr[2] << 8) + ImagePtr[3]; } } return EFI_JPEG_DECODE_SUCCESS; } /** Using the globle parameter mJfifData to initialize the globle parameter of mDecoderData. **/ VOID InitDecoderData ( ) { UINT8 Index1; UINT8 Index2; UINT8 Index3; UINT8 Blocks; // // Initialize the value of mDecoderData // mDecoderData.ImagePtr = mJfifData.SosPtr + 2 + ((UINTN)mJfifData.SosPtr[2] << 8) + mJfifData.SosPtr[3]; mDecoderData.CurByte = *(mDecoderData.ImagePtr); mDecoderData.ImagePtr++; mDecoderData.BitPos = 7; // //Initialize the blocks infomation in a MCU // Blocks = 0; for (Index3 = 0; Index3 < mJfifData.Sof0Data.Components; Index3++) { for (Index1 = 0; Index1 < mJfifData.Sof0Data.Samples[Index3].Vi; Index1++) { for (Index2 = 0; Index2 < mJfifData.Sof0Data.Samples[Index3].Hi; Index2++) { mDecoderData.BlocksInMcu[Blocks].DcAcHTIndex = *(mJfifData.SosPtr + 6 + Index3 * 2); mDecoderData.BlocksInMcu[Blocks].QTIndex = mJfifData.Sof0Data.Samples[Index3].QuanTable; mDecoderData.BlocksInMcu[Blocks].HiViIndex = (Index2 << 4) + Index1; mDecoderData.DcVal[Index3] = 0; mDecoderData.BlocksInMcu[Blocks].DcValPtr = &(mDecoderData.DcVal[Index3]); Blocks++; } } } mDecoderData.Blocks = Blocks; } /** Decodes a JFIF image into a UGA formatted image, and returns the decoded image, image's width and image's height @param [in] This Protocol instance structure @param [in] ImageData The data of the JFIF image, which will be decoded @param [in] ImageDataSize The size in bytes of ImageData @param [out] DecodedData The decoded data, this output parameter contains a newly allocated memory space, and it is the caller's responsibility to free this memory buffer. @param [out] DecodedDataSize The size in bytes of DecodedData @param [out] Height The height of the image's displaying @param [out] Width The width of the image's displaying @param [out] DecoderStatus The status of the decoding progress, defined in \Protocol\JpegDecoder\JpegDecoder.h. @retval EFI_SUCCESS The JFIF image is decoded successfully. @retval EFI_INVALID_PARAMETER Either one of ImageData, Width, Height, DecodedDataSize and DecoderStatus is NULL, or ImageDataSize is zero. @retval EFI_OUT_OF_RESOURCES The memory for DecodedData could not be allocated. @retval EFI_UNSUPPORTED The JFIF image can not be decoded, and the detail error info will be returned by the output parameter DecoderStatus. **/ EFI_STATUS EFIAPI H2OHiiJpegDecode ( IN UINT8 *ImageData, IN UINTN ImageDataSize, OUT UINT8 **DecodedData, OUT UINTN *DecodedDataSize, OUT UINTN *Height, OUT UINTN *Width, OUT EFI_JPEG_DECODER_STATUS *DecoderStatus ) { BOOLEAN IntervalFlag; BOOLEAN IsEnd; UINT16 McuCount; INT16 McuSrcBuff[10*64]; INT16 McuDstBuff[12*64]; UINT16 CurHPixel; UINT16 CurVLine; UINT64 BltBufferSize; if (ImageData == NULL || ImageDataSize == 0 || DecodedDataSize == NULL || Height == NULL || Width == NULL || DecoderStatus == NULL) { return EFI_INVALID_PARAMETER; } *DecoderStatus = InitJfifData (ImageData, ImageDataSize); if (*DecoderStatus != EFI_JPEG_DECODE_SUCCESS) { return EFI_UNSUPPORTED; } *Width = mJfifData.Sof0Data.Width; *Height = mJfifData.Sof0Data.Height; BltBufferSize = MultU64x32 ((UINT64) (mJfifData.Sof0Data.Width * mJfifData.Sof0Data.Height), sizeof (EFI_UGA_PIXEL)); if (BltBufferSize >= 0x100000000LL) { return EFI_UNSUPPORTED; } *DecodedDataSize = (UINTN) BltBufferSize; *DecodedData = H2OImageDecoderLibAllocateMem (*DecodedDataSize); if (*DecodedData == NULL) { return EFI_OUT_OF_RESOURCES; } ZeroMem (*DecodedData, *DecodedDataSize); CurHPixel = 0; CurVLine = 0; IntervalFlag = FALSE; IsEnd = FALSE; McuCount = 0; InitDecoderData(); while (!IsEnd) { SetMem(&McuDstBuff, sizeof(McuDstBuff), 0); if (McuRetrieve((INT16 *)&McuSrcBuff, IntervalFlag, &IsEnd, DecoderStatus) != EFI_SUCCESS) { break; } McuCount++; if ((mJfifData.McuRestart)&&(McuCount % mJfifData.McuRestart==0)) { IntervalFlag = TRUE; } else { IntervalFlag = FALSE; } McuDecode ((INT16 *)&McuSrcBuff, (INT16 *)&McuDstBuff); StoreMcuToUgaBlt ((INT16 *)&McuDstBuff, DecodedData, &CurHPixel, &CurVLine); } return EFI_SUCCESS; } /** Get a special Marker info of the JFIF image @param [in] Start The start of the JFIF image, or the output Next parameter from a previous call. @param [in] End The end of the JFIF image. @param [in, out] MarkerType The type of the marker in the JFIF image @param [out] MarkerData The pointer of the marker specified by the special MarkerType in the JFIF image. @param [out] DataSize The size in bytes of MarkerData (with the marker bytes and length bytes). @param [out] Next The next pointer following the "MarkerType" marker, it is next marker pointer, or the compressed data pointer after SOS marker. @retval EFI_SUCCESS The marker's information is retrieved sucessfully. @retval EFI_INVALID_PARAMETER Either one of Start, End and DataSize is NULL, or End is less than Start. @retval EFI_NOT_FOUND The marker can not be found in the JFIF image. **/ EFI_STATUS EFIAPI H2OHiiJpegDecoderGetMarkerData ( IN UINT8 *Start, IN UINT8 *End, IN OUT EFI_JPEG_MARKER_TYPE *MarkerType, OUT UINT8 **MarkerData, OUT UINT32 *DataSize, OUT UINT8 **Next OPTIONAL ) { UINT8 *ImagePtr; UINT16 i; i = 0; ImagePtr = Start; if (Start == NULL || End == NULL || End < Start || DataSize == NULL) { return EFI_INVALID_PARAMETER; } while (ImagePtr < End) { // // The ImagePtr is not a Marker's pointer // if (*ImagePtr != 0xFF || (*ImagePtr == 0xFF && *(ImagePtr + 1) == 0x00)) { ImagePtr++; continue; } if (*(ImagePtr + 2) == 0xFF) { // // The ImagePtr is a marker's pointer, and this marker does not have data info // *DataSize = 2; } else { *DataSize = (*(ImagePtr + 2) << 8) + *(ImagePtr + 3) + 2; } if (*MarkerType == JPEG_ANY) { // //Find the first marker following the Start pointer which has data info. // *MarkerType = *(ImagePtr + 1); *MarkerData = ImagePtr; if (Next != NULL) { *Next = ImagePtr + *DataSize; } return EFI_SUCCESS; } else if ( *(ImagePtr + 1) == *MarkerType) { // // The ImagePtr is a marker's pointer, and this marker have info // *MarkerData = ImagePtr; if (Next != NULL) { *Next = ImagePtr + *DataSize; } return EFI_SUCCESS; } else { ImagePtr += *DataSize; } } *MarkerData = NULL; *DataSize = 0; if (Next != NULL) { *Next = NULL; } return EFI_NOT_FOUND; }