vcl/CppunitTest_vcl_outdev.mk | 1 vcl/qa/cppunit/outdev.cxx | 69 ++++++++++++++++++++++++++++++++++++++++++ vcl/source/gdi/bitmapex.cxx | 33 ++++++++++++++++++++ vcl/source/outdev/bitmap.cxx | 39 ++++++++++++++++++++--- 4 files changed, 136 insertions(+), 6 deletions(-)
New commits: commit 3a6a4ff5971d32ebd47e94368f398003ee31576f Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Thu Oct 10 19:03:53 2019 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Oct 11 12:33:15 2019 +0200 vcl, BitmapEx transformed draw: special-case simple rotations In case OutputDevice::DrawTransformedBitmapEx() has to do both shearing and rotation, then recording to a metafile is unchanged. But if we need to do rotation, then it's not necessary to go via transformations. This has the additional benefit that 90/180/270 degree rotations don't introduce an off-by-one error, where the first row and col of the transformed bitmap is transparent. (At the moment it's not clear what introduces the unwanted translation, but at least the direct Rotate() way resolves the visible end-user problem, see the test.) (cherry picked from commit 68549e00d5e23aa22bc974a8151d93cd948444b3) Conflicts: vcl/source/outdev/bitmap.cxx Change-Id: Ie1adbdb2221b086c19cc66f69308b6b7256fe29a diff --git a/vcl/qa/cppunit/outdev.cxx b/vcl/qa/cppunit/outdev.cxx index b91eef31e4fa..244496857ce5 100644 --- a/vcl/qa/cppunit/outdev.cxx +++ b/vcl/qa/cppunit/outdev.cxx @@ -90,9 +90,22 @@ void VclOutdevTest::testDrawTransformedBitmapEx() // Also create a 16x16 bitmap. ScopedVclPtrInstance<VirtualDevice> pVDev; Bitmap aBitmap(Size(16, 16), 24); + { + // Fill the top left quarter with black. + Bitmap::ScopedWriteAccess pWriteAccess(aBitmap); + pWriteAccess->Erase(COL_WHITE); + for (int i = 0; i < 8; ++i) + { + for (int j = 0; j < 8; ++j) + { + pWriteAccess->SetPixel(j, i, Color(COL_BLACK)); + } + } + } BitmapEx aBitmapEx(aBitmap); basegfx::B2DHomMatrix aMatrix; aMatrix.scale(8, 8); + // Rotate 90 degrees clockwise, so the black part goes to the top right. aMatrix.rotate(M_PI / 2); GDIMetaFile aMtf; aMtf.Record(pVDev.get()); @@ -110,6 +123,29 @@ void VclOutdevTest::testDrawTransformedBitmapEx() // - Actual : 8x8 // I.e. the bitmap before scaling was already scaled down, just because it was rotated. CPPUNIT_ASSERT_EQUAL(Size(16, 16), aTransformedSize); + + aBitmap = rBitmapEx.GetBitmap(); + Bitmap::ScopedReadAccess pAccess(aBitmap); + for (int i = 0; i < 16; ++i) + { + for (int j = 0; j < 16; ++j) + { + BitmapColor aColor = pAccess->GetPixel(j, i); + Color aExpected = i >= 8 && j < 8 ? COL_BLACK : COL_WHITE; + std::stringstream ss; + ss << "Color is expected to be "; + ss << ((aExpected == COL_WHITE) ? "white" : "black"); + ss << ", is " << Color(aColor).AsRGBHexString(); + ss << " (row " << j << ", col " << i << ")"; + // Without the accompanying fix in place, this test would have failed with: + // - Expected: c[00000000] + // - Actual : c[ffffff00] + // - Color is expected to be black, is ffffff (row 0, col 8) + // i.e. the top right quarter of the image was not fully black, there was a white first + // row. + CPPUNIT_ASSERT_EQUAL_MESSAGE(ss.str(), aExpected, Color(aColor)); + } + } } CPPUNIT_TEST_SUITE_REGISTRATION(VclOutdevTest); diff --git a/vcl/source/outdev/bitmap.cxx b/vcl/source/outdev/bitmap.cxx index 2b274b32f8dc..5257d20997f8 100644 --- a/vcl/source/outdev/bitmap.cxx +++ b/vcl/source/outdev/bitmap.cxx @@ -1296,11 +1296,25 @@ void OutputDevice::DrawTransformedBitmapEx( aFullTransform *= aTransform; } - aTransformed = aTransformed.getTransformed( - aFullTransform, - aVisibleRange, - fMaximumArea, - bDoSmoothAtAll); + if (bSheared) + { + aTransformed = aTransformed.getTransformed( + aFullTransform, + aVisibleRange, + fMaximumArea, + bDoSmoothAtAll); + } + else + { + // Just rotation, can do that directly. + fFullRotate = fmod(fFullRotate * -1, F_2PI); + if (fFullRotate < 0) + { + fFullRotate += F_2PI; + } + long nAngle10 = basegfx::fround(basegfx::rad2deg(fFullRotate) * 10); + aTransformed.Rotate(nAngle10, COL_TRANSPARENT); + } basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0); // get logic object target range commit eca544b84624b188c8fbaa5d6501f6b796da127d Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Thu Oct 10 09:49:29 2019 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Oct 11 10:06:24 2019 +0200 vcl: only smooth bitmap transform when needed If you have a very small bitmap and you rotate it by 90 degrees, then smoothing is not needed, but the result will be blurry. So in case scaling / shear doesn't need it and we do 90/180/270 rotation, avoid smoothing. (cherry picked from commit 55b4d5ea9e1a42edf71d2eef6028830983dbc11c) Conflicts: vcl/qa/cppunit/BitmapExTest.cxx vcl/source/gdi/bitmapex.cxx Change-Id: I4b8fad4b0b70516d35eaecfa70a707e6e8362d18 diff --git a/vcl/source/gdi/bitmapex.cxx b/vcl/source/gdi/bitmapex.cxx index fc75f9e5b120..5378b10dd322 100644 --- a/vcl/source/gdi/bitmapex.cxx +++ b/vcl/source/gdi/bitmapex.cxx @@ -872,6 +872,38 @@ namespace return aDestination; } + + /// Decides if rTransformation needs smoothing or not (e.g. 180 deg rotation doesn't need it). + bool implTransformNeedsSmooth(const basegfx::B2DHomMatrix& rTransformation) + { + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rTransformation.decompose(aScale, aTranslate, fRotate, fShearX); + if (aScale != basegfx::B2DVector(1, 1)) + { + return true; + } + + fRotate = fmod( fRotate, F_2PI ); + if (fRotate < 0) + { + fRotate += F_2PI; + } + if (!rtl::math::approxEqual(fRotate, 0) + && !rtl::math::approxEqual(fRotate, F_PI2) + && !rtl::math::approxEqual(fRotate, F_PI) + && !rtl::math::approxEqual(fRotate, 3 * F_PI2)) + { + return true; + } + + if (!rtl::math::approxEqual(fShearX, 0)) + { + return true; + } + + return false; + } } // end of anonymous namespace BitmapEx BitmapEx::TransformBitmapEx( @@ -885,6 +917,7 @@ BitmapEx BitmapEx::TransformBitmapEx( // force destination to 24 bit, we want to smooth output const Size aDestinationSize(basegfx::fround(fWidth), basegfx::fround(fHeight)); + bSmooth = implTransformNeedsSmooth(rTransformation); const Bitmap aDestination(impTransformBitmap(GetBitmap(), aDestinationSize, rTransformation, bSmooth)); // create mask commit 1109d0a36651b1523a1a310a8497ab115b857fb8 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Oct 8 09:16:27 2019 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Oct 11 09:55:25 2019 +0200 vcl: avoid downscale && upscale in DrawTransformedBitmapEx() If we rotate a bitmap and put it to a metafile, we'll create a MetaBmpExScaleAction. But just because we need to store a transformed bitmap for rotation purposes, it doesn't mean we also need to scale it. This helps in case later the metafile is upscaled and the downscaled bitmap would look blurry. (cherry picked from commit dd4a67084853a030bf4b9f1f85d728620e0604a5) Conflicts: vcl/qa/cppunit/outdev.cxx Change-Id: I7d64a88af460e80dffde8052186888eddbb440fe diff --git a/vcl/CppunitTest_vcl_outdev.mk b/vcl/CppunitTest_vcl_outdev.mk index 183432fccfe6..f15d2e26d17d 100644 --- a/vcl/CppunitTest_vcl_outdev.mk +++ b/vcl/CppunitTest_vcl_outdev.mk @@ -21,6 +21,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,vcl_outdev, \ $(eval $(call gb_CppunitTest_use_externals,vcl_outdev,boost_headers)) $(eval $(call gb_CppunitTest_use_libraries,vcl_outdev, \ + basegfx \ comphelper \ cppu \ cppuhelper \ diff --git a/vcl/qa/cppunit/outdev.cxx b/vcl/qa/cppunit/outdev.cxx index 1e6508789574..b91eef31e4fa 100644 --- a/vcl/qa/cppunit/outdev.cxx +++ b/vcl/qa/cppunit/outdev.cxx @@ -14,9 +14,12 @@ #include <vcl/salbtype.hxx> #include <vcl/bitmapaccess.hxx> #include <vcl/wrkwin.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/metaact.hxx> #include <tools/stream.hxx> #include <vcl/pngwrite.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> class VclOutdevTest : public test::BootstrapFixture { @@ -24,9 +27,11 @@ public: VclOutdevTest() : BootstrapFixture(true, false) {} void testVirtualDevice(); + void testDrawTransformedBitmapEx(); CPPUNIT_TEST_SUITE(VclOutdevTest); CPPUNIT_TEST(testVirtualDevice); + CPPUNIT_TEST(testDrawTransformedBitmapEx); CPPUNIT_TEST_SUITE_END(); }; @@ -79,6 +84,34 @@ void VclOutdevTest::testVirtualDevice() #endif } +void VclOutdevTest::testDrawTransformedBitmapEx() +{ + // Create a virtual device, and connect a metafile to it. + // Also create a 16x16 bitmap. + ScopedVclPtrInstance<VirtualDevice> pVDev; + Bitmap aBitmap(Size(16, 16), 24); + BitmapEx aBitmapEx(aBitmap); + basegfx::B2DHomMatrix aMatrix; + aMatrix.scale(8, 8); + aMatrix.rotate(M_PI / 2); + GDIMetaFile aMtf; + aMtf.Record(pVDev.get()); + + // Draw the rotated bitmap on the vdev. + pVDev->DrawTransformedBitmapEx(aMatrix, aBitmapEx); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aMtf.GetActionSize()); + MetaAction* pAction = aMtf.GetAction(0); + CPPUNIT_ASSERT_EQUAL(MetaActionType::BMPEXSCALE, pAction->GetType()); + auto pBitmapAction = static_cast<MetaBmpExScaleAction*>(pAction); + const BitmapEx& rBitmapEx = pBitmapAction->GetBitmapEx(); + Size aTransformedSize = rBitmapEx.GetSizePixel(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 16x16 + // - Actual : 8x8 + // I.e. the bitmap before scaling was already scaled down, just because it was rotated. + CPPUNIT_ASSERT_EQUAL(Size(16, 16), aTransformedSize); +} + CPPUNIT_TEST_SUITE_REGISTRATION(VclOutdevTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/vcl/source/outdev/bitmap.cxx b/vcl/source/outdev/bitmap.cxx index 4ae3c84d37ce..2b274b32f8dc 100644 --- a/vcl/source/outdev/bitmap.cxx +++ b/vcl/source/outdev/bitmap.cxx @@ -1220,7 +1220,7 @@ void OutputDevice::DrawTransformedBitmapEx( const bool bInvert(RasterOp::Invert == meRasterOp); const bool bBitmapChangedColor(mnDrawMode & (DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap | DrawModeFlags::GrayBitmap | DrawModeFlags::GhostedBitmap)); bool bDone(false); - const basegfx::B2DHomMatrix aFullTransform(GetViewTransformation() * rTransformation); + basegfx::B2DHomMatrix aFullTransform(GetViewTransformation() * rTransformation); const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile ); if(!bForceToOwnTransformer && bTryDirectPaint) @@ -1283,6 +1283,19 @@ void OutputDevice::DrawTransformedBitmapEx( aTransformed = BitmapEx(aContent, aMaskBmp); } + // Remove scaling from aFulltransform: we transform due to shearing or rotation, scaling + // will happen according to aDestSize. + basegfx::B2DVector aFullScale, aFullTranslate; + double fFullRotate, fFullShearX; + aFullTransform.decompose(aFullScale, aFullTranslate, fFullRotate, fFullShearX); + if (aFullScale.getX() != 0 && aFullScale.getY() != 0) + { + basegfx::B2DHomMatrix aTransform = basegfx::tools::createScaleB2DHomMatrix( + rOriginalSizePixel.getWidth() / aFullScale.getX(), + rOriginalSizePixel.getHeight() / aFullScale.getY()); + aFullTransform *= aTransform; + } + aTransformed = aTransformed.getTransformed( aFullTransform, aVisibleRange, _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits