/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*/
#include <cppunit/TestAssert.h>
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <vcl/bitmap.hxx>
#include <tools/stream.hxx>
#include <vcl/graphicfilter.hxx>
#include <BitmapSymmetryCheck.hxx>
#include <vcl/BitmapWriteAccess.hxx>
namespace
{
class BitmapScaleTest : public CppUnit::TestFixture
{
void testScale();
void testScale2();
void testScaleSymmetry();
CPPUNIT_TEST_SUITE(BitmapScaleTest);
CPPUNIT_TEST(testScale);
CPPUNIT_TEST(testScale2);
CPPUNIT_TEST(testScaleSymmetry);
CPPUNIT_TEST_SUITE_END();
};
bool checkBitmapColor(Bitmap const & rBitmap, Color const & rExpectedColor)
{
bool bResult = true ;
Bitmap aBitmap(rBitmap);
BitmapScopedReadAccess pReadAccess(aBitmap);
tools::Long nHeight = pReadAccess->Height();
tools::Long nWidth = pReadAccess->Width();
for (tools::Long y = 0 ; y < nHeight; ++y)
{
Scanline pScanlineRead = pReadAccess->GetScanline(y);
for (tools::Long x = 0 ; x < nWidth; ++x)
{
Color aColor = pReadAccess->GetPixelFromData(pScanlineRead, x);
if (aColor != rExpectedColor)
bResult = false ;
}
}
return bResult;
}
void assertColorsAreSimilar(int maxDifference, int line, const BitmapColor& expected,
const BitmapColor& actual)
{
// Check that the two colors match or are reasonably similar.
if (expected.GetColorError(actual) <= maxDifference)
return ;
std::stringstream stream;
stream << "Line: " << line;
CPPUNIT_ASSERT_EQUAL_MESSAGE(stream.str(), expected, actual);
}
void BitmapScaleTest::testScale()
{
const bool bExportBitmap(false );
using tools::Rectangle;
static const BmpScaleFlag scaleMethods[]
= { BmpScaleFlag::Default , BmpScaleFlag::Fast, BmpScaleFlag::BestQuality,
BmpScaleFlag::Interpolate, BmpScaleFlag::Lanczos, BmpScaleFlag::BiCubic,
BmpScaleFlag::BiLinear };
for (BmpScaleFlag scaleMethod : scaleMethods)
{
struct ScaleSize
{
Size srcSize;
Size destSize;
};
static const ScaleSize scaleSizes[]
= { // test no-op
{ Size(16 , 16 ), Size(16 , 16 ) },
// powers of 2 (OpenGL may use texture atlas)
{ Size(16 , 16 ), Size(14 , 14 ) },
{ Size(14 , 14 ), Size(16 , 16 ) }, // both upscaling and downscaling
// "random" sizes
{ Size(18 , 18 ), Size(14 , 14 ) },
{ Size(14 , 14 ), Size(18 , 18 ) },
// different x/y ratios
{ Size(16 , 30 ), Size(14 , 18 ) },
{ Size(14 , 18 ), Size(16 , 30 ) },
// ratio larger than 16 (triggers different paths in some OpenGL algorithms)
{ Size(18 * 20 , 18 * 20 ), Size(14 , 14 ) },
{ Size(14 , 14 ), Size(18 * 20 , 18 * 20 ) },
// Boundary cases.
{ Size(1 , 1 ), Size(1 , 1 ) },
{ Size(16 , 1 ), Size(12 , 1 ) },
{ Size(1 , 16 ), Size(1 , 12 ) }
};
for (const ScaleSize& scaleSize : scaleSizes)
{
OString testStr = "Testing scale (" + scaleSize.srcSize.toString() + ")->("
+ scaleSize.destSize.toString() + "), method "
+ OString::number(static_cast <int >(scaleMethod));
fprintf(stderr, "%s\n" , testStr.getStr());
Bitmap bitmap(scaleSize.srcSize, vcl::PixelFormat::N24_BPP);
{
// Fill each quarter of the source bitmap with a different color,
// and center with yet another color.
BitmapScopedWriteAccess writeAccess(bitmap);
const int halfW = scaleSize.srcSize.getWidth() / 2 ;
const int halfH = scaleSize.srcSize.getHeight() / 2 ;
const Size aSize(std::max(halfW, 1 ), std::max(halfH, 1 ));
writeAccess->SetFillColor(COL_GREEN);
writeAccess->FillRect(Rectangle(Point(0 , 0 ), aSize));
writeAccess->SetFillColor(COL_RED);
writeAccess->FillRect(Rectangle(Point(0 , halfH), aSize));
writeAccess->SetFillColor(COL_YELLOW);
writeAccess->FillRect(Rectangle(Point(halfW, 0 ), aSize));
writeAccess->SetFillColor(COL_BLACK);
writeAccess->FillRect(Rectangle(Point(halfW, halfH), aSize));
writeAccess->SetFillColor(COL_BLUE);
writeAccess->FillRect(Rectangle(Point(halfW / 2 , halfH / 2 ), aSize));
}
if (bExportBitmap)
{
SvFileStream aStream(u"~/scale_before.png" _ustr,
StreamMode::WRITE | StreamMode::TRUNC);
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
rFilter.compressAsPNG(BitmapEx(bitmap), aStream);
}
CPPUNIT_ASSERT(bitmap.Scale(scaleSize.destSize, scaleMethod));
if (bExportBitmap)
{
SvFileStream aStream(u"~/scale_after.png" _ustr,
StreamMode::WRITE | StreamMode::TRUNC);
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
rFilter.compressAsPNG(BitmapEx(bitmap), aStream);
}
CPPUNIT_ASSERT_EQUAL(scaleSize.destSize, bitmap.GetSizePixel());
const int lastW = scaleSize.destSize.getWidth() - 1 ;
const int lastH = scaleSize.destSize.getHeight() - 1 ;
if (scaleSize.srcSize.getWidth() == 1 && scaleSize.srcSize.getHeight() == 1 )
{
BitmapReadAccess readAccess(bitmap);
assertColorsAreSimilar(2 , __LINE__, COL_BLUE, readAccess.GetColor(0 , 0 ));
assertColorsAreSimilar(2 , __LINE__, COL_BLUE, readAccess.GetColor(lastH, 0 ));
assertColorsAreSimilar(2 , __LINE__, COL_BLUE, readAccess.GetColor(0 , lastW));
assertColorsAreSimilar(2 , __LINE__, COL_BLUE, readAccess.GetColor(lastH, lastW));
assertColorsAreSimilar(2 , __LINE__, COL_BLUE,
readAccess.GetColor(lastH / 2 , lastW / 2 ));
}
else if (lastW && lastH)
{
// Scaling should keep each quarter of the resulting bitmap have the same color,
// so check that color in each corner of the result bitmap is the same color,
// or reasonably close (some algorithms may alter the color very slightly).
BitmapReadAccess readAccess(bitmap);
assertColorsAreSimilar(2 , __LINE__, COL_GREEN, readAccess.GetColor(0 , 0 ));
assertColorsAreSimilar(2 , __LINE__, COL_RED, readAccess.GetColor(lastH, 0 ));
assertColorsAreSimilar(2 , __LINE__, COL_YELLOW, readAccess.GetColor(0 , lastW));
assertColorsAreSimilar(2 , __LINE__, COL_BLACK, readAccess.GetColor(lastH, lastW));
assertColorsAreSimilar(2 , __LINE__, COL_BLUE,
readAccess.GetColor(lastH / 2 , lastW / 2 ));
}
else if (lastW)
{
BitmapReadAccess readAccess(bitmap);
assertColorsAreSimilar(2 , __LINE__, COL_RED, readAccess.GetColor(0 , 0 ));
assertColorsAreSimilar(2 , __LINE__, COL_BLACK, readAccess.GetColor(0 , lastW));
assertColorsAreSimilar(2 , __LINE__, COL_BLUE, readAccess.GetColor(0 , lastW / 2 ));
}
else if (lastH)
{
BitmapReadAccess readAccess(bitmap);
assertColorsAreSimilar(2 , __LINE__, COL_YELLOW, readAccess.GetColor(0 , 0 ));
assertColorsAreSimilar(2 , __LINE__, COL_BLACK, readAccess.GetColor(lastH, 0 ));
assertColorsAreSimilar(2 , __LINE__, COL_BLUE, readAccess.GetColor(lastH / 2 , 0 ));
}
}
}
}
void BitmapScaleTest::testScale2()
{
const bool bExportBitmap(false );
Bitmap aBitmap24Bit(Size(4096 , 4096 ), vcl::PixelFormat::N24_BPP);
CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP, aBitmap24Bit.getPixelFormat());
Color aBitmapColor = COL_YELLOW;
{
BitmapScopedWriteAccess aWriteAccess(aBitmap24Bit);
aWriteAccess->Erase(aBitmapColor);
}
if (bExportBitmap)
{
SvFileStream aStream(u"scale_before.png" _ustr, StreamMode::WRITE | StreamMode::TRUNC);
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
rFilter.compressAsPNG(BitmapEx(aBitmap24Bit), aStream);
}
// Scale - 65x65
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(4096 ), aBitmap24Bit.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(4096 ), aBitmap24Bit.GetSizePixel().Height());
Bitmap aScaledBitmap = aBitmap24Bit;
aScaledBitmap.Scale(Size(65 , 65 ));
if (bExportBitmap)
{
SvFileStream aStream(u"scale_after_65x65.png" _ustr, StreamMode::WRITE | StreamMode::TRUNC);
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
rFilter.compressAsPNG(BitmapEx(aScaledBitmap), aStream);
}
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(65 ), aScaledBitmap.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(65 ), aScaledBitmap.GetSizePixel().Height());
CPPUNIT_ASSERT(checkBitmapColor(aScaledBitmap, aBitmapColor));
// Scale - 64x64
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(4096 ), aBitmap24Bit.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(4096 ), aBitmap24Bit.GetSizePixel().Height());
aScaledBitmap = aBitmap24Bit;
aScaledBitmap.Scale(Size(64 , 64 ));
if (bExportBitmap)
{
SvFileStream aStream(u"scale_after_64x64.png" _ustr, StreamMode::WRITE | StreamMode::TRUNC);
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
rFilter.compressAsPNG(BitmapEx(aScaledBitmap), aStream);
}
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(64 ), aScaledBitmap.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(64 ), aScaledBitmap.GetSizePixel().Height());
CPPUNIT_ASSERT(checkBitmapColor(aScaledBitmap, aBitmapColor));
// Scale - 63x63
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(4096 ), aBitmap24Bit.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(4096 ), aBitmap24Bit.GetSizePixel().Height());
aScaledBitmap = aBitmap24Bit;
aScaledBitmap.Scale(Size(63 , 63 ));
if (bExportBitmap)
{
SvFileStream aStream(u"scale_after_63x63.png" _ustr, StreamMode::WRITE | StreamMode::TRUNC);
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
rFilter.compressAsPNG(BitmapEx(aScaledBitmap), aStream);
}
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(63 ), aScaledBitmap.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(63 ), aScaledBitmap.GetSizePixel().Height());
CPPUNIT_ASSERT(checkBitmapColor(aScaledBitmap, aBitmapColor));
}
void BitmapScaleTest::testScaleSymmetry()
{
const bool bExportBitmap(false );
Bitmap aBitmap24Bit(Size(10 , 10 ), vcl::PixelFormat::N24_BPP);
CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP, aBitmap24Bit.getPixelFormat());
{
BitmapScopedWriteAccess aWriteAccess(aBitmap24Bit);
aWriteAccess->Erase(COL_WHITE);
aWriteAccess->SetLineColor(COL_BLACK);
aWriteAccess->DrawRect(tools::Rectangle(1 , 1 , 8 , 8 ));
aWriteAccess->DrawRect(tools::Rectangle(3 , 3 , 6 , 6 ));
}
BitmapSymmetryCheck aBitmapSymmetryCheck;
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(10 ), aBitmap24Bit.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(10 ), aBitmap24Bit.GetSizePixel().Height());
// Check symmetry of the bitmap
CPPUNIT_ASSERT(BitmapSymmetryCheck::check(aBitmap24Bit));
if (bExportBitmap)
{
SvFileStream aStream(u"~/scale_before.png" _ustr, StreamMode::WRITE | StreamMode::TRUNC);
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
rFilter.compressAsPNG(BitmapEx(aBitmap24Bit), aStream);
}
aBitmap24Bit.Scale(2 , 2 , BmpScaleFlag::Fast);
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(20 ), aBitmap24Bit.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(static_cast <tools::Long >(20 ), aBitmap24Bit.GetSizePixel().Height());
// After scaling the bitmap should still be symmetrical. This check guarantees that
// scaling doesn't misalign the bitmap.
CPPUNIT_ASSERT(BitmapSymmetryCheck::check(aBitmap24Bit));
if (bExportBitmap)
{
SvFileStream aStream(u"~/scale_after.png" _ustr, StreamMode::WRITE | StreamMode::TRUNC);
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
rFilter.compressAsPNG(BitmapEx(aBitmap24Bit), aStream);
}
}
} // namespace
CPPUNIT_TEST_SUITE_REGISTRATION(BitmapScaleTest);
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=87 H=93 G=89
¤ Dauer der Verarbeitung: 0.5 Sekunden
¤
*© Formatika GbR, Deutschland