From bb3715b6283f0d828be92065016d7b65d8671088 Mon Sep 17 00:00:00 2001 From: Caolán McNamara Date: Thu, 30 Mar 2017 21:10:46 +0100 Subject: ofz#967 optimize tiff import if line is the same as previous line Change-Id: Ided61679a579a73481320f85c05b03e3ce3d762a --- filter/qa/cppunit/data/tiff/pass/multi-page-1.tiff | Bin 0 -> 1202 bytes filter/source/graphicfilter/itiff/ccidecom.cxx | 37 +++++++++++---------- filter/source/graphicfilter/itiff/ccidecom.hxx | 18 +++++++--- filter/source/graphicfilter/itiff/itiff.cxx | 22 ++++++++++-- 4 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 filter/qa/cppunit/data/tiff/pass/multi-page-1.tiff (limited to 'filter') diff --git a/filter/qa/cppunit/data/tiff/pass/multi-page-1.tiff b/filter/qa/cppunit/data/tiff/pass/multi-page-1.tiff new file mode 100644 index 000000000000..8eb7c8078054 Binary files /dev/null and b/filter/qa/cppunit/data/tiff/pass/multi-page-1.tiff differ diff --git a/filter/source/graphicfilter/itiff/ccidecom.cxx b/filter/source/graphicfilter/itiff/ccidecom.cxx index b5f3ab4cf529..a7d0ffc49f14 100644 --- a/filter/source/graphicfilter/itiff/ccidecom.cxx +++ b/filter/source/graphicfilter/itiff/ccidecom.cxx @@ -624,18 +624,17 @@ void CCIDecompressor::StartDecompression( SvStream & rIStream ) return; } - -bool CCIDecompressor::DecompressScanline( sal_uInt8 * pTarget, sal_uLong nTargetBits, bool bLastLine ) +DecompressStatus CCIDecompressor::DecompressScanline( sal_uInt8 * pTarget, sal_uLong nTargetBits, bool bLastLine ) { //Read[1|2]DScanlineData take a sal_uInt16, so its either limit here or expand there if (nTargetBits > SAL_MAX_UINT16) - return false; + return DecompressStatus(false, true); if ( nEOLCount >= 5 ) // RTC (Return To Controller) - return true; + return DecompressStatus(true, true); if ( !bStatus ) - return false; + return DecompressStatus(false, true); // If EOL-Codes exist, the EOL-Code also appeared in front of the first line. // (and I thought it means 'End of Line'...) @@ -660,13 +659,13 @@ bool CCIDecompressor::DecompressScanline( sal_uInt8 * pTarget, sal_uLong nTarget { if ( !ReadEOL( nTargetBits ) ) { - return bStatus; + return DecompressStatus(bStatus, true); } } } if ( nEOLCount >= 5 ) // RTC (Return To Controller) - return true; + return DecompressStatus(true, true); // should the situation arise, generate a white previous line for 2D: if ( nOptions & CCI_OPTION_2D ) @@ -696,11 +695,12 @@ bool CCIDecompressor::DecompressScanline( sal_uInt8 * pTarget, sal_uLong nTarget else b2D = false; + bool bUnchanged; // read scanline: if ( b2D ) - Read2DScanlineData( pTarget, (sal_uInt16)nTargetBits ); + bUnchanged = Read2DScanlineData(pTarget, nTargetBits); else - Read1DScanlineData( pTarget, (sal_uInt16)nTargetBits ); + bUnchanged = Read1DScanlineData(pTarget, nTargetBits); // if we're in 2D mode we have to remember the line: if ( nOptions & CCI_OPTION_2D && bStatus ) @@ -717,7 +717,7 @@ bool CCIDecompressor::DecompressScanline( sal_uInt8 * pTarget, sal_uLong nTarget if ( pIStream->GetError() ) bStatus = false; - return bStatus; + return DecompressStatus(bStatus, bUnchanged); } @@ -923,8 +923,9 @@ sal_uInt16 CCIDecompressor::CountBits(const sal_uInt8 * pData, sal_uInt16 nDataS return nPos-nBitPos; } -void CCIDecompressor::Read1DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTargetBits) +bool CCIDecompressor::Read1DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nBitsToRead) { + sal_uInt16 nTargetBits = nBitsToRead; sal_uInt16 nCode,nCodeBits,nDataBits,nTgtFreeByteBits; sal_uInt8 nByte; sal_uInt8 nBlackOrWhite; // is 0xff for black or 0x00 for white @@ -962,11 +963,11 @@ void CCIDecompressor::Read1DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTarget // is that an invalid code? if ( nDataBits == 9999 ) { - return; + return nTargetBits == nBitsToRead; } if ( nCodeBits == 0 ) { - return; // could be filling bits now + return nTargetBits == nBitsToRead; // could be filling bits now } nEOLCount = 0; // too much data? @@ -1017,10 +1018,11 @@ void CCIDecompressor::Read1DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTarget if (bTerminatingCode) nBlackOrWhite = ~nBlackOrWhite; } while (nTargetBits>0 || !bTerminatingCode); -} + return nTargetBits == nBitsToRead; +} -void CCIDecompressor::Read2DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTargetBits) +bool CCIDecompressor::Read2DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTargetBits) { sal_uInt16 n2DMode,nBitPos,nUncomp,nRun,nRun2,nt; sal_uInt8 nBlackOrWhite; @@ -1032,7 +1034,7 @@ void CCIDecompressor::Read2DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTarget n2DMode=ReadCodeAndDecode(p2DModeLookUp,10); if (!bStatus) - return; + return nBitPos == 0; if (n2DMode==CCI2DMODE_UNCOMP) { for (;;) { @@ -1114,7 +1116,8 @@ void CCIDecompressor::Read2DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTarget nBlackOrWhite=~nBlackOrWhite; } } -} + return nBitPos == 0; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/graphicfilter/itiff/ccidecom.hxx b/filter/source/graphicfilter/itiff/ccidecom.hxx index c92769df6576..02c7a16acf8a 100644 --- a/filter/source/graphicfilter/itiff/ccidecom.hxx +++ b/filter/source/graphicfilter/itiff/ccidecom.hxx @@ -45,6 +45,16 @@ struct CCILookUpTableEntry { class SvStream; +struct DecompressStatus +{ + bool m_bSuccess; + bool m_bBufferUnchanged; + DecompressStatus(bool bSuccess, bool bBufferUnchanged) + : m_bSuccess(bSuccess), m_bBufferUnchanged(bBufferUnchanged) + { + } +}; + class CCIDecompressor { public: @@ -54,7 +64,7 @@ public: void StartDecompression( SvStream & rIStream ); - bool DecompressScanline(sal_uInt8 * pTarget, sal_uLong nTargetBits, bool bLastLine ); + DecompressStatus DecompressScanline(sal_uInt8 * pTarget, sal_uLong nTargetBits, bool bLastLine); private: @@ -80,9 +90,9 @@ private: static sal_uInt16 CountBits(const sal_uInt8 * pData, sal_uInt16 nDataSizeBits, sal_uInt16 nBitPos, sal_uInt8 nBlackOrWhite); - void Read1DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTargetBits); - - void Read2DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTargetBits); + //returns true if pTarget was unmodified + bool Read1DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTargetBits); + bool Read2DScanlineData(sal_uInt8 * pTarget, sal_uInt16 nTargetBits); bool bTableBad; diff --git a/filter/source/graphicfilter/itiff/itiff.cxx b/filter/source/graphicfilter/itiff/itiff.cxx index 560056c6fa85..6fd5448e55a6 100644 --- a/filter/source/graphicfilter/itiff/itiff.cxx +++ b/filter/source/graphicfilter/itiff/itiff.cxx @@ -598,8 +598,10 @@ bool TIFFReader::ReadMap() aCCIDecom.StartDecompression( *pTIFF ); + bool bDifferentToPrev; for (sal_Int32 ny = 0; ny < nImageLength; ++ny) { + bDifferentToPrev = ny == 0; for (sal_uLong np = 0; np < nPlanes; np++ ) { if ( ny / GetRowsPerStrip() + np * nStripsPerPlane > nStrip ) @@ -615,13 +617,27 @@ bool TIFFReader::ReadMap() } if (np >= SAL_N_ELEMENTS(pMap)) return false; - if ( !aCCIDecom.DecompressScanline( pMap[ np ], nImageWidth * nBitsPerSample * nSamplesPerPixel / nPlanes, np + 1 == nPlanes ) ) + DecompressStatus aResult = aCCIDecom.DecompressScanline(pMap[np],nImageWidth * nBitsPerSample * nSamplesPerPixel / nPlanes, np + 1 == nPlanes); + if (!aResult.m_bSuccess) return false; + bDifferentToPrev |= !aResult.m_bBufferUnchanged; if ( pTIFF->GetError() ) return false; } - if ( !ConvertScanline( ny ) ) - return false; + if (!bDifferentToPrev) + { + //if the buffer for this line didn't change, then just copy the + //previous scanline instead of painfully decoding and setting + //each pixel one by one again + pAcc->CopyScanline(ny, pAcc->GetScanline(ny-1), + pAcc->GetScanlineFormat(), + pAcc->GetScanlineSize()); + } + else + { + if (!ConvertScanline(ny)) + return false; + } } } else if ( nCompression == 5 ) -- cgit