From ef3dabd1f814d1b005efc5d5144978c1d26a8e73 Mon Sep 17 00:00:00 2001 From: Luke Deller Date: Thu, 31 Oct 2019 01:36:22 +1100 Subject: Fix IsTransparent() for unloaded graphics Fix Graphic::IsAlpha() and Graphic::IsTransparent() for unloaded graphics. This fixes tdf#118036. GraphicDescriptor::Detect(true) is currently used to read the image size from the header of images which are not being fully loaded yet. This change extends GraphicDescriptor to also report whether the image supports transparency or alpha, implemented only for PNG format so far. Change-Id: I1753c0d11491f1dc518e23da8d7b3842945770cb Reviewed-on: https://gerrit.libreoffice.org/81785 Tested-by: Jenkins Reviewed-by: Miklos Vajna --- include/vcl/graphicfilter.hxx | 8 ++++ vcl/qa/cppunit/GraphicTest.cxx | 55 ++++++++++++++++++++++----- vcl/source/filter/graphicfilter2.cxx | 72 +++++++++++++++++++++--------------- vcl/source/gdi/impgraph.cxx | 16 ++++---- 4 files changed, 106 insertions(+), 45 deletions(-) diff --git a/include/vcl/graphicfilter.hxx b/include/vcl/graphicfilter.hxx index eea2e9ac8b7d..45fe8e261f9e 100644 --- a/include/vcl/graphicfilter.hxx +++ b/include/vcl/graphicfilter.hxx @@ -145,6 +145,8 @@ class VCL_DLLPUBLIC GraphicDescriptor final GraphicFileFormat nFormat; bool const bOwnStream; sal_uInt8 mnNumberOfImageComponents; + bool bIsTransparent; + bool bIsAlpha; void ImpConstruct(); @@ -214,6 +216,12 @@ public: /** @return number of color channels */ sal_uInt8 GetNumberOfImageComponents() const { return mnNumberOfImageComponents; } + /** @return whether image supports transparency */ + bool IsTransparent() const { return bIsTransparent; } + + /** @return whether image supports alpha values for translucent colours */ + bool IsAlpha() const { return bIsAlpha; } + /** @return filter number that is needed by the GraphFilter to read this format */ static OUString GetImportFormatShortName( GraphicFileFormat nFormat ); }; diff --git a/vcl/qa/cppunit/GraphicTest.cxx b/vcl/qa/cppunit/GraphicTest.cxx index af5985b01f99..56e05c8bf3de 100644 --- a/vcl/qa/cppunit/GraphicTest.cxx +++ b/vcl/qa/cppunit/GraphicTest.cxx @@ -28,25 +28,40 @@ class GraphicTest : public CppUnit::TestFixture void testUnloadedGraphic(); void testUnloadedGraphicLoading(); void testUnloadedGraphicWmf(); + void testUnloadedGraphicAlpha(); CPPUNIT_TEST_SUITE(GraphicTest); CPPUNIT_TEST(testUnloadedGraphic); CPPUNIT_TEST(testUnloadedGraphicLoading); CPPUNIT_TEST(testUnloadedGraphicWmf); + CPPUNIT_TEST(testUnloadedGraphicAlpha); CPPUNIT_TEST_SUITE_END(); }; -BitmapEx createBitmap() +BitmapEx createBitmap(bool alpha = false) { - Bitmap aBitmap(Size(100, 100), 24); + Bitmap aBitmap(Size(120, 100), 24); aBitmap.Erase(COL_LIGHTRED); - return BitmapEx(aBitmap); + aBitmap.SetPrefSize(Size(6000, 5000)); + aBitmap.SetPrefMapMode(MapMode(MapUnit::Map100thMM)); + + if (alpha) + { + sal_uInt8 uAlphaValue = 0x80; + AlphaMask aAlphaMask(Size(120, 100), &uAlphaValue); + + return BitmapEx(aBitmap, aAlphaMask); + } + else + { + return BitmapEx(aBitmap); + } } -void createBitmapAndExportForType(SvStream& rStream, OUString const& sType) +void createBitmapAndExportForType(SvStream& rStream, OUString const& sType, bool alpha) { - BitmapEx aBitmapEx = createBitmap(); + BitmapEx aBitmapEx = createBitmap(alpha); uno::Sequence aFilterData; GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); @@ -56,11 +71,11 @@ void createBitmapAndExportForType(SvStream& rStream, OUString const& sType) rStream.Seek(STREAM_SEEK_TO_BEGIN); } -Graphic makeUnloadedGraphic(OUString const& sType) +Graphic makeUnloadedGraphic(OUString const& sType, bool alpha = false) { SvMemoryStream aStream; GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); - createBitmapAndExportForType(aStream, sType); + createBitmapAndExportForType(aStream, sType, alpha); return rGraphicFilter.ImportUnloadedGraphic(aStream); } @@ -81,10 +96,15 @@ void GraphicTest::testUnloadedGraphic() // check GetSizePixel doesn't load graphic aGraphic = makeUnloadedGraphic("png"); CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); - CPPUNIT_ASSERT_EQUAL(100L, aGraphic.GetSizePixel().Width()); + CPPUNIT_ASSERT_EQUAL(120L, aGraphic.GetSizePixel().Width()); CPPUNIT_ASSERT_EQUAL(100L, aGraphic.GetSizePixel().Height()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); + // check GetPrefSize doesn't load graphic + CPPUNIT_ASSERT_EQUAL(6000L, aGraphic.GetPrefSize().Width()); + CPPUNIT_ASSERT_EQUAL(5000L, aGraphic.GetPrefSize().Height()); + CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); + // check GetSizeBytes loads graphic CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); CPPUNIT_ASSERT(aGraphic.GetSizeBytes() > 0); @@ -101,7 +121,7 @@ void GraphicTest::testUnloadedGraphicLoading() // check available CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); - CPPUNIT_ASSERT_EQUAL(100L, aGraphic.GetSizePixel().Width()); + CPPUNIT_ASSERT_EQUAL(120L, aGraphic.GetSizePixel().Width()); CPPUNIT_ASSERT_EQUAL(100L, aGraphic.GetSizePixel().Height()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); CPPUNIT_ASSERT(aGraphic.GetSizeBytes() > 0); @@ -157,6 +177,23 @@ void GraphicTest::testUnloadedGraphicWmf() CPPUNIT_ASSERT_EQUAL(Size(42, 42), aGraphic.GetPrefSize()); } +void GraphicTest::testUnloadedGraphicAlpha() +{ + // make unloaded test graphic with alpha + Graphic aGraphic = makeUnloadedGraphic("png", true); + + CPPUNIT_ASSERT_EQUAL(true, aGraphic.IsAlpha()); + CPPUNIT_ASSERT_EQUAL(true, aGraphic.IsTransparent()); + CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); + + // make unloaded test graphic without alpha + aGraphic = makeUnloadedGraphic("png", false); + + CPPUNIT_ASSERT_EQUAL(false, aGraphic.IsAlpha()); + CPPUNIT_ASSERT_EQUAL(false, aGraphic.IsTransparent()); + CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); +} + } // namespace CPPUNIT_TEST_SUITE_REGISTRATION(GraphicTest); diff --git a/vcl/source/filter/graphicfilter2.cxx b/vcl/source/filter/graphicfilter2.cxx index 03e1ac228067..5640e7c96ed2 100644 --- a/vcl/source/filter/graphicfilter2.cxx +++ b/vcl/source/filter/graphicfilter2.cxx @@ -99,6 +99,8 @@ void GraphicDescriptor::ImpConstruct() nBitsPerPixel = 0; nPlanes = 0; mnNumberOfImageComponents = 0; + bIsTransparent = false; + bIsAlpha = false; } bool GraphicDescriptor::ImpDetectBMP( SvStream& rStm, bool bExtendedInfo ) @@ -548,6 +550,11 @@ bool GraphicDescriptor::ImpDetectPNG( SvStream& rStm, bool bExtendedInfo ) rStm.ReadUChar( cByte ); nBitsPerPixel = cByte; + // Colour type - check whether it supports alpha values + sal_uInt8 cColType = 0; + rStm.ReadUChar( cColType ); + bIsAlpha = bIsTransparent = ( cColType == 4 || cColType == 6 ); + // Planes always 1; // compression always nPlanes = 1; @@ -555,46 +562,53 @@ bool GraphicDescriptor::ImpDetectPNG( SvStream& rStm, bool bExtendedInfo ) sal_uInt32 nLen32 = 0; nTemp32 = 0; - rStm.SeekRel( 8 ); + rStm.SeekRel( 7 ); - // read up to the pHYs-Chunk or the start of the image + // read up to the start of the image rStm.ReadUInt32( nLen32 ); rStm.ReadUInt32( nTemp32 ); - while( ( nTemp32 != 0x70485973 ) && ( nTemp32 != 0x49444154 ) - && rStm.good() ) + while( ( nTemp32 != 0x49444154 ) && rStm.good() ) { - rStm.SeekRel( 4 + nLen32 ); - rStm.ReadUInt32( nLen32 ); - rStm.ReadUInt32( nTemp32 ); - } + if ( nTemp32 == 0x70485973 ) // physical pixel dimensions + { + sal_uLong nXRes; + sal_uLong nYRes; - if (nTemp32 == 0x70485973 && rStm.good()) - { - sal_uLong nXRes; - sal_uLong nYRes; + // horizontal resolution + nTemp32 = 0; + rStm.ReadUInt32( nTemp32 ); + nXRes = nTemp32; - // horizontal resolution - nTemp32 = 0; - rStm.ReadUInt32( nTemp32 ); - nXRes = nTemp32; + // vertical resolution + nTemp32 = 0; + rStm.ReadUInt32( nTemp32 ); + nYRes = nTemp32; - // vertical resolution - nTemp32 = 0; - rStm.ReadUInt32( nTemp32 ); - nYRes = nTemp32; + // unit + cByte = 0; + rStm.ReadUChar( cByte ); - // unit - cByte = 0; - rStm.ReadUChar( cByte ); + if ( cByte ) + { + if ( nXRes ) + aLogSize.setWidth( (aPixSize.Width() * 100000) / nXRes ); - if ( cByte ) - { - if ( nXRes ) - aLogSize.setWidth( (aPixSize.Width() * 100000) / nXRes ); + if ( nYRes ) + aLogSize.setHeight( (aPixSize.Height() * 100000) / nYRes ); + } - if ( nYRes ) - aLogSize.setHeight( (aPixSize.Height() * 100000) / nYRes ); + nLen32 -= 9; + } + else if ( nTemp32 == 0x74524e53 ) // transparency + { + bIsTransparent = true; + bIsAlpha = ( cColType != 0 && cColType != 2 ); } + + // skip forward to next chunk + rStm.SeekRel( 4 + nLen32 ); + rStm.ReadUInt32( nLen32 ); + rStm.ReadUInt32( nTemp32 ); } } } diff --git a/vcl/source/gdi/impgraph.cxx b/vcl/source/gdi/impgraph.cxx index 0a215fede7b0..17c371e6d77f 100644 --- a/vcl/source/gdi/impgraph.cxx +++ b/vcl/source/gdi/impgraph.cxx @@ -545,10 +545,11 @@ void ImpGraphic::ImplSetPrepared(bool bAnimated, const Size* pSizeHint) maSwapInfo.maPrefSize = *pSizeHint; maSwapInfo.maPrefMapMode = MapMode(MapUnit::Map100thMM); } - else + + GraphicDescriptor aDescriptor(aMemoryStream, nullptr); + if (aDescriptor.Detect(true)) { - GraphicDescriptor aDescriptor(aMemoryStream, nullptr); - if (aDescriptor.Detect(true)) + if (!pSizeHint) { // If we have logic size, work with that, as later pixel -> logic // conversion will work with the output device DPI, not the graphic @@ -564,14 +565,15 @@ void ImpGraphic::ImplSetPrepared(bool bAnimated, const Size* pSizeHint) maSwapInfo.maPrefSize = aDescriptor.GetSizePixel(); maSwapInfo.maPrefMapMode = MapMode(MapUnit::MapPixel); } - - maSwapInfo.maSizePixel = aDescriptor.GetSizePixel(); } + + maSwapInfo.maSizePixel = aDescriptor.GetSizePixel(); + maSwapInfo.mbIsTransparent = aDescriptor.IsTransparent(); + maSwapInfo.mbIsAlpha = aDescriptor.IsAlpha(); } + maSwapInfo.mnAnimationLoopCount = 0; maSwapInfo.mbIsEPS = false; - maSwapInfo.mbIsTransparent = false; - maSwapInfo.mbIsAlpha = false; maSwapInfo.mbIsAnimated = bAnimated; } -- cgit v1.2.3