20#include <osl/endian.h>
30void lclReadStream(png_structp pPng, png_bytep pOutBytes, png_size_t nBytesToRead)
32 png_voidp pIO = png_get_io_ptr(pPng);
39 sal_Size nBytesRead = pStream->
ReadBytes(pOutBytes, nBytesToRead);
41 if (nBytesRead != nBytesToRead)
44 png_error(pPng,
"Error reading");
48 memset(pOutBytes + nBytesRead, 0, nBytesToRead - nBytesRead);
49 png_warning(pPng,
"Short read");
65 ~PngDestructor() { png_destroy_read_struct(&pPng, &pInfo,
nullptr); }
73 sal_uInt32 num_frames;
80 sal_uInt32 sequence_number;
81 virtual ~FrameDataChunk() =
default;
85struct fcTLChunk :
public FrameDataChunk
98struct fdATChunk :
public FrameDataChunk
100 std::vector<sal_uInt8> frame_data;
106 bool mbIsApng =
false;
107 acTLChunk maACTLChunk;
108 std::vector<std::unique_ptr<FrameDataChunk>> maFrameData;
111int handle_unknown_chunk(png_structp png, png_unknown_chunkp chunk)
113 std::string
sName(chunk->name, chunk->name + 4);
114 APNGInfo* aAPNGInfo =
static_cast<APNGInfo*
>(png_get_user_chunk_ptr(png));
117 if (chunk->size <
sizeof(acTLChunk))
119 aAPNGInfo->maACTLChunk = *
reinterpret_cast<acTLChunk*
>(chunk->data);
120 aAPNGInfo->maACTLChunk.num_frames = OSL_SWAPDWORD(aAPNGInfo->maACTLChunk.num_frames);
121 aAPNGInfo->maACTLChunk.num_plays = OSL_SWAPDWORD(aAPNGInfo->maACTLChunk.num_plays);
122 aAPNGInfo->mbIsApng =
true;
126 std::unique_ptr<FrameDataChunk> pBaseChunk;
131 if (chunk->size != 26)
148 std::unique_ptr<fcTLChunk> aChunk = std::make_unique<fcTLChunk>();
149 std::memcpy(&aChunk->width, chunk->data + 4, 4);
150 std::memcpy(&aChunk->height, chunk->data + 8, 4);
151 std::memcpy(&aChunk->x_offset, chunk->data + 12, 4);
152 std::memcpy(&aChunk->y_offset, chunk->data + 16, 4);
153 std::memcpy(&aChunk->delay_num, chunk->data + 20, 2);
154 std::memcpy(&aChunk->delay_den, chunk->data + 22, 2);
155 std::memcpy(&aChunk->dispose_op, chunk->data + 24, 1);
156 std::memcpy(&aChunk->blend_op, chunk->data + 25, 1);
157 aChunk->width = OSL_SWAPDWORD(aChunk->width);
158 aChunk->height = OSL_SWAPDWORD(aChunk->height);
159 aChunk->x_offset = OSL_SWAPDWORD(aChunk->x_offset);
160 aChunk->y_offset = OSL_SWAPDWORD(aChunk->y_offset);
161 aChunk->delay_num = OSL_SWAPWORD(aChunk->delay_num);
162 aChunk->delay_den = OSL_SWAPWORD(aChunk->delay_den);
163 pBaseChunk = std::move(aChunk);
165 else if (sName ==
"fdAT")
167 size_t nDataSize = chunk->size;
171 std::unique_ptr<fdATChunk> aChunk = std::make_unique<fdATChunk>();
172 aChunk->frame_data.resize(nDataSize);
175 std::memcpy(aChunk->frame_data.data(), &nIDATSwapped, 4);
177 std::memcpy(aChunk->frame_data.data() + 4, chunk->data + 4, nDataSize - 4);
178 pBaseChunk = std::move(aChunk);
186 sal_uInt32 nSequenceNumber = 0;
187 std::memcpy(&nSequenceNumber, chunk->data, 4);
188 nSequenceNumber = OSL_SWAPDWORD(nSequenceNumber);
190 pBaseChunk->sequence_number = nSequenceNumber;
191 if (pBaseChunk->sequence_number < aAPNGInfo->maFrameData.size())
195 aAPNGInfo->maFrameData.insert(aAPNGInfo->maFrameData.begin()
196 + pBaseChunk->sequence_number,
197 std::move(pBaseChunk));
201 aAPNGInfo->maFrameData.push_back(std::move(pBaseChunk));
208void getImportantChunks(
SvStream& rInStream,
SvStream& rOutStream, sal_uInt32 nWidth,
212 sal_uInt32 nChunkSize, nChunkType;
213 rInStream.
SetEndian(SvStreamEndian::BIG);
214 rOutStream.
SetEndian(SvStreamEndian::BIG);
221 sal_uInt32 nIHDRData1;
229 rOutStream.
ReadBytes(aIHDRData.data(), aIHDRData.size());
230 rOutStream.
WriteUInt32(rtl_crc32(0, aIHDRData.data(), aIHDRData.size()));
233 while (rInStream.
good())
237 bool bBreakOuter =
false;
273 rInStream.
Seek(nPos);
276sal_uInt32 NumDenToTime(sal_uInt16 nNumerator, sal_uInt16 nDenominator)
278 if (nDenominator == 0)
280 return (
static_cast<double>(nNumerator) / nDenominator) * 100;
283bool fcTLbeforeIDAT(
SvStream& rStream)
290 sal_uInt32 nChunkSize, nChunkType;
291 while (rStream.
good())
311#if defined __GNUC__ && __GNUC__ == 8 && !defined __clang__
312#pragma GCC diagnostic push
313#pragma GCC diagnostic ignored "-Wclobbered"
323 png_structp pPng = png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
328 png_set_read_user_chunk_fn(pPng, &aAPNGInfo, &handle_unknown_chunk);
330 png_infop pInfo = png_create_info_struct(pPng);
333 png_destroy_read_struct(&pPng,
nullptr,
nullptr);
337 PngDestructor pngDestructor = { pPng, pInfo };
350 const bool bOnlyCreateBitmap
352 const bool bUseExistingBitmap
355 if (setjmp(png_jmpbuf(pPng)))
357 if (!bUseExistingBitmap)
361 pWriteAccessInstance.
reset();
362 pWriteAccessAlphaInstance.
reset();
364 aBitmapEx =
BitmapEx(aBitmap, aBitmapAlpha);
372 rGraphic = aBitmapEx;
377 png_set_option(pPng, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
379 png_set_read_fn(pPng, &rStream, lclReadStream);
382 png_set_crc_action(pPng, PNG_CRC_ERROR_QUIT, PNG_CRC_WARN_DISCARD);
384 png_set_crc_action(pPng, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
388 png_read_info(pPng, pInfo);
390 png_uint_32 width = 0;
391 png_uint_32 height = 0;
396 png_uint_32 returnValue = png_get_IHDR(pPng, pInfo, &width, &height, &bitDepth, &colorType,
397 &interlace,
nullptr,
nullptr);
399 if (returnValue != 1)
402 if (colorType == PNG_COLOR_TYPE_PALETTE)
403 png_set_palette_to_rgb(pPng);
405 if (colorType == PNG_COLOR_TYPE_GRAY)
406 png_set_expand_gray_1_2_4_to_8(pPng);
408 if (png_get_valid(pPng, pInfo, PNG_INFO_tRNS))
409 png_set_tRNS_to_alpha(pPng);
412 png_set_scale_16(pPng);
415 png_set_packing(pPng);
418 if (colorType == PNG_COLOR_TYPE_GRAY_ALPHA
419 || (colorType == PNG_COLOR_TYPE_GRAY && png_get_valid(pPng, pInfo, PNG_INFO_tRNS)))
421 png_set_gray_to_rgb(pPng);
427 int nNumberOfPasses = png_set_interlace_handling(pPng);
429 png_read_update_info(pPng, pInfo);
430 returnValue = png_get_IHDR(pPng, pInfo, &width, &height, &bitDepth, &colorType,
nullptr,
433 if (returnValue != 1)
437 || (colorType != PNG_COLOR_TYPE_RGB && colorType != PNG_COLOR_TYPE_RGB_ALPHA
438 && colorType != PNG_COLOR_TYPE_GRAY))
443 png_uint_32 res_x = 0;
444 png_uint_32 res_y = 0;
446 if (png_get_pHYs(pPng, pInfo, &res_x, &res_y, &unit_type) != 0
447 && unit_type == PNG_RESOLUTION_METER && res_x && res_y)
450 prefSize =
Size(
static_cast<sal_Int32
>((100000.0 * width) / res_x),
451 static_cast<sal_Int32
>((100000.0 * height) / res_y));
454 if (!bUseExistingBitmap)
458 case PNG_COLOR_TYPE_RGB:
461 case PNG_COLOR_TYPE_RGBA:
462 if (bSupportsBitmap32)
468 aBitmapAlpha.
Erase(0);
471 case PNG_COLOR_TYPE_GRAY:
479 if (bOnlyCreateBitmap)
482 aBitmapEx =
BitmapEx(aBitmap, aBitmapAlpha);
490 rGraphic = aBitmapEx;
495 if (!pWriteAccessInstance)
500 if (!pWriteAccessAlphaInstance)
506 = pAlphaAccess ? *pAlphaAccess : pWriteAccessAlphaInstance;
508 if (colorType == PNG_COLOR_TYPE_RGB)
514 for (
int pass = 0;
pass < nNumberOfPasses;
pass++)
516 for (png_uint_32 y = 0;
y < height;
y++)
518 Scanline pScanline = pWriteAccess->GetScanline(y);
519 png_read_row(pPng, pScanline,
nullptr);
523 else if (colorType == PNG_COLOR_TYPE_RGB_ALPHA)
525 size_t aRowSizeBytes = png_get_rowbytes(pPng, pInfo);
527 if (bSupportsBitmap32)
533 for (
int pass = 0;
pass < nNumberOfPasses;
pass++)
535 for (png_uint_32 y = 0;
y < height;
y++)
537 Scanline pScanline = pWriteAccess->GetScanline(y);
538 png_read_row(pPng, pScanline,
nullptr);
541#if !ENABLE_WASM_STRIP_PREMULTIPLY
546 for (png_uint_32 y = 0;
y < height;
y++)
548 Scanline pScanline = pWriteAccess->GetScanline(y);
549 for (
size_t i = 0;
i < aRowSizeBytes;
i += 4)
552#if ENABLE_WASM_STRIP_PREMULTIPLY
567 for (png_uint_32 y = 0;
y < height;
y++)
569 Scanline pScanline = pWriteAccess->GetScanline(y);
570 for (
size_t i = 0;
i < aRowSizeBytes;
i += 4)
573#if ENABLE_WASM_STRIP_PREMULTIPLY
592 if (nNumberOfPasses == 1)
595 std::vector<png_byte> aRow(aRowSizeBytes, 0);
596 for (png_uint_32 y = 0;
y < height;
y++)
598 Scanline pScanline = pWriteAccess->GetScanline(y);
599 Scanline pScanAlpha = pWriteAccessAlpha->GetScanline(y);
600 png_bytep pRow = aRow.data();
601 png_read_row(pPng, pRow,
nullptr);
604 for (
size_t i = 0;
i < aRowSizeBytes;
i += 4)
606 pScanline[iColor++] = pRow[
i + 0];
607 pScanline[iColor++] = pRow[
i + 1];
608 pScanline[iColor++] = pRow[
i + 2];
609 pScanAlpha[iAlpha++] = pRow[
i + 3];
615 std::vector<std::vector<png_byte>> aRows(height);
616 for (
auto& rRow : aRows)
617 rRow.resize(aRowSizeBytes, 0);
618 for (
int pass = 0;
pass < nNumberOfPasses;
pass++)
620 for (png_uint_32 y = 0;
y < height;
y++)
622 Scanline pScanline = pWriteAccess->GetScanline(y);
623 Scanline pScanAlpha = pWriteAccessAlpha->GetScanline(y);
624 png_bytep pRow = aRows[
y].data();
625 png_read_row(pPng, pRow,
nullptr);
628 for (
size_t i = 0;
i < aRowSizeBytes;
i += 4)
630 pScanline[iColor++] = pRow[
i + 0];
631 pScanline[iColor++] = pRow[
i + 1];
632 pScanline[iColor++] = pRow[
i + 2];
633 pScanAlpha[iAlpha++] = pRow[
i + 3];
640 else if (colorType == PNG_COLOR_TYPE_GRAY)
642 for (
int pass = 0;
pass < nNumberOfPasses;
pass++)
644 for (png_uint_32 y = 0;
y < height;
y++)
646 Scanline pScanline = pWriteAccess->GetScanline(y);
647 png_read_row(pPng, pScanline,
nullptr);
652 png_read_end(pPng, pInfo);
654 if (!bUseExistingBitmap)
656 pWriteAccess.
reset();
657 pWriteAccessAlpha.
reset();
659 aBitmapEx =
BitmapEx(aBitmap, aBitmapAlpha);
669 if (aAPNGInfo.mbIsApng)
674 bool bFctlBeforeIDAT = fcTLbeforeIDAT(rStream);
675 size_t nSequenceIndex =
static_cast<size_t>(bFctlBeforeIDAT);
677 = aAPNGInfo.maACTLChunk.num_frames -
static_cast<sal_uInt32
>(bFctlBeforeIDAT);
679 fcTLChunk* aFctlChunk =
dynamic_cast<fcTLChunk*
>(aAPNGInfo.maFrameData[0].get());
682 Size aCanvasSize(aFctlChunk->width, aFctlChunk->height);
684 aAnimation.
SetLoopCount(aAPNGInfo.maACTLChunk.num_plays);
687 Point aFirstPoint(0, 0);
688 auto aDisposal =
static_cast<Disposal>(aFctlChunk->dispose_op);
689 auto aBlend =
static_cast<Blend>(aFctlChunk->blend_op);
693 aBitmapEx, aFirstPoint, aCanvasSize,
694 NumDenToTime(aFctlChunk->delay_num, aFctlChunk->delay_den), aDisposal, aBlend);
695 aAnimation.
Insert(aAnimationFrame);
698 for (sal_uInt32 i = 0;
i < nFrames;
i++)
701 fcTLChunk* aFctlChunk
702 =
static_cast<fcTLChunk*
>(aAPNGInfo.maFrameData[nSequenceIndex++].get());
704 Blend aBlend =
static_cast<Blend>(aFctlChunk->blend_op);
708 getImportantChunks(rStream, aFrameStream, aFctlChunk->width, aFctlChunk->height);
710 while (fdATChunk* pFdatChunk
711 =
dynamic_cast<fdATChunk*
>(aAPNGInfo.maFrameData[nSequenceIndex].get()))
714 auto nDataSize = pFdatChunk->frame_data.size();
716 aFrameStream.
WriteBytes(pFdatChunk->frame_data.data(), nDataSize);
717 sal_uInt32 nCrc = rtl_crc32(0, pFdatChunk->frame_data.data(), nDataSize);
720 if (nSequenceIndex >= aAPNGInfo.maFrameData.size())
727 aFrameStream.
Seek(0);
728 bool bSuccess = reader(aFrameStream, aFrameGraphic);
732 Point aStartPoint(aFctlChunk->x_offset, aFctlChunk->y_offset);
733 Size aSize(aFctlChunk->width, aFctlChunk->height);
735 aFrameBitmapEx, aStartPoint, aSize,
736 NumDenToTime(aFctlChunk->delay_num, aFctlChunk->delay_den), aDisposal, aBlend);
737 aAnimation.
Insert(aAnimationFrame);
739 rGraphic = aAnimation;
744 rGraphic = aBitmapEx;
766 constexpr sal_uInt32 PNGCHUNK_msOG = 0x6d734f47;
767 constexpr sal_uInt64 MSGifHeaderSize = 11;
768 if (type == PNGCHUNK_msOG && length > MSGifHeaderSize)
771 sal_uInt32 typeForCrc =
type;
772#if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
773 typeForCrc = OSL_SWAPDWORD(typeForCrc);
775 sal_uInt32 computedCrc = rtl_crc32(0, &typeForCrc, 4);
776 const sal_uInt64
pos = rStream.
Tell();
777 if (pos + length >= rStream.
TellEnd())
780 char msHeader[MSGifHeaderSize];
781 if (rStream.
ReadBytes(msHeader, MSGifHeaderSize) != MSGifHeaderSize)
783 computedCrc = rtl_crc32(computedCrc, msHeader, MSGifHeaderSize);
784 length -= MSGifHeaderSize;
789 computedCrc = rtl_crc32(computedCrc, chunk.getData(), chunk.getSize());
791 if (!ignoreCrc && crc != computedCrc)
803#if defined __GNUC__ && __GNUC__ == 8 && !defined __clang__
804#pragma GCC diagnostic pop
819 bool bRet = reader(
mrStream, aGraphic);
835 sal_uInt64 originalPosition = rStream.
Tell();
838 auto chunk = getMsGifChunk(rStream);
840 rStream.
Seek(originalPosition);
849 if (reader(
rInputStream, aGraphic, nImportFlags, pAccess, pAlphaAccess))
860 auto nStmPos = rStream.
Tell();
862 rStream.
Seek(nStmPos);
863 rStream.
SetEndian(SvStreamEndian::LITTLE);
868 sal_uInt32 nChunkSize, nChunkType;
vcl::ScopedBitmapAccess< BitmapWriteAccess, AlphaMask, &AlphaMask::AcquireAlphaWriteAccess > AlphaScopedWriteAccess
vcl::ScopedBitmapAccess< BitmapWriteAccess, Bitmap, &Bitmap::AcquireWriteAccess > BitmapScopedWriteAccess
constexpr sal_uInt32 PNG_FDAT_SIGNATURE
constexpr int PNG_SIZE_SIZE
constexpr sal_uInt32 PNG_FCTL_SIGNATURE
constexpr int PNG_TYPE_SIZE
constexpr sal_uInt32 PNG_IHDR_SIGNATURE
constexpr sal_uInt32 PNG_ACTL_SIGNATURE
constexpr int PNG_IEND_SIZE
constexpr sal_uInt32 PNG_IEND_SIGNATURE
constexpr sal_uInt32 PNG_IEND_CRC
constexpr sal_uInt64 PNG_SIGNATURE
constexpr sal_uInt32 PNG_IDAT_SIGNATURE
constexpr int PNG_SIGNATURE_SIZE
constexpr int PNG_CRC_SIZE
constexpr int PNG_IHDR_SIZE
void Erase(sal_uInt8 cTransparency)
void SetLoopCount(const sal_uInt32 nLoopCount)
void SetDisplaySizePixel(const Size &rSize)
bool Insert(const AnimationFrame &rAnimationFrame)
Container for the binary data, whose responsibility is to manage the make it as simple as possible to...
void SetPrefMapMode(const MapMode &rPrefMapMode)
void SetPrefSize(const Size &rPrefSize)
static const BitmapPalette & GetGreyPalette(int nEntries)
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
bool supportsBitmap32() const
void SetEndian(SvStreamEndian SvStreamEndian)
virtual sal_uInt64 TellEnd()
std::size_t WriteBytes(const void *pData, std::size_t nSize)
SvStream & WriteUChar(unsigned char nChar)
SvStream & WriteUInt64(sal_uInt64 nuInt64)
SvStream & WriteUInt32(sal_uInt32 nUInt32)
SvStream & ReadUInt32(sal_uInt32 &rUInt32)
SvStreamEndian GetEndian() const
sal_uInt64 Seek(sal_uInt64 nPos)
std::size_t ReadBytes(void *pData, std::size_t nSize)
sal_uInt64 SeekRel(sal_Int64 nPos)
sal_uInt64 remainingSize()
SvStream & ReadUChar(unsigned char &rChar)
PngImageReader(SvStream &rStream)
static BinaryDataContainer getMicrosoftGifChunk(SvStream &rStream)
static bool isAPng(SvStream &rStream)
This template handles BitmapAccess the RAII way.
Reference< XInputStream > rInputStream
@ UseExistingBitmap
Read pixel data into an existing bitmap.
@ OnlyCreateBitmap
Only create a bitmap, do not read pixel data.
constexpr OUStringLiteral aData
constexpr double alpha[nDetails]
sal_uInt8 premultiply(sal_uInt8 c, sal_uInt8 a)
lookup_table const & get_premultiply_table()
std::array< std::array< sal_uInt8, 256 >, 256 > lookup_table
bool ImportPNG(SvStream &rInputStream, Graphic &rGraphic, GraphicFilterImportFlags nImportFlags, BitmapScopedWriteAccess *pAccess, AlphaScopedWriteAccess *pAlphaAccess)
TOOLS_DLLPUBLIC bool checkSeek(SvStream &rSt, sal_uInt64 nOffset)
ImplSVData * ImplGetSVData()