initial commit

Signed-off-by: Peter Siegmund <mars3142@noreply.mars3142.dev>
This commit is contained in:
2025-10-31 23:37:30 +01:00
commit bf6b52fd94
9654 changed files with 4035664 additions and 0 deletions

View File

@@ -0,0 +1,537 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/graphics/affinetransform.cpp
// Purpose: Unit test for transformations implemented for wxAffineMatrix2D
// Author: Catalin Raceanu
// Created: 2011-04-14
// Copyright: (c) 2011 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#include "wx/graphics.h"
#include "wx/dcmemory.h"
#include "wx/affinematrix2d.h"
#include "wx/math.h"
#if wxUSE_GRAPHICS_CONTEXT
#include "wx/dcgraph.h"
#endif // wxUSE_GRAPHICS_CONTEXT
#include "testimage.h"
// ----------------------------------------------------------------------------
// Affine transform test class
// ----------------------------------------------------------------------------
class AffineTransformTestCase : public CppUnit::TestCase
{
public:
AffineTransformTestCase() {}
private:
CPPUNIT_TEST_SUITE( AffineTransformTestCase );
CPPUNIT_TEST( InvertMatrix );
CPPUNIT_TEST( Concat );
CPPUNIT_TEST_SUITE_END();
void InvertMatrix();
void Concat();
wxDECLARE_NO_COPY_CLASS(AffineTransformTestCase);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( AffineTransformTestCase );
// also include in its own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( AffineTransformTestCase, "AffineTransformTestCase" );
void AffineTransformTestCase::InvertMatrix()
{
wxAffineMatrix2D matrix1;
matrix1.Set(wxMatrix2D(2, 1, 1, 1), wxPoint2DDouble(1, 1));
wxAffineMatrix2D matrix2(matrix1);
matrix2.Invert();
wxMatrix2D m;
wxPoint2DDouble p;
matrix2.Get(&m, &p);
CPPUNIT_ASSERT_EQUAL( 1, (int)m.m_11 );
CPPUNIT_ASSERT_EQUAL( -1, (int)m.m_12 );
CPPUNIT_ASSERT_EQUAL( -1, (int)m.m_21 );
CPPUNIT_ASSERT_EQUAL( 2, (int)m.m_22 );
CPPUNIT_ASSERT_EQUAL( 0, (int)p.m_x );
CPPUNIT_ASSERT_EQUAL( -1, (int)p.m_y );
matrix2.Concat(matrix1);
CPPUNIT_ASSERT( matrix2.IsIdentity() );
}
void AffineTransformTestCase::Concat()
{
wxAffineMatrix2D m1;
m1.Set(wxMatrix2D(0.9, 0.4, -0.4, 0.9), wxPoint2DDouble(0.0, 0.0));
wxAffineMatrix2D m2;
m2.Set(wxMatrix2D(1.0, 0.0, 0.0, 1.0), wxPoint2DDouble(3.0, 5.0));
m1.Concat(m2);
wxMatrix2D m;
wxPoint2DDouble p;
m1.Get(&m, &p);
const double delta = 0.01;
CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.9, m.m_11, delta );
CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.4, m.m_12, delta );
CPPUNIT_ASSERT_DOUBLES_EQUAL( -0.4, m.m_21, delta );
CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.9, m.m_22, delta );
CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.7, p.m_x, delta );
CPPUNIT_ASSERT_DOUBLES_EQUAL( 5.7, p.m_y, delta );
}
#if wxUSE_DC_TRANSFORM_MATRIX
// -------------------------------
// Transform matrix test classes
// -------------------------------
// ====================
// wxDC / wxGCDC tests
// ====================
class TransformMatrixTestCaseDCBase : public CppUnit::TestCase
{
public:
TransformMatrixTestCaseDCBase()
{
m_dc = nullptr;
wxImage::AddHandler(new wxJPEGHandler);
m_imgOrig.LoadFile(wxS("horse.jpg"));
CPPUNIT_ASSERT( m_imgOrig.IsOk() );
}
virtual ~TransformMatrixTestCaseDCBase()
{
}
virtual void setUp() override
{
m_bmpOrig = wxBitmap(m_imgOrig);
m_bmpUsingMatrix.Create(m_bmpOrig.GetSize(), m_bmpOrig.GetDepth());
}
protected:
virtual void FlushDC() = 0;
void VMirrorAndTranslate();
void Rotate90Clockwise();
#if wxUSE_GRAPHICS_CONTEXT
void CompareToGraphicsContext();
#endif // wxUSE_GRAPHICS_CONTEXT
protected:
wxImage m_imgOrig;
wxBitmap m_bmpOrig;
wxBitmap m_bmpUsingMatrix;
wxDC* m_dc;
wxDECLARE_NO_COPY_CLASS(TransformMatrixTestCaseDCBase);
};
// ===========
// wxDC tests
// ===========
class TransformMatrixTestCaseDC : public TransformMatrixTestCaseDCBase
{
public:
TransformMatrixTestCaseDC()
{
m_dc = &m_mdc;
}
virtual ~TransformMatrixTestCaseDC()
{
}
virtual void setUp() override
{
TransformMatrixTestCaseDCBase::setUp();
m_mdc.SelectObject(m_bmpUsingMatrix);
}
virtual void tearDown() override
{
m_mdc.SelectObject(wxNullBitmap);
TransformMatrixTestCaseDCBase::tearDown();
}
protected:
virtual void FlushDC() override {}
private:
CPPUNIT_TEST_SUITE( TransformMatrixTestCaseDC );
CPPUNIT_TEST( VMirrorAndTranslate );
CPPUNIT_TEST( Rotate90Clockwise );
#if wxUSE_GRAPHICS_CONTEXT
CPPUNIT_TEST( CompareToGraphicsContext );
#endif // wxUSE_GRAPHICS_CONTEXT
CPPUNIT_TEST_SUITE_END();
protected:
wxMemoryDC m_mdc;
wxDECLARE_NO_COPY_CLASS(TransformMatrixTestCaseDC);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( TransformMatrixTestCaseDC );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TransformMatrixTestCaseDC, "TransformMatrixTestCaseDC" );
#if wxUSE_GRAPHICS_CONTEXT
// =============
// wxGCDC tests
// =============
class TransformMatrixTestCaseGCDC : public TransformMatrixTestCaseDC
{
public:
TransformMatrixTestCaseGCDC() {}
virtual ~TransformMatrixTestCaseGCDC() {}
virtual void setUp() override
{
TransformMatrixTestCaseDC::setUp();
m_gcdc = new wxGCDC(m_mdc);
m_dc = m_gcdc;
wxGraphicsContext* ctx = m_gcdc->GetGraphicsContext();
ctx->SetAntialiasMode(wxANTIALIAS_NONE);
}
virtual void tearDown() override
{
delete m_gcdc;
TransformMatrixTestCaseDC::tearDown();
}
protected:
virtual void FlushDC() override
{
m_gcdc->GetGraphicsContext()->Flush();
}
private:
CPPUNIT_TEST_SUITE( TransformMatrixTestCaseGCDC );
CPPUNIT_TEST( VMirrorAndTranslate );
CPPUNIT_TEST( Rotate90Clockwise );
CPPUNIT_TEST_SUITE_END();
protected:
wxGCDC* m_gcdc;
wxDECLARE_NO_COPY_CLASS(TransformMatrixTestCaseGCDC);
};
// For MSW we have individual test cases for each graphics renderer
// so we don't need to test wxGCDC with default renderer.
#ifndef __WXMSW__
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( TransformMatrixTestCaseGCDC );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TransformMatrixTestCaseGCDC, "TransformMatrixTestCaseGCDC" );
#endif // !__WXMSW__
#ifdef __WXMSW__
// GDI+ and Direct2D are available only under MSW.
#if wxUSE_GRAPHICS_GDIPLUS
class TransformMatrixTestCaseGCDCGDIPlus : public TransformMatrixTestCaseGCDC
{
public:
TransformMatrixTestCaseGCDCGDIPlus() {}
virtual ~TransformMatrixTestCaseGCDCGDIPlus() {}
virtual void setUp() override
{
TransformMatrixTestCaseGCDC::setUp();
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetGDIPlusRenderer();
wxGraphicsContext* ctx = rend->CreateContext(m_mdc);
m_gcdc->SetGraphicsContext(ctx);
}
private:
CPPUNIT_TEST_SUITE( TransformMatrixTestCaseGCDCGDIPlus );
CPPUNIT_TEST( VMirrorAndTranslate );
CPPUNIT_TEST( Rotate90Clockwise );
CPPUNIT_TEST_SUITE_END();
protected:
wxDECLARE_NO_COPY_CLASS(TransformMatrixTestCaseGCDCGDIPlus);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( TransformMatrixTestCaseGCDCGDIPlus );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TransformMatrixTestCaseGCDCGDIPlus, "TransformMatrixTestCaseGCDCGDIPlus" );
#endif // wxUSE_GRAPHICS_GDIPLUS
#if wxUSE_GRAPHICS_DIRECT2D
class TransformMatrixTestCaseGCDCDirect2D : public TransformMatrixTestCaseGCDC
{
public:
TransformMatrixTestCaseGCDCDirect2D() {}
virtual ~TransformMatrixTestCaseGCDCDirect2D() {}
virtual void setUp() override
{
TransformMatrixTestCaseGCDC::setUp();
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetDirect2DRenderer();
wxGraphicsContext* ctx = rend->CreateContext(m_mdc);
m_gcdc->SetGraphicsContext(ctx);
}
virtual void FlushDC() override
{
// Apparently, flushing native Direct2D renderer
// is not enough to update underlying DC (bitmap)
// and therefore we have to destroy the renderer
// to do so.
TransformMatrixTestCaseGCDC::FlushDC();
m_gcdc->SetGraphicsContext(nullptr);
}
private:
CPPUNIT_TEST_SUITE( TransformMatrixTestCaseGCDCDirect2D );
CPPUNIT_TEST( VMirrorAndTranslate );
CPPUNIT_TEST( Rotate90Clockwise );
CPPUNIT_TEST_SUITE_END();
protected:
wxDECLARE_NO_COPY_CLASS(TransformMatrixTestCaseGCDCDirect2D);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( TransformMatrixTestCaseGCDCDirect2D );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TransformMatrixTestCaseGCDCDirect2D, "TransformMatrixTestCaseGCDCDirect2D" );
#endif // wxUSE_GRAPHICS_DIRECT2D
#endif // __WXMSW__
#if wxUSE_CAIRO
class TransformMatrixTestCaseGCDCCairo : public TransformMatrixTestCaseGCDC
{
public:
TransformMatrixTestCaseGCDCCairo() {}
virtual ~TransformMatrixTestCaseGCDCCairo() {}
virtual void setUp() override
{
TransformMatrixTestCaseGCDC::setUp();
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetCairoRenderer();
wxGraphicsContext* ctx = rend->CreateContext(m_mdc);
m_gcdc->SetGraphicsContext(ctx);
}
private:
CPPUNIT_TEST_SUITE( TransformMatrixTestCaseGCDCCairo );
CPPUNIT_TEST( VMirrorAndTranslate );
CPPUNIT_TEST( Rotate90Clockwise );
CPPUNIT_TEST_SUITE_END();
protected:
wxDECLARE_NO_COPY_CLASS(TransformMatrixTestCaseGCDCCairo);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( TransformMatrixTestCaseGCDCCairo );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TransformMatrixTestCaseGCDCCairo, "TransformMatrixTestCaseGCDCCairo" );
#endif // wxUSE_CAIRO
#endif // wxUSE_GRAPHICS_CONTEXT
void TransformMatrixTestCaseDCBase::VMirrorAndTranslate()
{
// build the mirrored image using the transformation matrix
if ( !m_dc->CanUseTransformMatrix() )
return;
wxAffineMatrix2D matrix;
matrix.Mirror(wxVERTICAL);
// For wxDC pixel center is at (0, 0) so row 0 of the bitmap is the axis
// of mirroring and it is left intact by the transformation. In this case
// mirrored bitmap needs to be shifted by dim-1 pixels.
// For wxGCDC pixel center of underlying wxGraphicsContext is at (0.5, 0.5)
// so the axis of mirroring is above row 0 of the bitmap and this row
// is affected by the transformation. In this case mirrored bitmap
// needs to be shifthed by dim pixels.
int ty;
#if wxUSE_GRAPHICS_CONTEXT
if ( m_dc->GetGraphicsContext() )
ty = m_bmpOrig.GetHeight();
else
#endif // wxUSE_GRAPHICS_CONTEXT
ty = m_bmpOrig.GetHeight() - 1;
matrix.Translate(0, -ty);
m_dc->SetTransformMatrix(matrix);
m_dc->DrawBitmap(m_bmpOrig, 0, 0);
FlushDC();
CHECK_THAT( m_bmpUsingMatrix.ConvertToImage(),
RGBSameAs(m_imgOrig.Mirror(false)) );
}
void TransformMatrixTestCaseDCBase::Rotate90Clockwise()
{
// build the rotated image using the transformation matrix
if ( !m_dc->CanUseTransformMatrix() )
return;
wxAffineMatrix2D matrix;
matrix.Rotate(0.5 * M_PI);
matrix.Translate(0, -m_bmpOrig.GetHeight());
m_dc->SetTransformMatrix(matrix);
m_dc->DrawBitmap(m_bmpOrig, 0, 0);
FlushDC();
CHECK_THAT( m_bmpUsingMatrix.ConvertToImage(),
RGBSameAs(m_imgOrig.Rotate90(true)) );
}
#if wxUSE_GRAPHICS_CONTEXT
void TransformMatrixTestCaseDCBase::CompareToGraphicsContext()
{
wxPoint2DDouble pointA1(1.0, 3.0), pointA2(60.0, 50.0),
pointG1(1.0, 3.0), pointG2(60.0, 50.0);
// Create affine matrix and transform it
wxAffineMatrix2D matrixA1, matrixA2;
matrixA2.Rotate(M_PI / 3);
matrixA1.Translate(-m_bmpOrig.GetWidth()/2, -m_bmpOrig.GetHeight()/2);
matrixA1.Rotate(-M_PI *2/ 6);
matrixA1.Translate(m_bmpOrig.GetWidth()/2, m_bmpOrig.GetHeight()/2);
matrixA1.Mirror(wxHORIZONTAL);
matrixA1.Concat(matrixA2);
matrixA1.Mirror(wxVERTICAL);
matrixA1.Translate(m_bmpOrig.GetWidth()/2, -m_bmpOrig.GetHeight()/2);
matrixA1.Scale(0.9, 0.9);
matrixA1.Invert();
// Create image using first matrix
wxBitmap bmpUsingMatrixA1(m_bmpOrig.GetSize(), m_bmpOrig.GetDepth());
// Build the transformed image using the transformation matrix
{
wxMemoryDC dc(bmpUsingMatrixA1);
if ( !dc.CanUseTransformMatrix() )
return;
// Draw the bitmap
dc.SetTransformMatrix(matrixA1);
dc.DrawBitmap(m_bmpOrig, 0, 0);
// Draw a line
matrixA1.TransformPoint(&pointA1.m_x, &pointA1.m_y);
matrixA1.TransformDistance(&pointA2.m_x, &pointA2.m_y);
dc.DrawLine(wxRound(pointA1.m_x), wxRound(pointA1.m_y),
wxRound(pointA1.m_x + pointA2.m_x), wxRound(pointA1.m_x + pointA2.m_y));
}
// Create graphics matrix and transform it
wxGraphicsRenderer* r;
if ( m_dc->GetGraphicsContext() )
{
r = m_dc->GetGraphicsContext()->GetRenderer();
}
else
{
r = wxGraphicsRenderer::GetDefaultRenderer();
}
wxBitmap bmp(10, 10);
wxMemoryDC mDc(bmp);
wxGraphicsContext* gDc = r->CreateContext(mDc);
wxGraphicsMatrix matrixG1 = gDc->CreateMatrix();
wxGraphicsMatrix matrixG2 = gDc->CreateMatrix();
matrixG2.Rotate(M_PI / 3);
matrixG1.Translate(-m_bmpOrig.GetWidth()/2, -m_bmpOrig.GetHeight()/2);
matrixG1.Rotate(-M_PI*2 / 6);
matrixG1.Translate(m_bmpOrig.GetWidth()/2, m_bmpOrig.GetHeight()/2);
matrixG1.Scale(-1, 1);
matrixG1.Concat(matrixG2);
matrixG1.Scale(1, -1);
matrixG1.Translate(m_bmpOrig.GetWidth()/2, -m_bmpOrig.GetHeight()/2);
matrixG1.Scale(0.9, 0.9);
matrixG1.Invert();
// Create affine matrix from the graphics matrix
wxMatrix2D mat2D;
wxPoint2DDouble tr;
matrixG1.Get(&mat2D.m_11, &mat2D.m_12, &mat2D.m_21, &mat2D.m_22, &tr.m_x, &tr.m_y);
wxAffineMatrix2D matrixAG;
matrixAG.Set(mat2D, tr);
delete gDc;
// Create image using last matrix
wxBitmap bmpUsingMatrixAG(m_bmpOrig.GetHeight(), m_bmpOrig.GetWidth());
// Build the transformed image using the transformation matrix
{
wxMemoryDC dc(bmpUsingMatrixAG);
if ( !dc.CanUseTransformMatrix() )
return;
// Draw the bitmap
dc.SetTransformMatrix(matrixAG);
dc.DrawBitmap(m_bmpOrig, 0, 0);
// Draw a line
matrixG1.TransformPoint(&pointG1.m_x, &pointG1.m_y);
matrixG1.TransformDistance(&pointG2.m_x, &pointG2.m_y);
dc.DrawLine(wxRound(pointG1.m_x), wxRound(pointG1.m_y),
wxRound(pointG1.m_x + pointG2.m_x), wxRound(pointG1.m_x + pointG2.m_y));
}
CHECK_THAT( bmpUsingMatrixA1.ConvertToImage(),
RGBSameAs(bmpUsingMatrixAG.ConvertToImage()) );
// Save the images to check that something _is_ inside the visible area.
//bmpUsingMatrixA1.SaveFile("matrixA1.jpg", wxBITMAP_TYPE_JPEG);
//bmpUsingMatrixAG.SaveFile("matrixAG.jpg", wxBITMAP_TYPE_JPEG);
}
#endif // wxUSE_GRAPHICS_CONTEXT
#endif // wxUSE_DC_TRANSFORM_MATRIX

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,653 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/graphics/bmpbundle.cpp
// Purpose: wxBitmapBundle unit test
// Author: Vadim Zeitlin
// Created: 2021-09-27
// Copyright: (c) 2021 Vadim Zeitlin <vadim@wxwidgets.org>
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#include "wx/bmpbndl.h"
#include "wx/artprov.h"
#include "wx/dcmemory.h"
#include "wx/imaglist.h"
#ifdef __WINDOWS__
#include "wx/msw/private/resource_usage.h"
#endif // __WINDOWS__
#include "asserthelper.h"
// ----------------------------------------------------------------------------
// tests
// ----------------------------------------------------------------------------
TEST_CASE("BitmapBundle::Create", "[bmpbundle]")
{
wxBitmapBundle b;
CHECK( !b.IsOk() );
CHECK( b.GetDefaultSize() == wxDefaultSize );
b = wxBitmap(16, 16);
CHECK( b.IsOk() );
CHECK( b.GetDefaultSize() == wxSize(16, 16) );
}
TEST_CASE("BitmapBundle::FromBitmaps", "[bmpbundle]")
{
wxVector<wxBitmap> bitmaps;
bitmaps.push_back(wxBitmap(16, 16));
bitmaps.push_back(wxBitmap(24, 24));
wxBitmapBundle b = wxBitmapBundle::FromBitmaps(bitmaps);
REQUIRE( b.IsOk() );
CHECK( b.GetDefaultSize() == wxSize(16, 16) );
CHECK( b.GetBitmap(wxDefaultSize ).GetSize() == wxSize(16, 16) );
CHECK( b.GetBitmap(wxSize(16, 16)).GetSize() == wxSize(16, 16) );
CHECK( b.GetBitmap(wxSize(20, 20)).GetSize() == wxSize(20, 20) );
CHECK( b.GetBitmap(wxSize(24, 24)).GetSize() == wxSize(24, 24) );
}
TEST_CASE("BitmapBundle::GetBitmap", "[bmpbundle]")
{
wxBitmapBundle b = wxBitmapBundle::FromBitmap(wxBitmap(16, 16));
CHECK( b.GetBitmap(wxSize(16, 16)).GetSize() == wxSize(16, 16) );
CHECK( b.GetBitmap(wxSize(32, 32)).GetSize() == wxSize(32, 32) );
CHECK( b.GetBitmap(wxSize(24, 24)).GetSize() == wxSize(24, 24) );
// Test for the special case when the requested size uses the same height
// but not the same width.
wxBitmap nonSquare(wxSize(51, 41));
b = wxBitmapBundle::FromBitmap(nonSquare);
const wxSize scaledSize(52, 41);
CHECK( b.GetBitmap(scaledSize).GetSize() == scaledSize );
// Test upscaling too.
b = wxBitmapBundle::FromBitmap(wxBitmap(32, 32));
CHECK( b.GetBitmap(wxSize(24, 24)).GetSize() == wxSize(24, 24) );
CHECK( b.GetBitmap(wxSize(48, 48)).GetSize() == wxSize(48, 48) );
}
#ifdef __WINDOWS__
namespace
{
std::string wxGUIObjectUsageAsString(const wxGUIObjectUsage& useCount)
{
return wxString::Format("%lu GDI, %lu USER",
useCount.numGDI, useCount.numUSER)
.utf8_string();
}
} // anonymous namespace
namespace Catch
{
template <>
struct StringMaker<wxGUIObjectUsage>
{
static std::string convert(const wxGUIObjectUsage& useCount)
{
return wxGUIObjectUsageAsString(useCount);
}
};
}
TEST_CASE("BitmapBundle::ResourceLeak", "[bmpbundle]")
{
wxBitmapBundle bb = wxBitmapBundle::FromBitmap(wxBitmap(32, 32));
const auto usageBefore = wxGetCurrentlyUsedResources();
INFO("Usage before: " << wxGUIObjectUsageAsString(usageBefore));
for ( int n = 0; n < 10000; ++n )
{
wxBitmap bmp = bb.GetBitmap(wxSize(24, 24));
if ( !bmp.GetHandle() )
{
FAIL("Failed to create bitmap");
}
}
const auto usageAfter = wxGetCurrentlyUsedResources();
INFO("Usage after: " << wxGUIObjectUsageAsString(usageAfter));
INFO("Usage peak: " << wxGUIObjectUsageAsString(wxGetMaxUsedResources()));
// We shouldn't have used any USER resources.
CHECK( usageAfter.numUSER == usageBefore.numUSER );
// Ideally we'd want the GDI usage to be exactly the same as before too,
// but at least one extra resource gets allocated somewhere, so allow for
// it.
REQUIRE( usageAfter.numGDI >= usageBefore.numGDI );
CHECK( usageAfter.numGDI - usageBefore.numGDI < 10 );
}
#endif // __WINDOWS__
// Helper functions for the test below.
namespace
{
// Default size here doesn't really matter.
const wxSize BITMAP_SIZE(16, 16);
// The choice of colours here is arbitrary too, but they need to be all
// different to allow identifying which bitmap got scaled.
struct ColourAtScale
{
double scale;
wxUint32 rgb;
};
const ColourAtScale colours[] =
{
{ 1.0, 0x000000ff },
{ 1.5, 0x0000ff00 },
{ 2.0, 0x00ff0000 },
};
// Return the colour used for the (original) bitmap at the given scale.
wxColour GetColourForScale(double scale)
{
wxColour col;
for ( size_t n = 0; n < WXSIZEOF(colours); ++n )
{
if ( colours[n].scale == scale )
{
col.SetRGB(colours[n].rgb);
return col;
}
}
wxFAIL_MSG("no colour for this scale");
return col;
}
double GetScaleFromColour(const wxColour& col)
{
const wxUint32 rgb = col.GetRGB();
for ( size_t n = 0; n < WXSIZEOF(colours); ++n )
{
if ( colours[n].rgb == rgb )
return colours[n].scale;
}
wxFAIL_MSG(wxString::Format("no scale for colour %s", col.GetAsString()));
return 0.0;
}
double SizeToScale(const wxSize& size)
{
return static_cast<double>(size.y) / BITMAP_SIZE.y;
}
wxBitmap MakeSolidBitmap(double scale)
{
wxBitmap bmp(BITMAP_SIZE*scale);
wxMemoryDC dc(bmp);
dc.SetBackground(GetColourForScale(scale));
dc.Clear();
return bmp;
}
wxColour GetBitmapColour(const wxBitmap& bmp)
{
const wxImage img = bmp.ConvertToImage();
// We just assume the bitmap is solid colour, we could check it, but it
// doesn't seem really useful to do it.
return wxColour(img.GetRed(0, 0), img.GetGreen(0, 0), img.GetBlue(0, 0));
}
// This struct exists just to allow using it conveniently in CHECK_THAT().
struct BitmapAtScale
{
BitmapAtScale(const wxBitmapBundle& b, double scale)
: size(b.GetPreferredBitmapSizeAtScale(scale)),
bitmap(b.GetBitmap(size))
{
}
const wxSize size;
const wxBitmap bitmap;
};
class BitmapAtScaleMatcher : public Catch::MatcherBase<BitmapAtScale>
{
public:
explicit BitmapAtScaleMatcher(double scale, double scaleOrig)
: m_scale(scale),
m_scaleOrig(scaleOrig)
{
}
bool match(const BitmapAtScale& bitmapAtScale) const override
{
const wxBitmap& bmp = bitmapAtScale.bitmap;
if ( SizeToScale(bitmapAtScale.size) != m_scale ||
SizeToScale(bmp.GetSize()) != m_scale )
{
m_diffDesc.Printf("should have scale %.1f", m_scale);
}
if ( GetBitmapColour(bmp) != GetColourForScale(m_scaleOrig) )
{
if ( m_diffDesc.empty() )
m_diffDesc = "should be ";
else
m_diffDesc += " and be ";
m_diffDesc += wxString::Format("created from x%.1f", m_scaleOrig);
}
return m_diffDesc.empty();
}
std::string describe() const override
{
return m_diffDesc.utf8_string();
}
private:
const double m_scale;
const double m_scaleOrig;
mutable wxString m_diffDesc;
};
// The first parameter here determines the size of the expected bitmap and the
// second one, which defaults to the first one if it's not specified, the size
// of the bitmap which must have been scaled to create the bitmap of the right
// size.
BitmapAtScaleMatcher SameAs(double scale, double scaleOrig = 0.0)
{
if ( scaleOrig == 0.0 )
scaleOrig = scale;
return BitmapAtScaleMatcher(scale, scaleOrig);
}
} // anonymous namespace
namespace Catch
{
template <>
struct StringMaker<BitmapAtScale>
{
static std::string convert(const BitmapAtScale& bitmapAtScale)
{
const wxBitmap& bmp = bitmapAtScale.bitmap;
wxString scaleError;
if ( bmp.GetSize() != bitmapAtScale.size )
{
scaleError.Printf(" (DIFFERENT from expected %.1f)",
SizeToScale(bitmapAtScale.size));
}
return wxString::Format
(
"x%.1f bitmap%s created from x%.1f",
SizeToScale(bmp.GetSize()),
scaleError,
GetScaleFromColour(GetBitmapColour(bmp))
).utf8_string();
}
};
}
TEST_CASE("BitmapBundle::GetPreferredSize", "[bmpbundle]")
{
// Check that empty bundle doesn't have any preferred size.
wxBitmapBundle b;
CHECK( b.GetPreferredBitmapSizeAtScale(1) == wxDefaultSize );
const wxBitmap normal = MakeSolidBitmap(1.0);
const wxBitmap middle = MakeSolidBitmap(1.5);
const wxBitmap bigger = MakeSolidBitmap(2.0);
// Then check what happens if there is only a single bitmap.
b = wxBitmapBundle::FromBitmap(normal);
// We should avoid scaling as long as the size is close enough to the
// actual bitmap size.
CHECK_THAT( BitmapAtScale(b, 0 ), SameAs(1) );
CHECK_THAT( BitmapAtScale(b, 1 ), SameAs(1) );
CHECK_THAT( BitmapAtScale(b, 1.25), SameAs(1) );
CHECK_THAT( BitmapAtScale(b, 1.4 ), SameAs(1) );
CHECK_THAT( BitmapAtScale(b, 1.5 ), SameAs(1) );
// Once it becomes too big, we're going to need to scale, but we should be
// scaling by an integer factor.
CHECK_THAT( BitmapAtScale(b, 1.75), SameAs(2, 1) );
CHECK_THAT( BitmapAtScale(b, 2 ), SameAs(2, 1) );
CHECK_THAT( BitmapAtScale(b, 2.25), SameAs(2, 1) );
CHECK_THAT( BitmapAtScale(b, 2.5 ), SameAs(3, 1) );
// Now check what happens when there is also a double size bitmap.
b = wxBitmapBundle::FromBitmaps(normal, bigger);
// Check that the existing bitmaps are used without scaling for most of the
// typical scaling values.
CHECK_THAT( BitmapAtScale(b, 0 ), SameAs(1) );
CHECK_THAT( BitmapAtScale(b, 1 ), SameAs(1) );
CHECK_THAT( BitmapAtScale(b, 1.25), SameAs(1) );
CHECK_THAT( BitmapAtScale(b, 1.4 ), SameAs(1) );
CHECK_THAT( BitmapAtScale(b, 1.5 ), SameAs(1) );
CHECK_THAT( BitmapAtScale(b, 1.75), SameAs(2) );
CHECK_THAT( BitmapAtScale(b, 2 ), SameAs(2) );
CHECK_THAT( BitmapAtScale(b, 2.5 ), SameAs(2) );
CHECK_THAT( BitmapAtScale(b, 3 ), SameAs(2) );
// This scale is too big to use any of the existing bitmaps, so they will
// be scaled, but use integer factors and, importantly, scale the correct
// bitmap using them: we need to scale the small bitmap by a factor of 3,
// rather than scaling the larger bitmap by a factor of 1.5 here, but we
// must also scale the larger one by a factor of 2 rather than scaling the
// small one by a factor of 4.
CHECK_THAT( BitmapAtScale(b, 3.33), SameAs(3, 1) );
CHECK_THAT( BitmapAtScale(b, 4 ), SameAs(4, 2) );
CHECK_THAT( BitmapAtScale(b, 5 ), SameAs(5, 1) );
// Finally check that things work as expected when we have 3 versions.
wxVector<wxBitmap> bitmaps;
bitmaps.push_back(normal);
bitmaps.push_back(middle);
bitmaps.push_back(bigger);
b = wxBitmapBundle::FromBitmaps(bitmaps);
CHECK_THAT( BitmapAtScale(b, 0 ), SameAs(1.0) );
CHECK_THAT( BitmapAtScale(b, 1 ), SameAs(1.0) );
CHECK_THAT( BitmapAtScale(b, 1.25), SameAs(1.0) );
CHECK_THAT( BitmapAtScale(b, 1.4 ), SameAs(1.5) );
CHECK_THAT( BitmapAtScale(b, 1.5 ), SameAs(1.5) );
CHECK_THAT( BitmapAtScale(b, 1.75), SameAs(1.5) );
CHECK_THAT( BitmapAtScale(b, 2 ), SameAs(2.0) );
CHECK_THAT( BitmapAtScale(b, 2.5 ), SameAs(2.0) );
CHECK_THAT( BitmapAtScale(b, 3 ), SameAs(2.0) );
CHECK_THAT( BitmapAtScale(b, 3.33), SameAs(3.0, 1.5) );
CHECK_THAT( BitmapAtScale(b, 4.25), SameAs(4.0, 2.0) );
CHECK_THAT( BitmapAtScale(b, 4.50), SameAs(4.5, 1.5) );
CHECK_THAT( BitmapAtScale(b, 5 ), SameAs(5.0, 1.0) );
// Another check to detect that the scale is computed correctly even when
// rounding is involved.
wxBitmap nonSquare(wxSize(51, 41));
nonSquare.SetScaleFactor(1.5);
b = wxBitmapBundle::FromBitmap(nonSquare);
CHECK( b.GetPreferredBitmapSizeAtScale(1.5) == nonSquare.GetSize() );
}
#ifdef wxHAS_DPI_INDEPENDENT_PIXELS
TEST_CASE("BitmapBundle::Scaled", "[bmpbundle]")
{
// Adding a bitmap with scale factor > 1 should create the bundle using the
// scaled size as default size.
wxBitmap scaled2x(64, 64);
scaled2x.SetScaleFactor(2);
CHECK( scaled2x.GetLogicalSize() == wxSize(32, 32) );
wxBitmapBundle b(scaled2x);
CHECK( b.GetDefaultSize() == wxSize(32, 32) );
// Retrieving this bitmap back from the bundle should preserve its scale.
scaled2x = b.GetBitmap(wxSize(64, 64));
CHECK( scaled2x.GetSize() == wxSize(64, 64) );
CHECK( scaled2x.GetScaleFactor() == 2 );
// And retrieving the bitmap from the bundle should set scale factor for it
// even if it hadn't originally been added with it.
b = wxBitmapBundle::FromBitmaps(wxBitmap(32, 32), wxBitmap(64, 64));
scaled2x = b.GetBitmap(wxSize(64, 64));
CHECK( scaled2x.GetSize() == wxSize(64, 64) );
CHECK( scaled2x.GetScaleFactor() == 2 );
// Using scaled bitmaps when there is more than one of them is a bad idea
// in general, as only physical size matters, but the default size should
// still be the scaled size of the smallest one.
b = wxBitmapBundle::FromBitmaps(scaled2x, wxBitmap(64, 64));
CHECK( b.GetDefaultSize() == wxSize(32, 32) );
}
#endif // wxHAS_DPI_INDEPENDENT_PIXELS
#ifdef wxHAS_SVG
TEST_CASE("BitmapBundle::FromSVG", "[bmpbundle][svg]")
{
static const char svg_data[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">"
"<svg width=\"200\" height=\"200\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns=\"http://www.w3.org/2000/svg\">"
"<g>"
"<circle cx=\"100\" cy=\"100\" r=\"50\" fill=\"blue\"/>"
"</g>"
"</svg>"
;
wxBitmapBundle b = wxBitmapBundle::FromSVG(svg_data, wxSize(20, 20));
REQUIRE( b.IsOk() );
CHECK( b.GetDefaultSize() == wxSize(20, 20) );
CHECK( b.GetBitmap(wxSize(32, 32)).GetSize() == wxSize(32, 32) );
// Check that not using XML header works too.
const char* svg_tag_start = strstr(svg_data, "<svg");
REQUIRE( svg_tag_start );
b = wxBitmapBundle::FromSVG(svg_data, wxSize(20, 20));
REQUIRE( b.IsOk() );
CHECK( b.GetBitmap(wxSize(16, 16)).GetSize() == wxSize(16, 16) );
}
TEST_CASE("BitmapBundle::FromSVG-alpha", "[bmpbundle][svg][alpha]")
{
static const char svg_data[] =
"<svg viewBox=\"0 0 100 100\">"
"<line x1=\"0\" y1=\"0\" x2=\"100%\" y2=\"100%\" stroke=\"#3f7fff\" stroke-width=\"71%\"/>"
"</svg>"
;
wxBitmapBundle b = wxBitmapBundle::FromSVG(svg_data, wxSize(2, 2));
REQUIRE( b.IsOk() );
wxImage img = b.GetBitmap(wxDefaultSize).ConvertToImage();
REQUIRE( img.HasAlpha() );
// Check that anti-aliased edge at 50% alpha round-trips (after possibly
// premultiplied storage in wxBitmap) to substantially original straight
// alpha pixel values in wxImage, allowing for roundoff error.
CHECK( (int)img.GetRed(0, 1) >= 0x3c );
CHECK( (int)img.GetRed(0, 1) <= 0x3f );
CHECK( (int)img.GetGreen(0, 1) >= 0x7b );
CHECK( (int)img.GetGreen(0, 1) <= 0x7f);
CHECK( (int)img.GetBlue(0, 1) == 0xff );
}
TEST_CASE("BitmapBundle::FromSVGFile", "[bmpbundle][svg][file]")
{
const wxSize size(20, 20); // completely arbitrary
CHECK( !wxBitmapBundle::FromSVGFile("horse.bmp", size).IsOk() );
wxBitmapBundle b = wxBitmapBundle::FromSVGFile("horse.svg", size);
REQUIRE( b.IsOk() );
CHECK( b.GetDefaultSize() == size );
}
// This can be used to test loading an arbitrary image file by setting the
// environment variable WX_TEST_IMAGE_PATH to point to it.
TEST_CASE("BitmapBundle::Load", "[.]")
{
wxString path;
REQUIRE( wxGetEnv("WX_TEST_SVG", &path) );
wxBitmapBundle bb = wxBitmapBundle::FromSVGFile(path, wxSize(32, 32));
REQUIRE( bb.IsOk() );
}
#endif // wxHAS_SVG
TEST_CASE("BitmapBundle::ArtProvider", "[bmpbundle][art]")
{
// Check that creating a bogus bundle fails as expected.
wxBitmapBundle b = wxArtProvider::GetBitmapBundle("bloordyblop");
CHECK( !b.IsOk() );
// And that creating a bundle using a standard icon works.
const wxSize size(16, 16);
b = wxArtProvider::GetBitmapBundle(wxART_INFORMATION, wxART_MENU, size);
CHECK( b.IsOk() );
CHECK( b.GetDefaultSize() == size );
#if wxUSE_ARTPROVIDER_TANGO
// Tango art provider is supposed to use 16px for the default size of the
// menu and button images and 24px for all the other ones, but we need to
// choose the client kind for which the current platform doesn't define its
// own default/fallback size to be able to test for it, i.e. this is the
// client for which GetNativeSizeHint() of the native art provider returns
// wxDefaultSize.
const wxArtClient artClient =
#ifdef __WXMSW__
wxART_TOOLBAR
#else
wxART_LIST
#endif
;
// We also need to use an image provided by Tango but not by the native art
// provider, but here we can at least avoid the platform checks by using an
// image not provided by any native providers.
b = wxArtProvider::GetBitmapBundle(wxART_REFRESH, artClient);
CHECK( b.IsOk() );
CHECK( b.GetDefaultSize() == wxSize(24, 24) );
#endif // wxUSE_ARTPROVIDER_TANGO
}
// This test only makes sense for the ports that actually support scaled
// bitmaps, which is the case for the ports using real logical pixels (they
// have to support bitmap scale for things to work) and MSW, which doesn't, but
// still implements support for at least storing and retrieving bitmap scale in
// its wxBitmap.
#if defined(wxHAS_DPI_INDEPENDENT_PIXELS) || defined(__WXMSW__)
TEST_CASE("BitmapBundle::Scale", "[bmpbundle][scale]")
{
// This is not a wxBitmapBundle test, strictly speaking, but check that
// setting scale factor works correctly for bitmaps, as wxBitmapBundle does
// this internally.
wxBitmap bmp;
bmp.CreateWithDIPSize(8, 8, 2);
#ifdef wxHAS_DPI_INDEPENDENT_PIXELS
CHECK( bmp.GetLogicalSize() == wxSize(8, 8) );
#endif
CHECK( bmp.GetDIPSize() == wxSize(8, 8) );
CHECK( bmp.GetSize() == wxSize(16, 16) );
CHECK( bmp.GetScaleFactor() == 2 );
wxBitmap bmp2(bmp);
bmp.SetScaleFactor(3);
CHECK( bmp2.GetScaleFactor() == 2 );
CHECK( bmp.GetScaleFactor() == 3 );
// Check that creating bitmap bundle from a bitmap with a scale factor
// works as expected.
wxBitmapBundle b = bmp2;
CHECK( b.GetDefaultSize() == wxSize(8, 8) );
}
TEST_CASE("BitmapBundle::ImageList", "[bmpbundle][imagelist]")
{
wxVector<wxBitmapBundle> images;
images.push_back(wxBitmapBundle::FromBitmaps(wxBitmap(16, 16), wxBitmap(32, 32)));
images.push_back(wxBitmapBundle::FromBitmap(wxBitmap(24, 24)));
images.push_back(wxBitmapBundle::FromBitmaps(wxBitmap(16, 16), wxBitmap(32, 32)));
// There are 2 bundles with preferred size of 32x32, so they should win.
const wxSize size = wxBitmapBundle::GetConsensusSizeFor(2.0, images);
CHECK( size == wxSize(32, 32) );
wxImageList iml(size.x, size.y);
for ( const auto& bundle : images )
{
wxBitmap bmp = bundle.GetBitmap(size);
REQUIRE( bmp.IsOk() );
CHECK( bmp.GetSize() == size );
REQUIRE( iml.Add(bmp) != -1 );
}
CHECK( iml.GetBitmap(0).GetSize() == size );
#ifdef wxHAS_DPI_INDEPENDENT_PIXELS
CHECK( iml.GetBitmap(0).GetScaleFactor() == 2 );
#endif
CHECK( iml.GetBitmap(1).GetSize() == size );
#ifdef wxHAS_DPI_INDEPENDENT_PIXELS
CHECK( iml.GetBitmap(1).GetScaleFactor() == Approx(1.3333333333) );
#endif
}
#endif // ports with scaled bitmaps support
TEST_CASE("BitmapBundle::GetConsensusSize", "[bmpbundle]")
{
// Just a trivial helper to make writing the tests below simpler.
struct Bundles
{
wxVector<wxBitmapBundle> vec;
void Add(int size)
{
vec.push_back(wxBitmapBundle::FromBitmap(wxSize(size, size)));
}
int GetConsensusSize(double scale) const
{
return wxBitmapBundle::GetConsensusSizeFor(scale, vec).y;
}
} bundles;
// When there is a tie, a larger size is chosen by default.
bundles.Add(16);
bundles.Add(24);
CHECK( bundles.GetConsensusSize(2) == 48 );
// Breaking the tie results in the smaller size winning now.
bundles.Add(16);
CHECK( bundles.GetConsensusSize(2) == 32 );
// Integer scaling factors should be preferred.
CHECK( bundles.GetConsensusSize(1.5) == 16 );
}
// This test is not really related to wxBitmapBundle, but is just here because
// this file already uses wxArtProvider and we don't have any tests for it
// specifically right now.
TEST_CASE("wxArtProvider::Delete", "[artprov]")
{
auto* artprov = new wxArtProvider{};
wxArtProvider::Push(artprov);
delete artprov;
}

View File

@@ -0,0 +1,364 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/graphics/boundingbox.cpp
// Purpose: wxGCDC bounding box unit tests
// Author: Vadim Zeitlin / Maarten Spoek / Toni Ruža
// Created: 2011-01-36
// Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
// (c) 2014 Toni Ruža <toni.ruza@gmail.com>
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#if wxUSE_GRAPHICS_CONTEXT
#include "wx/bitmap.h"
#include "wx/dcmemory.h"
#include "wx/dcgraph.h"
#include "wx/icon.h"
#include "wx/colour.h"
#include "wx/gdicmn.h"
// ----------------------------------------------------------------------------
// test class
// ----------------------------------------------------------------------------
class GCDCBoundingBoxTestCase : public CppUnit::TestCase
{
public:
GCDCBoundingBoxTestCase()
{
m_bmp.Create(100, 100);
m_dc.SelectObject(m_bmp);
m_gcdc = new wxGCDC(m_dc);
}
~GCDCBoundingBoxTestCase()
{
delete m_gcdc;
m_dc.SelectObject(wxNullBitmap);
m_bmp = wxNullBitmap;
}
virtual void setUp() override
{
m_gcdc->ResetBoundingBox();
}
private:
wxBitmap m_bmp;
wxMemoryDC m_dc;
wxGCDC *m_gcdc;
void AssertBox(int minX, int minY, int width, int height, int margin = 0)
{
int maxX = minX + width;
int maxY = minY + height;
// Allow for a margin of error due to different implementation
// specificities regarding drawing paths.
if ( margin )
{
#define WX_ASSERT_CLOSE(expected, actual, delta) \
WX_ASSERT_MESSAGE(("%d != %d", actual, expected), \
abs(actual - expected) <= delta)
WX_ASSERT_CLOSE(minX, m_gcdc->MinX(), margin);
WX_ASSERT_CLOSE(minY, m_gcdc->MinY(), margin);
WX_ASSERT_CLOSE(maxX, m_gcdc->MaxX(), margin);
WX_ASSERT_CLOSE(maxY, m_gcdc->MaxY(), margin);
#undef WX_ASSERT_CLOSE
}
else
{
CPPUNIT_ASSERT_EQUAL(minX, m_gcdc->MinX());
CPPUNIT_ASSERT_EQUAL(minY, m_gcdc->MinY());
CPPUNIT_ASSERT_EQUAL(maxX, m_gcdc->MaxX());
CPPUNIT_ASSERT_EQUAL(maxY, m_gcdc->MaxY());
}
}
CPPUNIT_TEST_SUITE( GCDCBoundingBoxTestCase );
CPPUNIT_TEST( DrawBitmap );
CPPUNIT_TEST( DrawIcon );
CPPUNIT_TEST( DrawLine );
CPPUNIT_TEST( CrossHair );
CPPUNIT_TEST( DrawArc );
CPPUNIT_TEST( DrawEllipticArc );
CPPUNIT_TEST( DrawPoint );
CPPUNIT_TEST( DrawLines );
#if wxUSE_SPLINES
CPPUNIT_TEST( DrawSpline );
#endif
CPPUNIT_TEST( DrawPolygon );
CPPUNIT_TEST( DrawPolyPolygon );
CPPUNIT_TEST( DrawRectangle );
CPPUNIT_TEST( DrawTwoRectangles );
CPPUNIT_TEST( DrawRectsOnTransformedDC );
CPPUNIT_TEST( DrawRoundedRectangle );
CPPUNIT_TEST( DrawRectangleAndReset );
CPPUNIT_TEST( DrawEllipse );
CPPUNIT_TEST( Blit );
CPPUNIT_TEST( StretchBlit );
CPPUNIT_TEST( DrawRotatedText );
CPPUNIT_TEST( DrawText );
CPPUNIT_TEST( GradientFillLinear );
CPPUNIT_TEST( GradientFillConcentric );
CPPUNIT_TEST( DrawCheckMark );
CPPUNIT_TEST_SUITE_END();
void DrawBitmap();
void DrawIcon();
void DrawLine();
void CrossHair();
void DrawArc();
void DrawEllipticArc();
void DrawPoint();
void DrawLines();
#if wxUSE_SPLINES
void DrawSpline();
#endif
void DrawPolygon();
void DrawPolyPolygon();
void DrawRectangle();
void DrawTwoRectangles();
void DrawRectsOnTransformedDC();
void DrawRoundedRectangle();
void DrawRectangleAndReset();
void DrawEllipse();
void Blit();
void StretchBlit();
void DrawRotatedText();
void DrawText();
void GradientFillLinear();
void GradientFillConcentric();
void DrawCheckMark();
wxDECLARE_NO_COPY_CLASS(GCDCBoundingBoxTestCase);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( GCDCBoundingBoxTestCase );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( GCDCBoundingBoxTestCase, "GCDCBoundingBoxTestCase" );
void GCDCBoundingBoxTestCase::DrawBitmap()
{
wxBitmap bitmap;
bitmap.Create(12, 12);
m_gcdc->DrawBitmap(bitmap, 5, 5);
AssertBox(5, 5, 12, 12);
}
void GCDCBoundingBoxTestCase::DrawIcon()
{
wxBitmap bitmap;
bitmap.Create(16, 16);
wxIcon icon;
icon.CopyFromBitmap(bitmap);
m_gcdc->DrawIcon(icon, 42, 42);
AssertBox(42, 42, 16, 16);
}
void GCDCBoundingBoxTestCase::DrawLine()
{
m_gcdc->DrawLine(10, 10, 20, 15);
AssertBox(10, 10, 10, 5);
}
void GCDCBoundingBoxTestCase::CrossHair()
{
int w, h;
m_gcdc->GetSize(&w, &h);
m_gcdc->CrossHair(33, 33);
AssertBox(0, 0, w, h);
}
void GCDCBoundingBoxTestCase::DrawArc()
{
m_gcdc->DrawArc(25, 30, 15, 40, 25, 40); // quarter circle
AssertBox(15, 30, 10, 10, 3);
}
void GCDCBoundingBoxTestCase::DrawEllipticArc()
{
m_gcdc->DrawEllipticArc(40, 50, 30, 20, 0, 180); // half circle
AssertBox(40, 50, 30, 10, 3);
}
void GCDCBoundingBoxTestCase::DrawPoint()
{
m_gcdc->DrawPoint(20, 20);
AssertBox(20, 20, 0, 0);
}
void GCDCBoundingBoxTestCase::DrawLines()
{
wxPoint points[4];
points[0] = wxPoint(10, 20);
points[1] = wxPoint(20, 10);
points[2] = wxPoint(30, 20);
points[3] = wxPoint(20, 30);
m_gcdc->DrawLines(4, points, 7, 8);
AssertBox(17, 18, 20, 20);
}
#if wxUSE_SPLINES
void GCDCBoundingBoxTestCase::DrawSpline()
{
wxPoint points[3];
points[0] = wxPoint(10, 30);
points[1] = wxPoint(20, 20);
points[2] = wxPoint(40, 50);
m_gcdc->DrawSpline(3, points);
AssertBox(10, 20, 30, 30, 5);
}
#endif // wxUSE_SPLINES
void GCDCBoundingBoxTestCase::DrawPolygon()
{
wxPoint points[3];
points[0] = wxPoint(10, 30);
points[1] = wxPoint(20, 10);
points[2] = wxPoint(30, 30);
m_gcdc->DrawPolygon(3, points, -5, -7);
AssertBox(5, 3, 20, 20);
}
void GCDCBoundingBoxTestCase::DrawPolyPolygon()
{
int lenghts[2];
lenghts[0] = 3;
lenghts[1] = 3;
wxPoint points[6];
points[0] = wxPoint(10, 30);
points[1] = wxPoint(20, 10);
points[2] = wxPoint(30, 30);
points[3] = wxPoint(20, 60);
points[4] = wxPoint(30, 40);
points[5] = wxPoint(40, 60);
m_gcdc->DrawPolyPolygon(2, lenghts, points, 12, 5);
AssertBox(22, 15, 30, 50, 4);
}
void GCDCBoundingBoxTestCase::DrawRectangle()
{
m_gcdc->DrawRectangle(2, 2, 12, 12);
AssertBox(2, 2, 12, 12);
}
void GCDCBoundingBoxTestCase::DrawRoundedRectangle()
{
m_gcdc->DrawRoundedRectangle(27, 27, 12, 12, 2);
AssertBox(27, 27, 12, 12);
}
void GCDCBoundingBoxTestCase::DrawEllipse()
{
m_gcdc->DrawEllipse(54, 45, 23, 12);
AssertBox(54, 45, 23, 12);
}
void GCDCBoundingBoxTestCase::Blit()
{
wxBitmap bitmap;
bitmap.Create(20, 20);
wxMemoryDC dc(bitmap);
m_gcdc->Blit(20, 10, 12, 7, &dc, 0, 0);
AssertBox(20, 10, 12, 7);
dc.SelectObject(wxNullBitmap);
}
void GCDCBoundingBoxTestCase::StretchBlit()
{
wxBitmap bitmap;
bitmap.Create(20, 20);
wxMemoryDC dc(bitmap);
m_gcdc->StretchBlit(30, 50, 5, 5, &dc, 0, 0, 12, 4);
AssertBox(30, 50, 5, 5);
dc.SelectObject(wxNullBitmap);
}
void GCDCBoundingBoxTestCase::DrawRotatedText()
{
wxString text("vertical");
wxCoord w, h;
m_gcdc->GetTextExtent(text, &w, &h);
m_gcdc->DrawRotatedText(text, 43, 22, -90);
AssertBox(43 - h, 22, h, w, 3);
}
void GCDCBoundingBoxTestCase::DrawText()
{
wxString text("H");
wxCoord w, h;
m_gcdc->GetTextExtent(text, &w, &h);
m_gcdc->DrawText(text, 3, 3);
AssertBox(3, 3, w, h, 3);
}
void GCDCBoundingBoxTestCase::GradientFillLinear()
{
wxRect rect(16, 16, 30, 40);
m_gcdc->GradientFillLinear(rect, *wxWHITE, *wxBLACK, wxNORTH);
AssertBox(16, 16, 30, 40);
}
void GCDCBoundingBoxTestCase::GradientFillConcentric()
{
wxRect rect(6, 6, 30, 40);
m_gcdc->GradientFillConcentric(rect, *wxWHITE, *wxBLACK, wxPoint(10, 10));
AssertBox(6, 6, 30, 40);
}
void GCDCBoundingBoxTestCase::DrawCheckMark()
{
m_gcdc->DrawCheckMark(32, 24, 16, 16);
AssertBox(32, 24, 16, 16);
}
void GCDCBoundingBoxTestCase::DrawRectangleAndReset()
{
m_gcdc->DrawRectangle(2, 2, 12, 12);
m_gcdc->ResetBoundingBox();
AssertBox(0, 0, 0, 0);
}
void GCDCBoundingBoxTestCase::DrawTwoRectangles()
{
m_gcdc->DrawRectangle(10, 15, 50, 30);
m_gcdc->DrawRectangle(15, 20, 55, 35);
AssertBox(10, 15, 60, 40);
}
void GCDCBoundingBoxTestCase::DrawRectsOnTransformedDC()
{
m_gcdc->DrawRectangle(10, 15, 50, 30);
m_gcdc->SetDeviceOrigin(15, 20);
m_gcdc->DrawRectangle(15, 20, 45, 35);
m_gcdc->SetDeviceOrigin(5, 10);
AssertBox(5, 5, 65, 60);
}
#endif // wxUSE_GRAPHICS_CONTEXT

View File

@@ -0,0 +1,757 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/graphics/clipper.cpp
// Purpose: wxDCClipper unit tests
// Author: Artur Wieczorek
// Created: 2022-12-27
// Copyright: (c) 2022 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#include <memory>
#include "wx/bitmap.h"
#include "wx/dcclient.h"
#include "wx/dcgraph.h"
#include "wx/dcmemory.h"
#include "wx/dcsvg.h"
#include "wx/app.h"
#include "wx/window.h"
#include "testfile.h"
#include "waitfor.h"
static const wxSize s_dcSize(260, 300);
static inline wxRect DeviceToLogical(wxDC& dc, const wxRect& r)
{
return wxRect(dc.DeviceToLogical(r.GetPosition()), dc.DeviceToLogicalRel(r.GetSize()));
}
static void NoTransform(wxDC& dc)
{
wxRect initClipBox;
dc.GetClippingBox(initClipBox);
{
const wxRect r(10, 20, 30, 40);
wxDCClipper clipper(dc, r);
wxRect clipBox;
dc.GetClippingBox(clipBox);
CHECK(r == clipBox);
}
wxRect clipBox;
dc.GetClippingBox(clipBox);
CHECK(initClipBox == clipBox);
}
static void ExternalTransform(wxDC& dc, bool useTransformMatrix)
{
#if wxUSE_DC_TRANSFORM_MATRIX
if ( useTransformMatrix && !dc.CanUseTransformMatrix() )
return;
#endif // wxUSE_DC_TRANSFORM_MATRIX
#if wxUSE_DC_TRANSFORM_MATRIX
if ( useTransformMatrix )
{
wxAffineMatrix2D m;
m.Translate(40, 75);
m.Scale(2.0, 3.0);
dc.SetTransformMatrix(m);
}
else
#endif // wxUSE_DC_TRANSFORM_MATRIX
{
dc.SetDeviceOrigin(10, 15);
dc.SetUserScale(0.5, 1.5);
dc.SetLogicalScale(4.0, 2.0);
dc.SetLogicalOrigin(-15, -20);
}
wxRect initClipBox;
dc.GetClippingBox(initClipBox);
{
const wxRect r(10, 20, 30, 40);
wxDCClipper clipper(dc, r);
wxRect clipBox;
dc.GetClippingBox(clipBox);
CHECK(r == clipBox);
}
wxRect clipBox;
dc.GetClippingBox(clipBox);
CHECK(initClipBox == clipBox);
}
static void InternalTransform(wxDC& dc, bool useTransformMatrix)
{
#if wxUSE_DC_TRANSFORM_MATRIX
if ( useTransformMatrix && !dc.CanUseTransformMatrix() )
return;
#endif // wxUSE_DC_TRANSFORM_MATRIX
wxRect initClipBox;
dc.GetClippingBox(initClipBox);
{
const wxRect r(10, 20, 30, 40);
wxDCClipper clipper(dc, r);
#if wxUSE_DC_TRANSFORM_MATRIX
if ( useTransformMatrix )
{
wxAffineMatrix2D m;
m.Translate(40, 75);
m.Scale(2.0, 3.0);
dc.SetTransformMatrix(m);
}
else
#endif // wxUSE_DC_TRANSFORM_MATRIX
{
dc.SetDeviceOrigin(10, 15);
dc.SetUserScale(0.5, 1.5);
dc.SetLogicalScale(4.0, 2.0);
dc.SetLogicalOrigin(-15, -20);
}
wxRect clipExpected = DeviceToLogical(dc, r);
wxRect clipBox;
dc.GetClippingBox(clipBox);
CHECK(clipExpected == clipBox);
}
wxRect initClipExpected = DeviceToLogical(dc, initClipBox);
wxRect clipBox;
dc.GetClippingBox(clipBox);
CHECK(initClipExpected == clipBox);
}
static void SpecificClipping(wxDC& dc)
{
wxRect initClipBox;
dc.GetClippingBox(initClipBox);
{
const wxRect r(10, 20, 30, 40);
wxDCClipper clipper(dc, r);
const wxRect r1(16, 25, 20, 30);
dc.SetClippingRegion(r1);
wxRect clipExpected = DeviceToLogical(dc, r1);
wxRect clipBox;
dc.GetClippingBox(clipBox);
CHECK(clipExpected == clipBox);
}
wxRect initClipExpected = DeviceToLogical(dc, initClipBox);
wxRect clipBox;
dc.GetClippingBox(clipBox);
CHECK(initClipExpected == clipBox);
}
static void InternalTransformSpecificClipping(wxDC& dc, bool useTransformMatrix)
{
#if wxUSE_DC_TRANSFORM_MATRIX
if ( useTransformMatrix && !dc.CanUseTransformMatrix() )
return;
#endif // wxUSE_DC_TRANSFORM_MATRIX
wxRect initClipBox;
dc.GetClippingBox(initClipBox);
{
const wxRect r(10, 20, 30, 40);
wxDCClipper clipper(dc, r);
const wxRect r1(16, 25, 20, 30);
dc.SetClippingRegion(r1);
#if wxUSE_DC_TRANSFORM_MATRIX
if ( useTransformMatrix )
{
wxAffineMatrix2D m;
m.Translate(40, 75);
m.Scale(2.0, 3.0);
dc.SetTransformMatrix(m);
}
else
#endif // wxUSE_DC_TRANSFORM_MATRIX
{
dc.SetDeviceOrigin(10, 15);
dc.SetUserScale(0.5, 1.5);
dc.SetLogicalScale(4.0, 2.0);
dc.SetLogicalOrigin(-15, -20);
}
wxRect clipExpected = DeviceToLogical(dc, r1);
wxRect clipBox;
dc.GetClippingBox(clipBox);
CHECK(clipExpected == clipBox);
}
wxRect initClipExpected = DeviceToLogical(dc, initClipBox);
wxRect clipBox;
dc.GetClippingBox(clipBox);
CHECK(initClipExpected == clipBox);
}
static void NoTransformEmbeddedClip(wxDC& dc)
{
wxRect initClipBox;
dc.GetClippingBox(initClipBox);
{
const wxRect r1(10, 20, 30, 40);
wxDCClipper clipper1(dc, r1);
{
const wxRect r2(15, 25, 20, 30);
wxDCClipper clipper2(dc, r2);
wxRect clipBox2;
dc.GetClippingBox(clipBox2);
CHECK(r2 == clipBox2);
}
wxRect clipBox1;
dc.GetClippingBox(clipBox1);
CHECK(r1 == clipBox1);
}
wxRect clipBox;
dc.GetClippingBox(clipBox);
CHECK(initClipBox == clipBox);
}
static void DCAttributes(wxDC& dc)
{
// Check if wxDC atrributes left unchanged
wxFont font = dc.GetFont().Bold().Smaller();
wxPen pen(*wxYELLOW, 2);
wxBrush brush = *wxBLUE_BRUSH;
wxDCFontChanger fontChanger(dc, font);
wxDCPenChanger penChanger(dc,pen);
wxDCBrushChanger brushChanger(dc, brush);
wxCoord chWidth = dc.GetCharWidth();
wxCoord chHeight = dc.GetCharHeight();
wxFontMetrics fm = dc.GetFontMetrics();
{
wxDCClipper clipper(dc, 10, 20, 30, 40);
}
CHECK(dc.GetFont() == font);
CHECK(dc.GetPen() == pen);
CHECK(dc.GetBrush() == brush);
CHECK(dc.GetCharWidth() == chWidth);
CHECK(dc.GetCharHeight() == chHeight);
wxFontMetrics fm2 = dc.GetFontMetrics();
CHECK(fm2.ascent == fm.ascent);
CHECK(fm2.averageWidth == fm.averageWidth);
CHECK(fm2.descent == fm.descent);
CHECK(fm2.externalLeading == fm.externalLeading);
CHECK(fm2.height == fm.height);
CHECK(fm2.internalLeading == fm.internalLeading);
}
TEST_CASE("ClipperTestCase::wxDC", "[clipper][dc]")
{
wxBitmap bmp(s_dcSize);
wxMemoryDC dc(bmp);
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
SECTION("NoTransform")
{
NoTransform(dc);
}
SECTION("ExternalTransform 1")
{
ExternalTransform(dc, false);
}
SECTION("ExternalTransform 2")
{
ExternalTransform(dc, true);
}
SECTION("InternalTransform 1")
{
InternalTransform(dc, false);
}
SECTION("InternalTransform 2")
{
InternalTransform(dc, true);
}
SECTION("SpecificClipping")
{
SpecificClipping(dc);
}
SECTION("InternalTransformSpecificClipping 1")
{
InternalTransformSpecificClipping(dc, false);
}
SECTION("InternalTransformSpecificClipping 2")
{
InternalTransformSpecificClipping(dc, true);
}
SECTION("NoTransformEmbeddedClip")
{
NoTransformEmbeddedClip(dc);
}
SECTION("DCAttributes")
{
DCAttributes(dc);
}
}
#if wxUSE_GRAPHICS_CONTEXT
TEST_CASE("ClipperTestCase::wxGCDC", "[clipper][dc][gcdc]")
{
#ifdef __WXMSW__
int depth = GENERATE(24, 32);
wxBitmap bmp(s_dcSize, depth);
#else
wxBitmap bmp(s_dcSize);
#endif
wxMemoryDC mdc(bmp);
mdc.SetBackground(*wxWHITE_BRUSH);
mdc.Clear();
wxGCDC dc(mdc);
dc.GetGraphicsContext()->SetAntialiasMode(wxANTIALIAS_NONE);
dc.GetGraphicsContext()->DisableOffset();
SECTION("NoTransform")
{
NoTransform(dc);
}
SECTION("ExternalTransform 1")
{
ExternalTransform(dc, false);
}
SECTION("ExternalTransform 2")
{
ExternalTransform(dc, true);
}
SECTION("InternalTransform 1")
{
InternalTransform(dc, false);
}
SECTION("InternalTransform 2")
{
InternalTransform(dc, true);
}
SECTION("SpecificClipping")
{
SpecificClipping(dc);
}
SECTION("InternalTransformSpecificClipping 1")
{
InternalTransformSpecificClipping(dc, false);
}
SECTION("InternalTransformSpecificClipping 2")
{
InternalTransformSpecificClipping(dc, true);
}
SECTION("NoTransformEmbeddedClip")
{
NoTransformEmbeddedClip(dc);
}
SECTION("DCAttributes")
{
DCAttributes(dc);
}
}
#ifdef __WXMSW__
#if wxUSE_GRAPHICS_GDIPLUS
TEST_CASE("ClipperTestCase::wxGCDC(GDI+)", "[clipper][dc][gcdc][gdiplus]")
{
int depth = GENERATE(24, 32);
wxBitmap bmp(s_dcSize, depth);
wxMemoryDC mdc(bmp);
mdc.SetBackground(*wxWHITE_BRUSH);
mdc.Clear();
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetGDIPlusRenderer();
REQUIRE(rend);
wxGraphicsContext* gc = rend->CreateContext(mdc);
gc->SetAntialiasMode(wxANTIALIAS_NONE);
gc->DisableOffset();
wxGCDC dc(gc);
SECTION("NoTransform")
{
NoTransform(dc);
}
SECTION("ExternalTransform 1")
{
ExternalTransform(dc, false);
}
SECTION("ExternalTransform 2")
{
ExternalTransform(dc, true);
}
SECTION("InternalTransform 1")
{
InternalTransform(dc, false);
}
SECTION("InternalTransform 2")
{
InternalTransform(dc, true);
}
SECTION("SpecificClipping")
{
SpecificClipping(dc);
}
SECTION("InternalTransformSpecificClipping 1")
{
InternalTransformSpecificClipping(dc, false);
}
SECTION("InternalTransformSpecificClipping 2")
{
InternalTransformSpecificClipping(dc, true);
}
SECTION("NoTransformEmbeddedClip")
{
NoTransformEmbeddedClip(dc);
}
SECTION("DCAttributes")
{
DCAttributes(dc);
}
}
#endif // wxUSE_GRAPHICS_GDIPLUS
#if wxUSE_GRAPHICS_DIRECT2D
TEST_CASE("ClipperTestCase::wxGCDC(Direct2D)", "[clipper][dc][gcdc][direct2d]")
{
if ( wxIsRunningUnderWine() )
{
WARN("Skipping tests known to fail in Wine");
}
else
{
int depth = GENERATE(24, 32);
wxBitmap bmp(s_dcSize, depth);
wxMemoryDC mdc(bmp);
mdc.SetBackground(*wxWHITE_BRUSH);
mdc.Clear();
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetDirect2DRenderer();
REQUIRE(rend);
wxGraphicsContext* gc = rend->CreateContext(mdc);
gc->SetAntialiasMode(wxANTIALIAS_NONE);
gc->DisableOffset();
wxGCDC dc(gc);
SECTION("NoTransform")
{
NoTransform(dc);
}
SECTION("ExternalTransform 1")
{
ExternalTransform(dc, false);
}
SECTION("ExternalTransform 2")
{
ExternalTransform(dc, true);
}
SECTION("InternalTransform 1")
{
InternalTransform(dc, false);
}
SECTION("InternalTransform 2")
{
InternalTransform(dc, true);
}
SECTION("SpecificClipping")
{
SpecificClipping(dc);
}
SECTION("InternalTransformSpecificClipping 1")
{
InternalTransformSpecificClipping(dc, false);
}
SECTION("InternalTransformSpecificClipping 2")
{
InternalTransformSpecificClipping(dc, true);
}
SECTION("NoTransformEmbeddedClip")
{
NoTransformEmbeddedClip(dc);
}
SECTION("DCAttributes")
{
DCAttributes(dc);
}
}
}
#endif // wxUSE_GRAPHICS_DIRECT2D
#endif // __WXMSW__
#if wxUSE_CAIRO
TEST_CASE("ClipperTestCase::wxGCDC(Cairo)", "[clipper][dc][gcdc][cairo]")
{
#ifdef __WXMSW__
int depth = GENERATE(24, 32);
wxBitmap bmp(s_dcSize, depth);
#else
wxBitmap bmp(s_dcSize);
#endif
wxMemoryDC mdc(bmp);
mdc.SetBackground(*wxWHITE_BRUSH);
mdc.Clear();
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetCairoRenderer();
REQUIRE(rend);
wxGraphicsContext* gc = rend->CreateContext(mdc);
gc->SetAntialiasMode(wxANTIALIAS_NONE);
gc->DisableOffset();
wxGCDC dc(gc);
SECTION("NoTransform")
{
NoTransform(dc);
}
SECTION("ExternalTransform 1")
{
ExternalTransform(dc, false);
}
SECTION("ExternalTransform 2")
{
ExternalTransform(dc, true);
}
SECTION("InternalTransform 1")
{
InternalTransform(dc, false);
}
SECTION("InternalTransform 2")
{
InternalTransform(dc, true);
}
SECTION("SpecificClipping")
{
SpecificClipping(dc);
}
SECTION("InternalTransformSpecificClipping 1")
{
InternalTransformSpecificClipping(dc, false);
}
SECTION("InternalTransformSpecificClipping 2")
{
InternalTransformSpecificClipping(dc, true);
}
SECTION("NoTransformEmbeddedClip")
{
NoTransformEmbeddedClip(dc);
}
SECTION("DCAttributes")
{
DCAttributes(dc);
}
}
#endif // wxUSE_CAIRO
#endif // wxUSE_GRAPHICS_CONTEXT
#if wxUSE_SVG
TEST_CASE("ClipperTestCase::wxSVGFileDC", "[clipper][dc][svgdc]")
{
TestFile tf;
wxSVGFileDC dc(tf.GetName(), s_dcSize.x, s_dcSize.y);
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
SECTION("NoTransform")
{
NoTransform(dc);
}
SECTION("ExternalTransform 1")
{
ExternalTransform(dc, false);
}
SECTION("ExternalTransform 2")
{
ExternalTransform(dc, true);
}
SECTION("InternalTransform 1")
{
InternalTransform(dc, false);
}
SECTION("InternalTransform 2")
{
InternalTransform(dc, true);
}
SECTION("SpecificClipping")
{
SpecificClipping(dc);
}
SECTION("InternalTransformSpecificClipping 1")
{
InternalTransformSpecificClipping(dc, false);
}
SECTION("InternalTransformSpecificClipping 2")
{
InternalTransformSpecificClipping(dc, true);
}
SECTION("NoTransformEmbeddedClip")
{
NoTransformEmbeddedClip(dc);
}
SECTION("DCAttributes")
{
DCAttributes(dc);
}
}
#endif // wxUSE_SVG
TEST_CASE("ClipperTestCase::wxPaintDC", "[clipper][dc][paintdc]")
{
// Ensure window is shown and large enough for testing
wxTheApp->GetTopWindow()->Raise();
REQUIRE(wxTheApp->GetTopWindow()->IsShown());
wxSize winSize = wxTheApp->GetTopWindow()->GetSize();
winSize.x = wxMax(winSize.x, s_dcSize.x + 50);
winSize.y = wxMax(winSize.y, s_dcSize.y + 50);
wxTheApp->GetTopWindow()->SetSize(winSize);
#if defined(__WXGTK__)
// Under wxGTK we need to have two children (at least) because if there
// is one child its paint area is set to fill the whole parent frame.
std::unique_ptr<wxWindow> w0(new wxWindow(wxTheApp->GetTopWindow(), wxID_ANY));
#endif // wxGTK
std::unique_ptr<wxWindow> win(new wxWindow(wxTheApp->GetTopWindow(), wxID_ANY, wxPoint(0, 0)));
win->SetClientSize(s_dcSize);
// Wait for the first paint event to be sure
// that window really has its final size.
wxWindow* testWin = win.get();
{
WaitForPaint waitForPaint(testWin);
testWin->Show();
waitForPaint.YieldUntilPainted();
}
bool paintExecuted = false;
testWin->Bind(wxEVT_PAINT, [=, &paintExecuted](wxPaintEvent&)
{
wxPaintDC dc(testWin);
REQUIRE(dc.GetSize() == s_dcSize);
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
SECTION("NoTransform")
{
NoTransform(dc);
}
SECTION("ExternalTransform 1")
{
ExternalTransform(dc, false);
}
SECTION("ExternalTransform 2")
{
ExternalTransform(dc, true);
}
SECTION("InternalTransform 1")
{
InternalTransform(dc, false);
}
SECTION("InternalTransform 2")
{
InternalTransform(dc, true);
}
SECTION("SpecificClipping")
{
SpecificClipping(dc);
}
SECTION("InternalTransformSpecificClipping 1")
{
InternalTransformSpecificClipping(dc, false);
}
SECTION("InternalTransformSpecificClipping 2")
{
InternalTransformSpecificClipping(dc, true);
}
SECTION("NoTransformEmbeddedClip")
{
NoTransformEmbeddedClip(dc);
}
SECTION("DCAttributes")
{
DCAttributes(dc);
}
paintExecuted = true;
});
testWin->Refresh();
testWin->Update();
// Wait for update to be done
YieldForAWhile();
CHECK(paintExecuted == true);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,202 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/graphics/colour.cpp
// Purpose: wxColour unit test
// Author: Vadim Zeitlin
// Created: 2009-09-19
// Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#include "wx/colour.h"
#include "asserthelper.h"
// ----------------------------------------------------------------------------
// helpers for checking wxColour RGB[A] values
// ----------------------------------------------------------------------------
typedef wxColour::ChannelType ChannelType;
class ColourRGBMatcher : public Catch::MatcherBase<wxColour>
{
public:
ColourRGBMatcher(ChannelType red, ChannelType green, ChannelType blue)
: m_red(red),
m_green(green),
m_blue(blue)
{
}
bool match(const wxColour& c) const override
{
return c.Red() == m_red && c.Green() == m_green && c.Blue() == m_blue;
}
std::string describe() const override
{
return wxString::Format("!= RGB(%#02x, %#02x, %#02x)",
m_red, m_green, m_blue).ToStdString();
}
protected:
const ChannelType m_red, m_green, m_blue;
};
class ColourRGBAMatcher : public ColourRGBMatcher
{
public:
ColourRGBAMatcher(ChannelType red, ChannelType green, ChannelType blue,
ChannelType alpha)
: ColourRGBMatcher(red, green, blue),
m_alpha(alpha)
{
}
bool match(const wxColour& c) const override
{
return ColourRGBMatcher::match(c) && c.Alpha() == m_alpha;
}
std::string describe() const override
{
return wxString::Format("!= RGBA(%#02x, %#02x, %#02x, %#02x)",
m_red, m_green, m_blue, m_alpha).ToStdString();
}
private:
const ChannelType m_alpha;
};
inline
ColourRGBMatcher
RGBSameAs(ChannelType red, ChannelType green, ChannelType blue)
{
return ColourRGBMatcher(red, green, blue);
}
inline
ColourRGBAMatcher
RGBASameAs(ChannelType red, ChannelType green, ChannelType blue, ChannelType alpha)
{
return ColourRGBAMatcher(red, green, blue, alpha);
}
// ----------------------------------------------------------------------------
// tests
// ----------------------------------------------------------------------------
TEST_CASE("wxColour::GetSetRGB", "[colour][rgb]")
{
wxColour c;
c.SetRGB(0x123456);
CHECK( c.Red() == 0x56 );
CHECK( c.Green() == 0x34 );
CHECK( c.Blue() == 0x12 );
CHECK( c.Alpha() == wxALPHA_OPAQUE );
CHECK( c == wxColour(0x123456) );
CHECK( c.GetRGB() == 0x123456 );
c.SetRGBA(0xaabbccdd);
CHECK( c.Red() == 0xdd);
CHECK( c.Green() == 0xcc);
CHECK( c.Blue() == 0xbb);
// wxX11 doesn't support alpha at all currently.
#ifndef __WXX11__
CHECK( c.Alpha() == 0xaa );
#endif // __WXX11__
// FIXME: at least under wxGTK wxColour ctor doesn't take alpha channel
// into account: bug or feature?
//CHECK( c == wxColour(0xaabbccdd) );
CHECK( c.GetRGB() == 0xbbccdd );
#ifndef __WXX11__
CHECK( c.GetRGBA() == 0xaabbccdd );
#endif // __WXX11__
}
TEST_CASE("wxColour::FromString", "[colour][string]")
{
CHECK_THAT( wxColour("rgb(11, 22, 33)"), RGBSameAs(11, 22, 33) );
// wxX11 doesn't support alpha at all currently.
#ifndef __WXX11__
CHECK_THAT( wxColour("rgba(11, 22, 33, 0.5)"), RGBASameAs(11, 22, 33, 128) );
CHECK_THAT( wxColour("rgba( 11, 22, 33, 0.5 )"), RGBASameAs(11, 22, 33, 128) );
#endif // __WXX11__
CHECK_THAT( wxColour("#aabbcc"), RGBSameAs(0xaa, 0xbb, 0xcc) );
CHECK_THAT( wxColour("red"), RGBSameAs(0xff, 0, 0) );
wxColour col;
CHECK( !wxFromString("rgb(1, 2)", &col) );
CHECK( !wxFromString("rgba(1, 2, 3.456)", &col) );
CHECK( !wxFromString("rgba(1, 2, 3.456, foo)", &col) );
}
TEST_CASE("wxColour::GetAsString", "[colour][string]")
{
CHECK( wxColour().GetAsString() == "" );
wxColour red("red");
CHECK( red.GetAsString() == "red" );
CHECK( red.GetAsString(wxC2S_CSS_SYNTAX) == "rgb(255, 0, 0)" );
CHECK( red.GetAsString(wxC2S_HTML_SYNTAX) == "#FF0000" );
}
TEST_CASE("wxColour::GetLuminance", "[colour][luminance]")
{
CHECK( wxBLACK->GetLuminance() == Approx(0.0) );
CHECK( wxWHITE->GetLuminance() == Approx(1.0) );
CHECK( wxRED->GetLuminance() > 0 );
CHECK( wxRED->GetLuminance() < 1 );
}
TEST_CASE("wxColour::IsXXX", "[colour][opacity]")
{
CHECK(wxColour{ 0, 0, 0, 0 }.IsTransparent());
CHECK_FALSE(wxColour{ 0, 0, 0, 1 }.IsTransparent());
CHECK(wxColour{ 0, 0, 0, 255 }.IsOpaque());
CHECK_FALSE(wxColour{ 0, 0, 0, 1 }.IsOpaque());
CHECK(wxColour{ 0, 0, 0, 254 }.IsTranslucent());
CHECK(wxColour{ 0, 0, 0, 10 }.IsTranslucent());
CHECK_FALSE(wxColour{ 0, 0, 0, 0 }.IsTranslucent());
CHECK_FALSE(wxColour{ 0, 0, 0, 255 }.IsTranslucent());
}
TEST_CASE("wxColour::Database", "[colour][database]")
{
wxColourDatabase db;
// Check that we can add custom colours.
db.AddColour("NQB", wxColour(0x010203)); // Not quite black.
CHECK_THAT( db.Find("nqb"), RGBSameAs(0x03, 0x02, 0x01) );
// Unfortunately we can't check that all colours round trip because this is
// not the case for the colours present in the database under multiple
// names, such as "GREY" and "GRAY" for example. But we can at least check
// that the name found for all colours uses the same colour.
for ( const auto& name : db.GetAllNames() )
{
const wxColour& colour = db.Find(name);
const wxString& maybeOtherName = db.FindName(colour);
CHECK( db.Find(maybeOtherName) == colour );
}
// Check that green uses CSS value by default.
CHECK_THAT( db.Find("green"), RGBSameAs(0, 0x80, 0) );
// But we can use the legacy value for it too.
db.UseScheme(wxColourDatabase::Traditional);
CHECK_THAT( db.Find("green"), RGBSameAs(0, 0xff, 0) );
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,156 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/graphics/ellipsization.cpp
// Purpose: wxControlBase::*Ellipsize* unit test
// Author: Francesco Montorsi
// Created: 2010-03-10
// Copyright: (c) 2010 Francesco Montorsi
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#include "wx/control.h"
#include "wx/dcmemory.h"
// ----------------------------------------------------------------------------
// test class
// ----------------------------------------------------------------------------
TEST_CASE("Ellipsization::NormalCase", "[ellipsization]")
{
wxMemoryDC dc;
static const char *stringsToTest[] =
{
"N",
".",
"x",
"foobar",
"\xCE\xB1", // U03B1 (GREEK SMALL LETTER ALPHA)
"Another test",
"a very very very very very very very long string",
// alpha+beta+gamma+delta+epsilon+zeta+eta+theta+iota
"\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4\xCE\xB5\xCE\xB6\xCE\xB7\xCE\xB8\xCE\xB9",
"\t", "\t\t\t\t\t", "a\tstring\twith\ttabs",
"\n", "\n\n\n\n\n", "a\nstring\nwith\nnewlines",
"&", "&&&&&&&", "a&string&with&newlines",
"\t\n&", "a\t\n&string\t\n&with\t\n&many\t\n&chars"
};
static const int flagsToTest[] =
{
0,
wxELLIPSIZE_FLAGS_PROCESS_MNEMONICS,
wxELLIPSIZE_FLAGS_EXPAND_TABS,
wxELLIPSIZE_FLAGS_PROCESS_MNEMONICS | wxELLIPSIZE_FLAGS_EXPAND_TABS
};
static const wxEllipsizeMode modesToTest[] =
{
wxELLIPSIZE_START,
wxELLIPSIZE_MIDDLE,
wxELLIPSIZE_END
};
const int charWidth = dc.GetCharWidth();
int widthsToTest[] = { 6*charWidth, 10*charWidth, 15*charWidth };
for ( unsigned int s = 0; s < WXSIZEOF(stringsToTest); s++ )
{
const wxString str = wxString::FromUTF8(stringsToTest[s]);
for ( unsigned int f = 0; f < WXSIZEOF(flagsToTest); f++ )
{
for ( unsigned int m = 0; m < WXSIZEOF(modesToTest); m++ )
{
for ( unsigned int w = 0; w < WXSIZEOF(widthsToTest); w++ )
{
wxString ret = wxControl::Ellipsize
(
str,
dc,
modesToTest[m],
widthsToTest[w],
flagsToTest[f]
);
// Note that we must measure the width of the text that
// will be rendered, and when mnemonics are used, this
// means we have to remove them first.
const wxString
displayed = flagsToTest[f] & wxELLIPSIZE_FLAGS_PROCESS_MNEMONICS
? wxControl::RemoveMnemonics(ret)
: ret;
const int
width = dc.GetMultiLineTextExtent(displayed).GetWidth();
WX_ASSERT_MESSAGE
(
(
"Test #(%u,%u.%u): %s\n\"%s\" -> \"%s\"; width=%dpx > %dpx",
s, f, m,
dc.GetFont().GetNativeFontInfoUserDesc(),
str,
ret,
width,
widthsToTest[w]
),
width <= widthsToTest[w]
);
}
}
}
}
}
TEST_CASE("Ellipsization::EnoughSpace", "[ellipsization]")
{
// No ellipsization should occur if there's plenty of space.
wxMemoryDC dc;
wxString testString("some label");
const int width = dc.GetTextExtent(testString).GetWidth() + 50;
CHECK( wxControl::Ellipsize(testString, dc, wxELLIPSIZE_START, width) == testString );
CHECK( wxControl::Ellipsize(testString, dc, wxELLIPSIZE_MIDDLE, width) == testString );
CHECK( wxControl::Ellipsize(testString, dc, wxELLIPSIZE_END, width) == testString );
}
TEST_CASE("Ellipsization::VeryLittleSpace", "[ellipsization]")
{
// If there's not enough space, the shortened label should still contain "..." and one character
wxMemoryDC dc;
const int width = dc.GetTextExtent("s...").GetWidth();
CHECK( wxControl::Ellipsize("some label", dc, wxELLIPSIZE_START, width) == "...l" );
CHECK( wxControl::Ellipsize("some label", dc, wxELLIPSIZE_MIDDLE, width) == "s..." );
CHECK( wxControl::Ellipsize("some label1", dc, wxELLIPSIZE_MIDDLE, width) == "s..." );
CHECK( wxControl::Ellipsize("some label", dc, wxELLIPSIZE_END, width) == "s..." );
}
TEST_CASE("Ellipsization::HasThreeDots", "[ellipsization]")
{
wxMemoryDC dc;
wxString testString("some longer text");
const int width = dc.GetTextExtent(testString).GetWidth() - 5;
CHECK( wxControl::Ellipsize(testString, dc, wxELLIPSIZE_START, width).StartsWith("...") );
CHECK( !wxControl::Ellipsize(testString, dc, wxELLIPSIZE_START, width).EndsWith("...") );
CHECK( wxControl::Ellipsize(testString, dc, wxELLIPSIZE_END, width).EndsWith("...") );
CHECK( wxControl::Ellipsize(testString, dc, wxELLIPSIZE_MIDDLE, width).Contains("...") );
CHECK( !wxControl::Ellipsize(testString, dc, wxELLIPSIZE_MIDDLE, width).StartsWith("...") );
CHECK( !wxControl::Ellipsize(testString, dc, wxELLIPSIZE_MIDDLE, width).EndsWith("...") );
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,303 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/graphics/graphmatrix.cpp
// Purpose: Graphics matrix unit test
// Author: Artur Wieczorek
// Created: 2016-09-18
// Copyright: (c) 2016 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////
#include "testprec.h"
#if wxUSE_GRAPHICS_CONTEXT
#include "wx/graphics.h"
#include "wx/dcmemory.h"
#include <memory>
static void InitState(wxGraphicsContext* gc);
static void InvertMatrix(wxGraphicsContext* gc);
static void Concat1(wxGraphicsContext* gc);
static void Concat2(wxGraphicsContext* gc);
static void Concat3(wxGraphicsContext* gc);
TEST_CASE("GraphicsMatrixTestCase::DefaultRenderer", "[graphmatrix][default]")
{
wxBitmap bmp(100, 100);
wxMemoryDC dc(bmp);
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetDefaultRenderer();
REQUIRE(rend);
std::unique_ptr<wxGraphicsContext> gc(rend->CreateContext(dc));
REQUIRE(gc.get());
SECTION("InitState")
{
InitState(gc.get());
}
SECTION("InvertMatrix")
{
InvertMatrix(gc.get());
}
SECTION("Concat1")
{
Concat1(gc.get());
}
SECTION("Concat2")
{
Concat2(gc.get());
}
SECTION("Concat3")
{
Concat3(gc.get());
}
}
#ifdef __WXMSW__
#if wxUSE_GRAPHICS_GDIPLUS
TEST_CASE("GraphicsMatrixTestCase::GDIPlusRenderer", "[graphmatrix][gdiplus]")
{
wxBitmap bmp(100, 100);
wxMemoryDC dc(bmp);
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetGDIPlusRenderer();
REQUIRE(rend);
std::unique_ptr<wxGraphicsContext> gc(rend->CreateContext(dc));
REQUIRE(gc.get());
SECTION("InitState")
{
InitState(gc.get());
}
SECTION("InvertMatrix")
{
InvertMatrix(gc.get());
}
SECTION("Concat1")
{
Concat1(gc.get());
}
SECTION("Concat2")
{
Concat2(gc.get());
}
SECTION("Concat3")
{
Concat3(gc.get());
}
}
#endif // wxUSE_GRAPHICS_GDIPLUS
#if wxUSE_GRAPHICS_DIRECT2D
TEST_CASE("GraphicsMatrixTestCase::Direct2DRenderer", "[graphmatrix][direct2d]")
{
wxBitmap bmp(100, 100);
wxMemoryDC dc(bmp);
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetDirect2DRenderer();
REQUIRE(rend);
std::unique_ptr<wxGraphicsContext> gc(rend->CreateContext(dc));
REQUIRE(gc.get());
SECTION("InitState")
{
InitState(gc.get());
}
SECTION("InvertMatrix")
{
InvertMatrix(gc.get());
}
SECTION("Concat1")
{
Concat1(gc.get());
}
SECTION("Concat2")
{
Concat2(gc.get());
}
SECTION("Concat3")
{
Concat3(gc.get());
}
}
#endif // wxUSE_GRAPHICS_DIRECT2D
#endif // __WXMSW__
#if wxUSE_CAIRO
TEST_CASE("GraphicsMatrixTestCase::CairoRenderer", "[graphmatrix][cairo]")
{
wxBitmap bmp(100, 100);
wxMemoryDC dc(bmp);
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetCairoRenderer();
REQUIRE(rend);
std::unique_ptr<wxGraphicsContext> gc(rend->CreateContext(dc));
REQUIRE(gc.get());
SECTION("InitState")
{
InitState(gc.get());
}
SECTION("InvertMatrix")
{
InvertMatrix(gc.get());
}
SECTION("Concat1")
{
Concat1(gc.get());
}
SECTION("Concat2")
{
Concat2(gc.get());
}
SECTION("Concat3")
{
Concat3(gc.get());
}
}
#endif // wxUSE_CAIRO
// ===== Implementation =====
static inline double RoundVal(double v)
{
wxString s = wxString::Format("%g", v);
s.ToDouble(&v);
return v;
}
static void CheckMatrix(const wxGraphicsMatrix& m,
double a, double b, double c, double d,
double tx, double ty)
{
double cur_a, cur_b, cur_c, cur_d, cur_tx, cur_ty;
m.Get(&cur_a, &cur_b, &cur_c, &cur_d, &cur_tx, &cur_ty);
wxString msg;
if ( RoundVal(a) != RoundVal(cur_a) )
{
if ( !msg.empty() )
{
msg += "\n- ";
}
msg += wxString::Format("Invalid m11 value: Actual: %g Expected: %g",
cur_a, a );
}
if ( RoundVal(b) != RoundVal(cur_b) )
{
if ( !msg.empty() )
{
msg += "\n- ";
}
msg += wxString::Format("Invalid m12 value: Actual: %g Expected: %g",
cur_b, b );
}
if ( RoundVal(c) != RoundVal(cur_c) )
{
if ( !msg.empty() )
{
msg += "\n- ";
}
msg += wxString::Format("Invalid m21 value: Actual: %g Expected: %g",
cur_c, c );
}
if ( RoundVal(d) != RoundVal(cur_d) )
{
if ( !msg.empty() )
{
msg += "\n- ";
}
msg += wxString::Format("Invalid m22 value: Actual: %g Expected: %g",
cur_d, d );
}
if ( RoundVal(tx) != RoundVal(cur_tx) )
{
if ( !msg.empty() )
{
msg += "\n- ";
}
msg += wxString::Format("Invalid tx value: Actual: %g Expected: %g",
cur_tx, tx );
}
if ( RoundVal(ty) != RoundVal(cur_ty) )
{
if ( !msg.empty() )
{
msg += "\n- ";
}
msg += wxString::Format("Invalid ty value: Actual: %g Expected: %g",
cur_ty, ty );
}
if( !msg.empty() )
{
wxCharBuffer buffer = msg.ToUTF8();
FAIL_CHECK( buffer.data() );
}
}
static void InitState(wxGraphicsContext* gc)
{
wxGraphicsMatrix m = gc->CreateMatrix();
CheckMatrix(m, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
}
static void InvertMatrix(wxGraphicsContext* gc)
{
wxGraphicsMatrix m = gc->CreateMatrix(2.0, 1.0, 1.0, 1.0, 1.0, 1.0);
m.Invert();
CheckMatrix(m, 1.0, -1.0, -1.0, 2.0, 0.0, -1.0);
}
static void Concat1(wxGraphicsContext* gc)
{
wxGraphicsMatrix m1 = gc->CreateMatrix(0.9, 0.4, -0.4, 0.9, 0.0, 0.0);
wxGraphicsMatrix m2 = gc->CreateMatrix(1.0, 0.0, 0.0, 1.0, 3.0, 5.0);
m1.Concat(m2);
CheckMatrix(m1, 0.9, 0.4, -0.4, 0.9, 0.7, 5.7);
}
static void Concat2(wxGraphicsContext* gc)
{
wxGraphicsMatrix m1 = gc->CreateMatrix(0.9, 0.4, -0.4, 0.9, 0.0, 0.0);
wxGraphicsMatrix m2 = gc->CreateMatrix(1.0, 0.0, 0.0, 1.0, 3.0, 5.0);
m2.Concat(m1);
CheckMatrix(m2, 0.9, 0.4, -0.4, 0.9, 3.0, 5.0);
}
static void Concat3(wxGraphicsContext* gc)
{
wxGraphicsMatrix m1 = gc->CreateMatrix(0.9, 0.4, -0.4, 0.9, 0.0, 0.0);
wxGraphicsMatrix m2 = gc->CreateMatrix(1.0, 0.0, 0.0, 1.0, 3.0, 5.0);
wxGraphicsMatrix m = m1;
m.Concat(m2);
CheckMatrix(m, 0.9, 0.4, -0.4, 0.9, 0.7, 5.7);
}
#endif // wxUSE_GRAPHICS_CONTEXT

View File

@@ -0,0 +1,851 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/graphics/grappath.cpp
// Purpose: graphics path unit tests
// Author: Artur Wieczorek
// Created: 2018-07-01
// Copyright: (c) 2018 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#if wxUSE_GRAPHICS_CONTEXT
#include "wx/bitmap.h"
#include "wx/dcmemory.h"
#include "wx/dcgraph.h"
#include <memory>
static void DoAllTests(wxGraphicsContext* gc);
// For MSW we have individual test cases for each graphics renderer
// so we don't need to execute tests with default renderer.
#ifndef __WXMSW__
TEST_CASE("GraphicsPathTestCase", "[path]")
{
wxBitmap bmp(500, 500);
wxMemoryDC mdc(bmp);
std::unique_ptr<wxGraphicsContext> gc(wxGraphicsRenderer::GetDefaultRenderer()->CreateContext(mdc));
REQUIRE(gc);
DoAllTests(gc.get());
}
#else
#if wxUSE_GRAPHICS_GDIPLUS
TEST_CASE("GraphicsPathTestCaseGDIPlus", "[path][gdi+]")
{
wxBitmap bmp(500, 500);
wxMemoryDC mdc(bmp);
std::unique_ptr<wxGraphicsContext> gc(wxGraphicsRenderer::GetGDIPlusRenderer()->CreateContext(mdc));
REQUIRE(gc);
DoAllTests(gc.get());
}
#endif // wxUSE_GRAPHICS_GDIPLUS
#if wxUSE_GRAPHICS_DIRECT2D
TEST_CASE("GraphicsPathTestCaseDirect2D", "[path][d2d]")
{
if ( wxIsRunningUnderWine() )
return;
wxBitmap bmp(500, 500);
wxMemoryDC mdc(bmp);
std::unique_ptr<wxGraphicsContext> gc(wxGraphicsRenderer::GetDirect2DRenderer()->CreateContext(mdc));
REQUIRE(gc);
DoAllTests(gc.get());
}
#endif // wxUSE_GRAPHICS_DIRECT2D
#endif // __WXMSW__ / !__WXMSW__
#if wxUSE_CAIRO
TEST_CASE("GraphicsPathTestCaseCairo", "[path][cairo]")
{
wxBitmap bmp(500, 500);
wxMemoryDC mdc(bmp);
std::unique_ptr<wxGraphicsContext> gc(wxGraphicsRenderer::GetCairoRenderer()->CreateContext(mdc));
REQUIRE(gc);
DoAllTests(gc.get());
}
#endif // wxUSE_CAIRO
#define WX_CHECK_POINT(p1, p2, tolerance) \
CHECK(fabs(p1.m_x - p2.m_x) <= tolerance); \
CHECK(fabs(p1.m_y - p2.m_y) <= tolerance)
static void TestCurrentPoint(wxGraphicsContext* gc)
{
// No current point
{
wxGraphicsPath path = gc->CreatePath();
// Should return (0, 0) if current point is not yet set.
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(0, 0), 0);
}
// MoveToPoint
{
wxGraphicsPath path = gc->CreatePath();
wxPoint2DDouble pt(27, 35);
path.MoveToPoint(pt);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, pt, 1E-3);
}
// AddLineToPoint - no current point
{
wxGraphicsPath path = gc->CreatePath();
wxPoint2DDouble pt(27, 35);
path.AddLineToPoint(pt);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, pt, 1E-3);
}
// AddLineToPoint
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(10, 18);
wxPoint2DDouble pt(37, 45);
path.AddLineToPoint(pt);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, pt, 1E-3);
}
// AddArc - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble r = 40;
path.AddArc(x, y, r, 0, M_PI/2, true);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x, y + r), 1E-3);
}
// AddArc
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(20, 38);
const wxDouble x = 200;
const wxDouble y = 50;
const wxDouble r = 40;
path.AddArc(x, y, r, 0, M_PI / 2, true);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x, y + r), 1E-3);
}
// AddArcToPoint - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x1 = 80;
const wxDouble y1 = 80;
const wxDouble x2 = -30;
const wxDouble y2 = y1;
const wxDouble r = 20;
wxASSERT(x1 == y1 && y2 == y1); // alpha = 45 deg
double d = r / tan(45 / 180.0 * M_PI / 2.0);
path.AddArcToPoint(x1, y1, x2, y2, r);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x1 - d, y2), 1E-3);
}
// AddArcToPoint
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 20;
const wxDouble y0 = 20;
path.MoveToPoint(x0, y0);
const wxDouble x1 = 80;
const wxDouble y1 = 80;
const wxDouble x2 = 140;
const wxDouble y2 = y1;
const wxDouble r = 20;
wxASSERT(x0 == y0 && x1 == y1 && y2 == y1); // alpha = 135 deg
double d = r / tan(135 / 180.0 * M_PI / 2.0);
path.AddArcToPoint(x1, y1, x2, y2, r);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x1 + d, y2), 1E-3);
}
// AddCurveToPoint - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x1 = 102;
const wxDouble y1 = 230;
const wxDouble x2 = 153;
const wxDouble y2 = 25;
const wxDouble x3 = 230;
const wxDouble y3 = 128;
path.AddCurveToPoint(x1, y1, x2, y2, x3, y3);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x3, y3), 1E-3);
}
// AddCurveToPoint
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 25;
const wxDouble y0 = 128;
path.MoveToPoint(x0, y0);
const wxDouble x1 = 102;
const wxDouble y1 = 230;
const wxDouble x2 = 153;
const wxDouble y2 = 25;
const wxDouble x3 = 230;
const wxDouble y3 = 128;
path.AddCurveToPoint(x1, y1, x2, y2, x3, y3);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x3, y3), 1E-3);
}
// AddQuadCurveToPoint - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x1 = 200;
const wxDouble y1 = 200;
const wxDouble x2 = 300;
const wxDouble y2 = 100;
path.AddQuadCurveToPoint(x1, y1, x2, y2);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x2, y2), 1E-3);
}
// AddQuadCurveToPoint
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 20;
const wxDouble y0 = 100;
path.MoveToPoint(x0, y0);
const wxDouble x1 = 200;
const wxDouble y1 = 200;
const wxDouble x2 = 300;
const wxDouble y2 = 100;
path.AddQuadCurveToPoint(x1, y1, x2, y2);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x2, y2), 1E-3);
}
// AddCircle - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble r = 30;
path.AddCircle(x, y, r);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x + r, y), 1E-3);
}
// AddCircle
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 80);
const wxDouble x = 100;
const wxDouble y = 140;
const wxDouble r = 40;
path.AddCircle(x, y, r);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x + r, y), 1E-3);
}
// AddEllipse - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddEllipse(x, y, w, h);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x + w, y + h / 2), 1E-3);
}
// AddEllipse
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 60);
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddEllipse(x, y, w, h);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x + w, y + h / 2), 1E-3);
}
// AddRectangle - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
path.AddRectangle(x, y, 40, 20);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x, y), 1E-3);
}
// AddRectangle
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 60);
const wxDouble x = 100;
const wxDouble y = 150;
path.AddRectangle(x, y, 50, 30);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x, y), 1E-3);
}
// AddRoundedRectangle - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddRoundedRectangle(x, y, w, h, 5);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x + w, y + h / 2), 1E-3);
}
// AddRoundedRectangle - no current point, radius = 0
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
path.AddRoundedRectangle(x, y, 40, 20, 0); // Should behave like AddRectangle
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x, y), 1E-3);
}
// AddRoundedRectangle
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 60);
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddRoundedRectangle(x, y, w, h, 5);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x + w, y + h / 2), 1E-3);
}
// AddRoundedRectangle - radius = 0
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 50;
const wxDouble y0 = 60;
path.MoveToPoint(x0, y0);
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddRoundedRectangle(x, y, w, h, 0); // Should behave like AddRectangle
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x, y), 1E-3);
}
// CloseSubpath - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 50;
const wxDouble y0 = 80;
path.AddLineToPoint(x0, y0);
path.AddArcToPoint(100, 160, 50, 200, 30);
path.CloseSubpath();
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x0, y0), 1E-3);
}
// CloseSubpath
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 10;
const wxDouble y0 = 20;
path.MoveToPoint(x0, y0);
path.AddLineToPoint(50, 80);
path.AddArcToPoint(100, 160, 50, 200, 30);
path.CloseSubpath();
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, wxPoint2DDouble(x0, y0), 1E-3);
}
// AddPath - no current point
{
// Path to be added
wxGraphicsPath path2 = gc->CreatePath();
path2.AddArcToPoint(100, 160, 50, 200, 30);
path2.AddLineToPoint(50, 80);
path2.CloseSubpath();
wxPoint2DDouble cp2 = path2.GetCurrentPoint();
// Main path
wxGraphicsPath path = gc->CreatePath();
path.AddLineToPoint(50, 80);
const wxDouble x = 100;
const wxDouble y = 140;
path.AddRectangle(x, y, 50, 200);
path.AddPath(path2);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, cp2, 1E-3);
}
// AddPath
{
// Path to be added
wxGraphicsPath path2 = gc->CreatePath();
path2.AddArcToPoint(100, 160, 50, 200, 30);
path2.AddLineToPoint(50, 80);
path2.CloseSubpath();
wxPoint2DDouble cp2 = path2.GetCurrentPoint();
// Main path
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(15, 35);
path.AddLineToPoint(50, 80);
const wxDouble x = 100;
const wxDouble y = 140;
const wxDouble r = 20;
path.AddCircle(x, y, r);
path.AddPath(path2);
wxPoint2DDouble cp = path.GetCurrentPoint();
WX_CHECK_POINT(cp, cp2, 1E-3);
}
}
#define WX_CHECK_BOX(r1, r2, tolerance) \
WX_CHECK_POINT(r1.GetLeftTop(), r2.GetLeftTop(), tolerance); \
WX_CHECK_POINT(r1.GetRightBottom(), r2.GetRightBottom(), tolerance)
static void TestBox(wxGraphicsContext* gc)
{
// No current point
{
wxGraphicsPath path = gc->CreatePath();
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(0, 0, 0, 0), 0);
}
// MoveToPoint
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(28, 38);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(0, 0, 0, 0), 0);
}
// AddLineToPoint - no current point
{
wxGraphicsPath path = gc->CreatePath();
path.AddLineToPoint(28, 36);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(0, 0, 0, 0), 0);
}
// AddLineToPoint
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 10;
const wxDouble y0 = 18;
path.MoveToPoint(x0, y0);
const wxDouble w = 20;
const wxDouble h = 46;
path.AddLineToPoint(x0 + w, y0 + h);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x0, y0, w, h), 0);
}
// AddArc - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble r = 40;
path.AddArc(x, y, r, 0, M_PI / 2, true);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x, y, r, r), 1E-3);
}
// AddArc
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 20;
const wxDouble y0 = 20;
path.MoveToPoint(x0, y0);
const wxDouble x = 200;
const wxDouble y = 50;
const wxDouble r = 40;
path.AddArc(x, y, r, 0, M_PI / 2, true);
const wxDouble x2 = x + r;
const wxDouble y2 = y + r;
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x0, y0, x2 - x0, y2 - y0), 1E-3);
}
// AddArcToPoint - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x1 = 80;
const wxDouble y1 = 0;
const wxDouble x2 = x1;
const wxDouble y2 = 40;
const wxDouble r = 20;
wxASSERT(y1 == 0 && x2 == x1); // alpha = 90 deg
path.AddArcToPoint(x1, y1, x2, y2, r);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(0, 0, x1, r), 1E-3);
}
// AddArcToPoint
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 20;
const wxDouble y0 = 20;
path.MoveToPoint(x0, y0);
const wxDouble x1 = 80;
const wxDouble y1 = y0;
const wxDouble x2 = x1;
const wxDouble y2 = 140;
const wxDouble r = 20;
wxASSERT(y1 == y0 && x2 == x1); // alpha = 90 deg
path.AddArcToPoint(x1, y1, x2, y2, r);
const wxDouble xe = x1;
const wxDouble ye = y1 + r;
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x0, y0, xe - x0, ye - y0), 1E-3);
}
// AddCurveToPoint - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x1 = 102;
const wxDouble y1 = 230;
const wxDouble x2 = 153;
const wxDouble y2 = 25;
const wxDouble x3 = 230;
const wxDouble y3 = 128;
path.AddCurveToPoint(x1, y1, x2, y2, x3, y3);
const wxDouble xmin = wxMin(wxMin(x1, x2), x3);
const wxDouble ymin = wxMin(wxMin(y1, y2), y3);
const wxDouble xmax = wxMax(wxMax(x1, x2), x3);
const wxDouble ymax = wxMax(wxMax(y1, y2), y3);
wxRect2DDouble b = path.GetBox();
const wxDouble tolerance = 1E-3;
CHECK(xmin - tolerance <= b.GetLeft());
CHECK(ymin - tolerance <= b.GetTop());
CHECK(xmax + tolerance >= b.GetRight());
CHECK(ymax + tolerance >= b.GetBottom());
}
// AddCurveToPoint
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 25;
const wxDouble y0 = 128;
path.MoveToPoint(x0, y0);
const wxDouble x1 = 102;
const wxDouble y1 = 230;
const wxDouble x2 = 153;
const wxDouble y2 = 25;
const wxDouble x3 = 230;
const wxDouble y3 = 128;
path.AddCurveToPoint(x1, y1, x2, y2, x3, y3);
const wxDouble xmin = wxMin(wxMin(wxMin(x0, x1), x2), x3);
const wxDouble ymin = wxMin(wxMin(wxMin(y0, y1), y2), y3);
const wxDouble xmax = wxMax(wxMax(wxMax(x0, x1), x2), x3);
const wxDouble ymax = wxMax(wxMax(wxMax(y0, y1), y2), y3);
wxRect2DDouble b = path.GetBox();
const wxDouble tolerance = 1E-3;
CHECK(xmin - tolerance <= b.GetLeft());
CHECK(ymin - tolerance <= b.GetTop());
CHECK(xmax + tolerance >= b.GetRight());
CHECK(ymax + tolerance >= b.GetBottom());
}
// AddQuadCurveToPoint - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x1 = 200;
const wxDouble y1 = 200;
const wxDouble x2 = 300;
const wxDouble y2 = 100;
path.AddQuadCurveToPoint(x1, y1, x2, y2);
// const wxDouble xmin = wxMin(x1, x2);
const wxDouble xmin = 133;
const wxDouble ymin = wxMin(y1, y2);
const wxDouble xmax = wxMax(x1, x2);
const wxDouble ymax = wxMax(y1, y2);
wxRect2DDouble b = path.GetBox();
const wxDouble tolerance = 1E-3;
CHECK(xmin - tolerance <= b.GetLeft());
CHECK(ymin - tolerance <= b.GetTop());
CHECK(xmax + tolerance >= b.GetRight());
CHECK(ymax + tolerance >= b.GetBottom());
}
// AddQuadCurveToPoint
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 20;
const wxDouble y0 = 100;
path.MoveToPoint(x0, y0);
const wxDouble x1 = 200;
const wxDouble y1 = 200;
const wxDouble x2 = 300;
const wxDouble y2 = 100;
path.AddQuadCurveToPoint(x1, y1, x2, y2);
const wxDouble xmin = wxMin(wxMin(x0, x1), x2);
const wxDouble ymin = wxMin(wxMin(y0, y1), y2);
const wxDouble xmax = wxMax(wxMax(x0, x1), x2);
const wxDouble ymax = wxMax(wxMax(y0, y1), y2);
wxRect2DDouble b = path.GetBox();
const wxDouble tolerance = 1E-3;
CHECK(xmin - tolerance <= b.GetLeft());
CHECK(ymin - tolerance <= b.GetTop());
CHECK(xmax + tolerance >= b.GetRight());
CHECK(ymax + tolerance >= b.GetBottom());
}
// AddCircle - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble r = 30;
path.AddCircle(x, y, r);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x - r, y - r, 2 * r, 2 * r), 1E-3);
}
// AddCircle
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 80);
const wxDouble x = 100;
const wxDouble y = 140;
const wxDouble r = 40;
path.AddCircle(x, y, r);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x - r, y - r, 2 * r, 2 * r), 1E-3);
}
// AddEllipse - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddEllipse(x, y, w, h);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x, y, w, h), 1E-3);
}
// AddEllipse
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 60);
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddEllipse(x, y, w, h);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x, y, w, h), 1E-3);
}
// AddRectangle - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddRectangle(x, y, w, h);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x, y, w, h), 1E-3);
}
// AddRectangle
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 60);
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 50;
const wxDouble h = 30;
path.AddRectangle(x, y, w, h);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x, y, w, h), 1E-3);
}
// AddRoundedRectangle - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddRoundedRectangle(x, y, w, h, 5);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x, y, w, h), 1E-3);
}
// AddRoundedRectangle - no current point, radius = 0
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddRoundedRectangle(x, y, w, h, 0);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x, y, w, h), 1E-3);
}
// AddRoundedRectangle
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 60);
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddRoundedRectangle(x, y, w, h, 5);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x, y, w, h), 1E-3);
}
// AddRoundedRectangle - radius = 0
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 60);
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 40;
const wxDouble h = 20;
path.AddRoundedRectangle(x, y, w, h, 0);
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x, y, w, h), 1E-3);
}
// CloseSubpath - empty path
{
wxGraphicsPath path = gc->CreatePath();
path.CloseSubpath();
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(0, 0, 0, 0), 0);
}
// CloseSubpath - no current point
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 50;
const wxDouble y0 = 80;
path.AddLineToPoint(x0, y0);
const wxDouble x1 = 100;
const wxDouble y1 = 160;
path.AddLineToPoint(x1, y1);
path.CloseSubpath();
const wxDouble w = x1 - x0;
const wxDouble h = y1 - y0;
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x0, y0, w, h), 1E-3);
}
// CloseSubpath
{
wxGraphicsPath path = gc->CreatePath();
const wxDouble x0 = 10;
const wxDouble y0 = 20;
path.MoveToPoint(x0, y0);
path.AddLineToPoint(50, 80);
const wxDouble x = 100;
const wxDouble y = 160;
path.AddLineToPoint(x, y);
path.CloseSubpath();
const wxDouble w = x - x0;
const wxDouble h = y - y0;
wxRect2DDouble b = path.GetBox();
WX_CHECK_BOX(b, wxRect2DDouble(x0, y0, w, h), 1E-3);
}
// AddPath - no current point
{
// Path to be added
wxGraphicsPath path2 = gc->CreatePath();
path2.AddLineToPoint(100, 160);
path2.AddLineToPoint(50, 80);
path2.CloseSubpath();
wxRect2DDouble b2 = path2.GetBox();
// Main path
wxGraphicsPath path = gc->CreatePath();
path.AddLineToPoint(50, 80);
const wxDouble x = 100;
const wxDouble y = 140;
path.AddRectangle(x, y, 50, 200);
wxRect2DDouble b0 = path.GetBox();
b0.Union(b2);
path.AddPath(path2);
wxRect2DDouble b1 = path.GetBox();
WX_CHECK_BOX(b0, b1, 1E-3);
}
// AddPath
{
// Path to be added
wxGraphicsPath path2 = gc->CreatePath();
path2.AddArcToPoint(100, 160, 50, 200, 30);
path2.AddLineToPoint(50, 80);
path2.CloseSubpath();
wxRect2DDouble b2 = path2.GetBox();
// Main path
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(15, 35);
path.AddLineToPoint(50, 80);
const wxDouble x = 100;
const wxDouble y = 140;
const wxDouble r = 20;
path.AddCircle(x, y, r);
wxRect2DDouble b0 = path.GetBox();
b0.Union(b2);
path.AddPath(path2);
wxRect2DDouble b1 = path.GetBox();
WX_CHECK_BOX(b0, b1, 1E-3);
}
// Overlapping figures
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 60);
const wxDouble xr = 100;
const wxDouble yr = 150;
const wxDouble wr = 80;
const wxDouble hr = 40;
path.AddRectangle(xr, yr, wr, hr);
const wxDouble xe = xr + wr / 4;
const wxDouble ye = yr + hr / 4;
const wxDouble we = wr / 2;
const wxDouble he = hr / 2;
path.AddEllipse(xe, ye, we, he);
wxRect2DDouble b = path.GetBox();
wxRect2DDouble r;
wxRect2DDouble::Union(wxRect2DDouble(xe, ye, we, he), wxRect2DDouble(xr, yr, wr, hr), &r);
WX_CHECK_BOX(b, r, 1E-3);
}
// Partially overlapping figures
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 60);
const wxDouble xe = 100;
const wxDouble ye = 150;
const wxDouble we = 40;
const wxDouble he = 20;
path.AddEllipse(xe, ye, we, he);
const wxDouble xr = xe + he / 2;
const wxDouble yr = ye + we / 2;
const wxDouble wr = we + 10;
const wxDouble hr = he + 10;
path.AddRectangle(xr, yr, wr, hr);
wxRect2DDouble b = path.GetBox();
wxRect2DDouble r;
wxRect2DDouble::Union(wxRect2DDouble(xe, ye, we, he), wxRect2DDouble(xr, yr, wr, hr), &r);
WX_CHECK_BOX(b, r, 1E-3);
}
// Non-overlapping figures
{
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 60);
const wxDouble xe = 100;
const wxDouble ye = 150;
const wxDouble we = 40;
const wxDouble he = 20;
path.AddEllipse(xe, ye, we, he);
const wxDouble xr = xe + he + 10;
const wxDouble yr = ye + we + 20;
const wxDouble wr = 50;
const wxDouble hr = 30;
path.AddRectangle(xr, yr, wr, hr);
wxRect2DDouble b = path.GetBox();
wxRect2DDouble r;
wxRect2DDouble::Union(wxRect2DDouble(xe, ye, we, he), wxRect2DDouble(xr, yr, wr, hr), &r);
WX_CHECK_BOX(b, r, 1E-3);
}
// Path from transformed graphics context
{
gc->PushState();
gc->Translate(5, 15);
gc->Rotate(10 * M_PI / 180);
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(50, 60);
const wxDouble x = 100;
const wxDouble y = 150;
const wxDouble w = 50;
const wxDouble h = 30;
path.AddRectangle(x, y, w, h);
wxRect2DDouble b = path.GetBox();
gc->PopState();
WX_CHECK_BOX(b, wxRect2DDouble(x, y, w, h), 1E-3);
}
}
static void DoAllTests(wxGraphicsContext* gc)
{
gc->DisableOffset();
TestCurrentPoint(gc);
TestBox(gc);
}
#endif // wxUSE_GRAPHICS_CONTEXT

View File

@@ -0,0 +1,790 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/graphics/imagelist.cpp
// Purpose: image list unit tests
// Author: Artur Wieczorek
// Created: 2021-01-11
// Copyright: (c) 2021 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#include "wx/bitmap.h"
#include "wx/graphics.h"
#include "wx/icon.h"
#include "wx/imaglist.h"
#include "wx/dcmemory.h"
static bool HasNoRealAlpha(const wxBitmap& bmp)
{
if ( !bmp.HasAlpha() )
return true;
// wxMSW can add a fully opaque alpha channel to the bitmaps used in the
// image list.
const wxImage img = bmp.ConvertToImage();
const unsigned char* p = img.GetAlpha();
if ( !p )
return true;
const unsigned char* const end = p + img.GetWidth()*img.GetHeight();
for ( ; p < end; ++p )
{
if ( *p != wxALPHA_OPAQUE )
return false;
}
return true;
}
static bool HasMaskOrAlpha(const wxBitmap& bmp)
{
// When adding bitmaps with mask to the image list, the mask can be
// transformed to alpha channel internally, so check that the bitmap has
// either mask or alpha.
return bmp.HasAlpha() || bmp.GetMask();
}
// ----------------------------------------------------------------------------
// test fixture
// ----------------------------------------------------------------------------
class ImageListTestCase
{
protected:
ImageListTestCase();
const wxSize BITMAP_SIZE;
wxBitmap bmpRGB, bmpRGBA, bmpMask,
bmpRGBWithMask, bmpRGBAWithMask;
wxIcon ico;
};
ImageListTestCase::ImageListTestCase()
: BITMAP_SIZE(32, 32),
bmpRGB(BITMAP_SIZE, 24),
bmpMask(BITMAP_SIZE, 1)
{
{
wxMemoryDC mdc(bmpRGB);
mdc.SetBackground(*wxBLUE_BRUSH);
mdc.Clear();
mdc.SetBrush(*wxRED_BRUSH);
mdc.DrawRectangle(4, 4, 24, 24);
}
REQUIRE(bmpRGB.IsOk());
// Make a bitmap with some transparent and semi-transparent pixels.
wxImage imgWithAlpha(BITMAP_SIZE.x, BITMAP_SIZE.y);
imgWithAlpha.SetAlpha();
unsigned char* const alpha = imgWithAlpha.GetAlpha();
for ( unsigned char* a = alpha; a < alpha + BITMAP_SIZE.x*BITMAP_SIZE.y; ++a )
*a = wxALPHA_OPAQUE;
alpha[0] = wxALPHA_TRANSPARENT;
alpha[1] = wxALPHA_OPAQUE / 2;
bmpRGBA = wxBitmap(imgWithAlpha);
REQUIRE(bmpRGBA.IsOk());
{
wxMemoryDC mdc(bmpMask);
#if wxUSE_GRAPHICS_CONTEXT
wxGraphicsContext* gc = mdc.GetGraphicsContext();
if ( gc )
gc->SetAntialiasMode(wxANTIALIAS_NONE);
#endif //wxUSE_GRAPHICS_CONTEXT
mdc.SetBackground(*wxBLACK_BRUSH);
mdc.Clear();
mdc.SetBrush(*wxWHITE_BRUSH);
mdc.DrawRectangle(0, 0, 16, 32);
}
bmpRGBWithMask = bmpRGB;
bmpRGBWithMask.SetMask(new wxMask(bmpMask));
REQUIRE(bmpRGBWithMask.IsOk());
bmpRGBAWithMask = bmpRGBA;
bmpRGBAWithMask.SetMask(new wxMask(bmpMask));
REQUIRE(bmpRGBAWithMask.IsOk());
ico.CopyFromBitmap(bmpRGBWithMask);
REQUIRE(ico.IsOk());
REQUIRE(bmpRGB.HasAlpha() == false);
REQUIRE(bmpRGB.GetMask() == nullptr);
REQUIRE(bmpRGBWithMask.HasAlpha() == false);
REQUIRE(bmpRGBWithMask.GetMask() != nullptr);
REQUIRE(bmpRGBA.HasAlpha() == true);
REQUIRE(bmpRGBA.GetMask() == nullptr);
REQUIRE(bmpRGBAWithMask.HasAlpha() == true);
REQUIRE(bmpRGBAWithMask.GetMask() != nullptr);
}
// ----------------------------------------------------------------------------
// tests
// ----------------------------------------------------------------------------
TEST_CASE_METHOD(ImageListTestCase,
"ImageList:WithMask", "[imagelist][withmask]")
{
wxImageList il(BITMAP_SIZE.x, BITMAP_SIZE.y, true);
SECTION("Add RGB image to list")
{
il.RemoveAll();
int idx = il.Add(bmpRGB);
CHECK(il.GetImageCount() == 1);
wxBitmap bmp1 = il.GetBitmap(idx);
CHECK(HasNoRealAlpha(bmp1));
CHECK(bmp1.GetSize() == BITMAP_SIZE);
idx = il.Add(bmpRGBWithMask);
CHECK(il.GetImageCount() == 2);
wxBitmap bmp2 = il.GetBitmap(idx);
CHECK(HasMaskOrAlpha(bmp2));
CHECK(bmp2.GetSize() == BITMAP_SIZE);
idx = il.Add(bmpRGB, *wxRED);
CHECK(il.GetImageCount() == 3);
wxBitmap bmp3 = il.GetBitmap(idx);
CHECK(HasMaskOrAlpha(bmp3));
CHECK(bmp3.GetSize() == BITMAP_SIZE);
}
SECTION("Add RGBA image to list")
{
il.RemoveAll();
int idx = il.Add(bmpRGBA);
CHECK(il.GetImageCount() == 1);
wxBitmap bmp1 = il.GetBitmap(idx);
CHECK(HasMaskOrAlpha(bmp1));
CHECK(bmp1.GetSize() == BITMAP_SIZE);
idx = il.Add(bmpRGBAWithMask);
CHECK(il.GetImageCount() == 2);
wxBitmap bmp2 = il.GetBitmap(idx);
CHECK(HasMaskOrAlpha(bmp2));
CHECK(bmp2.GetSize() == BITMAP_SIZE);
idx = il.Add(bmpRGBA, *wxRED);
CHECK(il.GetImageCount() == 3);
wxBitmap bmp3 = il.GetBitmap(idx);
CHECK(HasMaskOrAlpha(bmp3));
CHECK(bmp3.GetSize() == BITMAP_SIZE);
}
SECTION("Add icon to list")
{
il.RemoveAll();
int idx = il.Add(ico);
CHECK(il.GetImageCount() == 1);
wxIcon icon1 = il.GetIcon(idx);
CHECK(icon1.GetSize() == BITMAP_SIZE);
}
SECTION("Replace with RGB image")
{
il.RemoveAll();
int idx1 = il.Add(bmpRGBA);
CHECK(il.GetImageCount() == 1);
int idx2 = il.Add(bmpRGBAWithMask);
CHECK(il.GetImageCount() == 2);
il.Replace(idx1, bmpRGB);
il.Replace(idx2, bmpRGBWithMask);
wxBitmap bmp1 = il.GetBitmap(idx1);
CHECK(HasMaskOrAlpha(bmp1));
CHECK(bmp1.GetSize() == BITMAP_SIZE);
wxBitmap bmp2 = il.GetBitmap(idx2);
CHECK(HasMaskOrAlpha(bmp2));
CHECK(bmp2.GetSize() == BITMAP_SIZE);
}
SECTION("Replace with RGBA image")
{
il.RemoveAll();
int idx1 = il.Add(bmpRGB);
CHECK(il.GetImageCount() == 1);
int idx2 = il.Add(bmpRGBWithMask);
CHECK(il.GetImageCount() == 2);
il.Replace(idx1, bmpRGBA);
il.Replace(idx2, bmpRGBAWithMask);
wxBitmap bmp1 = il.GetBitmap(idx1);
CHECK(HasMaskOrAlpha(bmp1));
CHECK(bmp1.GetSize() == BITMAP_SIZE);
wxBitmap bmp2 = il.GetBitmap(idx2);
CHECK(HasMaskOrAlpha(bmp2));
CHECK(bmp2.GetSize() == BITMAP_SIZE);
}
SECTION("Add images with incompatible sizes")
{
il.RemoveAll();
wxSize sz = il.GetSize();
wxBitmap bmpSmallerW(sz.GetWidth() / 2, sz.GetHeight(), 24);
{
wxMemoryDC mdc(bmpSmallerW);
mdc.SetBackground(*wxBLUE_BRUSH);
mdc.Clear();
}
REQUIRE(bmpSmallerW.IsOk());
wxBitmap bmpSmallerH(sz.GetWidth(), sz.GetHeight() / 2, 24);
{
wxMemoryDC mdc(bmpSmallerH);
mdc.SetBackground(*wxBLUE_BRUSH);
mdc.Clear();
}
REQUIRE(bmpSmallerH.IsOk());
wxBitmap bmpSmallerWH(sz.GetWidth() / 2, sz.GetHeight() / 2, 24);
{
wxMemoryDC mdc(bmpSmallerWH);
mdc.SetBackground(*wxBLUE_BRUSH);
mdc.Clear();
}
REQUIRE(bmpSmallerWH.IsOk());
wxBitmap bmpBiggerW(sz.GetWidth() * 3 / 2, sz.GetHeight(), 24);
{
wxMemoryDC mdc(bmpBiggerW);
mdc.SetBackground(*wxBLUE_BRUSH);
mdc.Clear();
}
REQUIRE(bmpBiggerW.IsOk());
wxBitmap bmpBiggerW2x(sz.GetWidth() * 2, sz.GetHeight(), 24);
{
wxMemoryDC mdc(bmpBiggerW2x);
mdc.SetBackground(*wxBLUE_BRUSH);
mdc.Clear();
}
REQUIRE(bmpBiggerW2x.IsOk());
wxBitmap bmpBiggerH(sz.GetWidth(), sz.GetHeight() * 3 / 2, 24);
{
wxMemoryDC mdc(bmpBiggerH);
mdc.SetBackground(*wxBLUE_BRUSH);
mdc.Clear();
}
REQUIRE(bmpBiggerH.IsOk());
wxBitmap bmpBiggerH2x(sz.GetWidth(), sz.GetHeight() * 2, 24);
{
wxMemoryDC mdc(bmpBiggerH2x);
mdc.SetBackground(*wxBLUE_BRUSH);
mdc.Clear();
}
REQUIRE(bmpBiggerH2x.IsOk());
wxBitmap bmpBiggerWH(sz.GetWidth() * 3 / 2, sz.GetHeight() * 3 / 2, 24);
{
wxMemoryDC mdc(bmpBiggerWH);
mdc.SetBackground(*wxBLUE_BRUSH);
mdc.Clear();
}
REQUIRE(bmpBiggerWH.IsOk());
wxBitmap bmpBiggerWH2x(sz.GetWidth() * 2, sz.GetHeight() * 2, 24);
{
wxMemoryDC mdc(bmpBiggerWH2x);
mdc.SetBackground(*wxBLUE_BRUSH);
mdc.Clear();
}
REQUIRE(bmpBiggerWH2x.IsOk());
// Adding
int cnt = il.GetImageCount();
int idx = il.Add(bmpSmallerW);
CHECK(idx == -1);
CHECK(il.GetImageCount() == cnt);
cnt = il.GetImageCount();
idx = il.Add(bmpSmallerH);
CHECK(idx >= 0);
CHECK(il.GetImageCount() == cnt + 1);
wxBitmap bmp = il.GetBitmap(idx);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
idx = il.Add(bmpSmallerWH);
CHECK(idx == -1);
CHECK(il.GetImageCount() == cnt);
cnt = il.GetImageCount();
idx = il.Add(bmpBiggerW);
CHECK(idx >= 0);
CHECK(il.GetImageCount() == cnt + 1);
bmp = il.GetBitmap(idx);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
idx = il.Add(bmpBiggerW2x);
CHECK(idx >= 0);
CHECK(il.GetImageCount() == cnt + 2);
bmp = il.GetBitmap(idx);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
idx = il.Add(bmpBiggerH);
CHECK(idx >= 0);
CHECK(il.GetImageCount() == cnt + 1);
bmp = il.GetBitmap(idx);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
idx = il.Add(bmpBiggerH2x);
CHECK(idx >= 0);
CHECK(il.GetImageCount() == cnt + 1);
bmp = il.GetBitmap(idx);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
idx = il.Add(bmpBiggerWH);
CHECK(idx >= 0);
CHECK(il.GetImageCount() == cnt + 1);
bmp = il.GetBitmap(idx);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
idx = il.Add(bmpBiggerWH2x);
CHECK(idx >= 0);
CHECK(il.GetImageCount() == cnt + 2);
bmp = il.GetBitmap(idx);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
// Replacing
il.RemoveAll();
cnt = il.GetImageCount();
bool ok = il.Replace(0, bmpRGBA);
CHECK(ok == false);
CHECK(il.GetImageCount() == cnt);
// List with 1 image
idx = il.Add(bmpRGB);
CHECK(idx >= 0);
cnt = il.GetImageCount();
ok = il.Replace(0, bmpRGBA);
CHECK(ok == true);
CHECK(il.GetImageCount() == cnt);
bmp = il.GetBitmap(0);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
ok = il.Replace(0, bmpSmallerW);
CHECK(ok == true);
CHECK(il.GetImageCount() == cnt);
bmp = il.GetBitmap(0);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
ok = il.Replace(0, bmpSmallerH);
CHECK(ok == true);
CHECK(il.GetImageCount() == cnt);
bmp = il.GetBitmap(0);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
ok = il.Replace(0, bmpSmallerWH);
CHECK(ok == true);
CHECK(il.GetImageCount() == cnt);
bmp = il.GetBitmap(0);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
ok = il.Replace(0, bmpBiggerW);
CHECK(ok == true);
CHECK(il.GetImageCount() == cnt);
bmp = il.GetBitmap(0);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
ok = il.Replace(0, bmpBiggerW2x);
CHECK(ok == true);
CHECK(il.GetImageCount() == cnt);
bmp = il.GetBitmap(0);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
ok = il.Replace(0, bmpBiggerH);
CHECK(ok == true);
CHECK(il.GetImageCount() == cnt);
bmp = il.GetBitmap(0);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
ok = il.Replace(0, bmpBiggerH2x);
CHECK(ok == true);
CHECK(il.GetImageCount() == cnt);
bmp = il.GetBitmap(0);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
ok = il.Replace(0, bmpBiggerWH);
CHECK(ok == true);
CHECK(il.GetImageCount() == cnt);
bmp = il.GetBitmap(0);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
cnt = il.GetImageCount();
ok = il.Replace(0, bmpBiggerWH2x);
CHECK(ok == true);
CHECK(il.GetImageCount() == cnt);
bmp = il.GetBitmap(0);
CHECK(bmp.GetWidth() == sz.GetWidth());
CHECK(bmp.GetHeight() == sz.GetHeight());
}
}
TEST_CASE_METHOD(ImageListTestCase,
"ImageList:NoMask", "[imagelist][nomask]")
{
wxImageList il(BITMAP_SIZE.x, BITMAP_SIZE.y, false);
SECTION("Add RGB image to list")
{
il.RemoveAll();
int idx = il.Add(bmpRGB);
CHECK(il.GetImageCount() == 1);
wxBitmap bmp1 = il.GetBitmap(idx);
CHECK(bmp1.HasAlpha() == false);
CHECK(bmp1.GetMask() == nullptr);
CHECK(bmp1.GetSize() == BITMAP_SIZE);
idx = il.Add(bmpRGBWithMask);
CHECK(il.GetImageCount() == 2);
wxBitmap bmp2 = il.GetBitmap(idx);
CHECK(HasMaskOrAlpha(bmp2));
CHECK(bmp2.GetSize() == BITMAP_SIZE);
idx = il.Add(bmpRGB, *wxRED);
CHECK(il.GetImageCount() == 3);
wxBitmap bmp3 = il.GetBitmap(idx);
CHECK(HasMaskOrAlpha(bmp3));
CHECK(bmp3.GetSize() == BITMAP_SIZE);
}
SECTION("Add RGBA image to list")
{
il.RemoveAll();
int idx = il.Add(bmpRGBA);
CHECK(il.GetImageCount() == 1);
wxBitmap bmp1 = il.GetBitmap(idx);
CHECK(bmp1.HasAlpha() == true);
CHECK(bmp1.GetMask() == nullptr);
CHECK(bmp1.GetSize() == BITMAP_SIZE);
idx = il.Add(bmpRGBAWithMask);
CHECK(il.GetImageCount() == 2);
wxBitmap bmp2 = il.GetBitmap(idx);
CHECK(HasMaskOrAlpha(bmp2));
CHECK(bmp2.GetSize() == BITMAP_SIZE);
idx = il.Add(bmpRGBA, *wxRED);
CHECK(il.GetImageCount() == 3);
wxBitmap bmp3 = il.GetBitmap(idx);
CHECK(HasMaskOrAlpha(bmp3));
CHECK(bmp3.GetSize() == BITMAP_SIZE);
}
SECTION("Add icon to list")
{
il.RemoveAll();
int idx = il.Add(ico);
CHECK(il.GetImageCount() == 1);
wxIcon icon1 = il.GetIcon(idx);
CHECK(icon1.GetSize() == BITMAP_SIZE);
}
SECTION("Replace with RGB image")
{
il.RemoveAll();
int idx1 = il.Add(bmpRGBA);
CHECK(il.GetImageCount() == 1);
int idx2 = il.Add(bmpRGBAWithMask);
CHECK(il.GetImageCount() == 2);
il.Replace(idx1, bmpRGB);
il.Replace(idx2, bmpRGBWithMask);
wxBitmap bmp1 = il.GetBitmap(idx1);
CHECK(bmp1.HasAlpha() == false);
CHECK(bmp1.GetMask() == nullptr);
CHECK(bmp1.GetSize() == BITMAP_SIZE);
wxBitmap bmp2 = il.GetBitmap(idx2);
CHECK(HasMaskOrAlpha(bmp2));
CHECK(bmp2.GetSize() == BITMAP_SIZE);
}
SECTION("Replace with RGBA image")
{
il.RemoveAll();
int idx1 = il.Add(bmpRGB);
CHECK(il.GetImageCount() == 1);
int idx2 = il.Add(bmpRGBWithMask);
CHECK(il.GetImageCount() == 2);
il.Replace(idx1, bmpRGBA);
il.Replace(idx2, bmpRGBAWithMask);
wxBitmap bmp1 = il.GetBitmap(idx1);
CHECK(bmp1.HasAlpha() == true);
CHECK(bmp1.GetMask() == nullptr);
CHECK(bmp1.GetSize() == BITMAP_SIZE);
wxBitmap bmp2 = il.GetBitmap(idx2);
CHECK(HasMaskOrAlpha(bmp2));
CHECK(bmp2.GetSize() == BITMAP_SIZE);
}
SECTION("Add 2x width image")
{
il.RemoveAll();
int idx = il.Add(wxBitmap(BITMAP_SIZE.x * 2, BITMAP_SIZE.y));
CHECK(idx == 0);
CHECK(il.GetImageCount() == 2);
}
}
TEST_CASE("ImageList:NegativeTests", "[imagelist][negative]")
{
wxBitmap bmp(32, 32, 24);
{
wxMemoryDC mdc(bmp);
mdc.SetBackground(*wxBLUE_BRUSH);
mdc.Clear();
mdc.SetBrush(*wxRED_BRUSH);
mdc.DrawRectangle(4, 4, 24, 24);
}
REQUIRE(bmp.IsOk());
SECTION("Invalid size (negative)")
{
wxImageList il;
bool ok = il.Create(-1, -1);
CHECK_FALSE(ok);
#ifdef __WXDEBUG__
REQUIRE_THROWS(il.GetImageCount());
#else
CHECK(il.GetImageCount() == 0);
#endif
wxSize sz = il.GetSize();
CHECK(sz.x == 0);
CHECK(sz.y == 0);
int w = -1;
int h = -1;
#ifdef __WXDEBUG__
REQUIRE_THROWS(il.GetSize(0, w, h));
#else
ok = il.GetSize(0, w, h);
CHECK_FALSE(ok);
CHECK(w == 0);
CHECK(h == 0);
#endif
#ifdef __WXDEBUG__
CHECK_THROWS(il.Add(bmp));
REQUIRE_THROWS(il.GetImageCount());
#else
CHECK(il.Add(bmp) == -1);
CHECK(il.GetImageCount() == 0);
#endif
}
SECTION("Invalid size (zero)")
{
wxImageList il;
bool ok = il.Create(0, 0);
CHECK_FALSE(ok);
#ifdef __WXDEBUG__
REQUIRE_THROWS(il.GetImageCount());
#else
CHECK(il.GetImageCount() == 0);
#endif
wxSize sz = il.GetSize();
CHECK(sz.x == 0);
CHECK(sz.y == 0);
int w = -1;
int h = -1;
#ifdef __WXDEBUG__
REQUIRE_THROWS(ok = il.GetSize(0, w, h));
#else
ok = il.GetSize(0, w, h);
CHECK_FALSE(ok);
CHECK(w == 0);
CHECK(h == 0);
#endif
#ifdef __WXDEBUG__
CHECK_THROWS(il.Add(bmp));
REQUIRE_THROWS(il.GetImageCount());
#else
CHECK(il.Add(bmp) == -1);
CHECK(il.GetImageCount() == 0);
#endif
#ifdef __WXDEBUG__
CHECK_THROWS(il.Replace(0, bmp));
REQUIRE_THROWS(il.GetImageCount());
#else
CHECK_FALSE(il.Replace(0, bmp));
CHECK(il.GetImageCount() == 0);
#endif
}
SECTION("Add to invalid image list")
{
wxImageList il;
#ifdef __WXDEBUG__
CHECK_THROWS( il.Add(bmp) );
#else
CHECK( il.Add(bmp) == -1 );
#endif
}
SECTION("Invalid Get/Replace/Remove indices")
{
wxImageList il(32, 32, false);
CHECK(il.GetImageCount() == 0);
wxSize sz = il.GetSize();
CHECK(sz.x == 32);
CHECK(sz.y == 32);
int w = -1;
int h = -1;
bool ok = il.GetSize(0, w, h);
CHECK(ok == true);
CHECK(w == 32);
CHECK(h == 32);
int idx = il.Add(bmp);
CHECK(idx == 0);
CHECK(il.GetImageCount() == 1);
wxBitmap bmp2 = il.GetBitmap(-1);
CHECK_FALSE(bmp2.IsOk());
CHECK(il.GetImageCount() == 1);
wxBitmap bmp3 = il.GetBitmap(5);
CHECK_FALSE(bmp3.IsOk());
CHECK(il.GetImageCount() == 1);
wxIcon icon2 = il.GetIcon(-1);
CHECK_FALSE(icon2.IsOk());
CHECK(il.GetImageCount() == 1);
wxBitmap icon3 = il.GetIcon(5);
CHECK_FALSE(icon3.IsOk());
CHECK(il.GetImageCount() == 1);
ok = il.Replace(-1, bmp);
CHECK_FALSE(ok);
CHECK(il.GetImageCount() == 1);
ok = il.Replace(5, bmp);
CHECK_FALSE(ok);
CHECK(il.GetImageCount() == 1);
ok = il.Remove(-1);
CHECK_FALSE(ok);
CHECK(il.GetImageCount() == 1);
ok = il.Remove(5);
CHECK_FALSE(ok);
CHECK(il.GetImageCount() == 1);
}
}
// This test relies on logical pixels being different from physical ones.
#ifdef wxHAS_DPI_INDEPENDENT_PIXELS
TEST_CASE("ImageList:HiDPI", "[imagelist][hidpi]")
{
wxImage img1(8, 4);
img1.SetRGB(wxRect(0, 0, 8, 4), 0, 63, 127);
REQUIRE(img1.IsOk());
wxImage img2(16, 8);
img2.SetRGB(wxRect(0, 0, 16, 8), 255, 128, 64);
REQUIRE(img2.IsOk());
wxBitmap bmp1x(img1, -1, 1.0);
REQUIRE(bmp1x.IsOk());
CHECK(bmp1x.GetSize() == wxSize(8, 4));
CHECK(bmp1x.GetLogicalSize() == wxSize(8, 4));
CHECK_FALSE(bmp1x.HasAlpha());
CHECK(bmp1x.GetMask() == nullptr);
wxBitmap bmp2x(img2, -1, 2.0);
REQUIRE(bmp2x.IsOk());
CHECK(bmp2x.GetSize() == wxSize(16, 8));
CHECK(bmp2x.GetLogicalSize() == wxSize(8, 4));
CHECK_FALSE(bmp2x.HasAlpha());
CHECK(bmp2x.GetMask() == nullptr);
// Logical image size
wxImageList il(8, 4, false);
int idx = il.Add(bmp2x);
REQUIRE(idx == 0);
REQUIRE(il.GetImageCount() == 1);
idx = il.Add(bmp1x);
REQUIRE(idx == 1);
REQUIRE(il.GetImageCount() == 2);
wxBitmap bmp = il.GetBitmap(0);
REQUIRE(bmp.IsOk() == true);
CHECK(bmp.GetScaleFactor() == 2.0);
CHECK(bmp.GetSize() == wxSize(16, 8));
CHECK(bmp.GetLogicalSize() == wxSize(8, 4));
CHECK_FALSE(bmp.HasAlpha());
CHECK(bmp.GetMask() == nullptr);
bmp = il.GetBitmap(1);
REQUIRE(bmp.IsOk() == true);
CHECK(bmp.GetScaleFactor() == 1.0);
CHECK(bmp.GetSize() == wxSize(8, 4));
CHECK(bmp.GetLogicalSize() == wxSize(8, 4));
CHECK_FALSE(bmp.HasAlpha());
CHECK(bmp.GetMask() == nullptr);
}
#endif // wxHAS_DPI_INDEPENDENT_PIXELS

View File

@@ -0,0 +1,190 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/graphics/measuring.cpp
// Purpose: Tests for wxGraphicsRenderer::CreateMeasuringContext
// Author: Kevin Ollivier, Vadim Zeitlin (non wxGC parts)
// Created: 2008-02-12
// Copyright: (c) 2008 Kevin Ollivier <kevino@theolliviers.com>
// (c) 2012 Vadim Zeitlin <vadim@wxwidgets.org>
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/font.h"
#include "wx/window.h"
#endif // WX_PRECOMP
// wxCairoRenderer::CreateMeasuringContext() is not implement for wxX11
#if wxUSE_GRAPHICS_CONTEXT && !defined(__WXX11__)
#include "wx/graphics.h"
#define TEST_GC
#endif
#include "wx/dcclient.h"
#include "wx/dcmemory.h"
#include "wx/dcps.h"
#include "wx/metafile.h"
#include "asserthelper.h"
// ----------------------------------------------------------------------------
// helper for XXXTextExtent() methods
// ----------------------------------------------------------------------------
namespace
{
// Run a couple of simple tests for GetTextExtent().
template <typename T>
void GetTextExtentTester(const T& obj)
{
// Test that getting the height only doesn't crash.
int y;
obj.GetTextExtent("H", nullptr, &y);
CHECK( y > 1 );
wxSize size = obj.GetTextExtent("Hello");
CHECK( size.x > 1 );
CHECK( size.y == y );
// Test that getting text extent of an empty string returns (0, 0).
CHECK( obj.GetTextExtent(wxString()) == wxSize() );
}
} // anonymous namespace
// ----------------------------------------------------------------------------
// tests themselves
// ----------------------------------------------------------------------------
TEST_CASE("wxDC::GetTextExtent", "[dc][text-extent]")
{
wxClientDC dc(wxTheApp->GetTopWindow());
GetTextExtentTester(dc);
int w;
dc.GetMultiLineTextExtent("Good\nbye", &w, nullptr);
const wxSize sz = dc.GetTextExtent("Good");
CHECK( w == sz.x );
CHECK( dc.GetMultiLineTextExtent("Good\nbye").y >= 2*sz.y );
// Check that empty lines get counted
CHECK( dc.GetMultiLineTextExtent("\n\n\n").y >= 3*sz.y );
// And even empty strings count like one line.
CHECK( dc.GetMultiLineTextExtent(wxString()) == wxSize(0, sz.y) );
}
TEST_CASE("wxMemoryDC::GetTextExtent", "[memdc][text-extent]")
{
wxBitmap bmp(100, 100);
wxMemoryDC memdc(bmp);
GetTextExtentTester(memdc);
// Under MSW, this wxDC should work even without any valid font -- but
// this is not the case under wxGTK and probably neither elsewhere, so
// restrict this test to that platform only.
#ifdef __WXMSW__
memdc.SetFont(wxNullFont);
GetTextExtentTester(memdc);
#endif // __WXMSW__
}
#if wxUSE_PRINTING_ARCHITECTURE && wxUSE_POSTSCRIPT
TEST_CASE("wxPostScriptDC::GetTextExtent", "[psdc][text-extent]")
{
wxPostScriptDC psdc;
// wxPostScriptDC doesn't have any font set by default but its
// GetTextExtent() requires one to be set. This is probably a bug and we
// should set the default font in it implicitly but for now just work
// around it.
psdc.SetFont(*wxNORMAL_FONT);
GetTextExtentTester(psdc);
}
#endif // wxUSE_POSTSCRIPT
#if wxUSE_ENH_METAFILE
TEST_CASE("wxEnhMetaFileDC::GetTextExtent", "[emfdc][text-extent]")
{
wxEnhMetaFileDC metadc;
GetTextExtentTester(metadc);
}
#endif // wxUSE_ENH_METAFILE
TEST_CASE("wxDC::LeadingAndDescent", "[dc][text-extent]")
{
wxClientDC dc(wxTheApp->GetTopWindow());
// Retrieving just the descent should work.
int descent = -17;
dc.GetTextExtent("foo", nullptr, nullptr, &descent);
CHECK( descent != -17 );
// Same for external leading.
int leading = -289;
dc.GetTextExtent("foo", nullptr, nullptr, nullptr, &leading);
CHECK( leading != -289 );
// And both should also work for the empty string as they retrieve the
// values valid for the entire font and not just this string.
int descent2,
leading2;
dc.GetTextExtent("", nullptr, nullptr, &descent2, &leading2);
CHECK( descent2 == descent );
CHECK( leading2 == leading );
}
TEST_CASE("wxWindow::GetTextExtent", "[window][text-extent]")
{
wxWindow* const win = wxTheApp->GetTopWindow();
GetTextExtentTester(*win);
}
TEST_CASE("wxDC::GetPartialTextExtent", "[dc][text-extent][partial]")
{
wxClientDC dc(wxTheApp->GetTopWindow());
wxArrayInt widths;
REQUIRE( dc.GetPartialTextExtents("Hello", widths) );
REQUIRE( widths.size() == 5 );
CHECK( widths[0] == dc.GetTextExtent("H").x );
#ifdef __WXQT__
// Skip test which work locally, but not when run on GitHub CI
if ( IsAutomaticTest() )
return;
#endif
CHECK( widths[4] == dc.GetTextExtent("Hello").x );
}
#ifdef TEST_GC
TEST_CASE("wxGC::GetTextExtent", "[dc][text-extent]")
{
wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer();
REQUIRE(renderer);
wxGraphicsContext* context = renderer->CreateMeasuringContext();
REQUIRE(context);
wxFont font(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
REQUIRE(font.IsOk());
context->SetFont(font, *wxBLACK);
double width, height, descent, externalLeading = 0.0;
context->GetTextExtent("x", &width, &height, &descent, &externalLeading);
delete context;
// TODO: Determine a way to make these tests more robust.
CHECK(width > 0.0);
CHECK(height > 0.0);
}
#endif // TEST_GC