wxWidgets/tests/benchmarks/graphics.cpp
Vadim Zeitlin b53f7ac904 Restore support for using faster dotted pens in wxMSW
Changes of d245dc9e1f (Fix drawing of dotted lines with wxDC in wxMSW,
2020-03-27) improved the appearance of dotted and dashed lines in wxMSW
but at the expense of significant (up to a factor of 300) slowdown.

Allow the applications for which the drawing performance is important to
explicitly request the old behaviour, with uglier, but faster, pens by
choosing to use low quality pens.

Update the graphics benchmark to allow specifying the pen quality and
verify that the performance when using it is the same as before 3.1.4.

See https://github.com/wxWidgets/wxWidgets/pull/2218

See #7097.

Closes #18875.
2021-02-22 00:04:07 +01:00

1059 lines
30 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: graphics.cpp
// Purpose: Some benchmarks for measuring graphics operations performance
// Author: Vadim Zeitlin
// Created: 2008-04-13
// Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/app.h"
#include "wx/frame.h"
#include "wx/cmdline.h"
#include "wx/dcclient.h"
#include "wx/dcmemory.h"
#include "wx/dcgraph.h"
#include "wx/image.h"
#include "wx/rawbmp.h"
#include "wx/stopwatch.h"
#include "wx/crt.h"
#if wxUSE_GLCANVAS
#include "wx/glcanvas.h"
#ifdef _MSC_VER
#pragma comment(lib, "opengl32")
#endif
#endif // wxUSE_GLCANVAS
#if wxUSE_GLCANVAS
GLuint g_texture;
wxImage g_image;
void InitializeTexture(int w, int h)
{
glGenTextures(1, &g_texture);
glBindTexture(GL_TEXTURE_2D, g_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
g_image.Create(w, h, false /* don't clear */);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0,
GL_RGB, g_image.GetWidth(), g_image.GetHeight(), 0,
GL_RGB, GL_UNSIGNED_BYTE, g_image.GetData());
}
#endif // wxUSE_GLCANVAS
struct GraphicsBenchmarkOptions
{
GraphicsBenchmarkOptions()
{
mapMode = 0;
penWidth = 0;
penStyle = wxPENSTYLE_INVALID;
penQuality = wxPEN_QUALITY_DEFAULT;
width = 800;
height = 600;
numIters = 1000;
testBitmaps =
testImages =
testLines =
testRawBitmaps =
testRectangles =
testCircles =
testEllipses =
testTextExtent =
testMultiLineTextExtent =
testPartialTextExtents = false;
usePaint =
useClient =
useMemory = false;
useDC =
useGC =
useGL = false;
renderer = Default;
}
long mapMode,
penWidth,
width,
height,
numIters;
wxPenStyle penStyle;
wxPenQuality penQuality;
bool testBitmaps,
testImages,
testLines,
testRawBitmaps,
testRectangles,
testCircles,
testEllipses,
testTextExtent,
testMultiLineTextExtent,
testPartialTextExtents;
bool usePaint,
useClient,
useMemory;
bool useDC,
useGC,
useGL;
#ifdef __WXMSW__
enum GraphicsRenderer { Default, GDIPlus, Direct2D, Cairo };
#else
enum GraphicsRenderer { Default };
#endif // __WXMSW__ / !__WXMSW__
GraphicsRenderer renderer;
} opts;
class GraphicsBenchmarkFrame : public wxFrame
{
public:
GraphicsBenchmarkFrame()
: wxFrame(NULL, wxID_ANY, "wxWidgets Graphics Benchmark")
{
SetClientSize(opts.width, opts.height);
#if wxUSE_GLCANVAS
m_glCanvas = NULL;
m_glContext = NULL;
if ( opts.useGL )
{
m_glCanvas = new wxGLCanvas(this, wxID_ANY, NULL,
wxPoint(0, 0),
wxSize(opts.width, opts.height));
m_glContext = new wxGLContext(m_glCanvas);
m_glContext->SetCurrent(*m_glCanvas);
glViewport(0, 0, opts.width, opts.height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1, 1, -1, 1, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
InitializeTexture(opts.width, opts.height);
m_glCanvas->Connect(
wxEVT_PAINT,
wxPaintEventHandler(GraphicsBenchmarkFrame::OnGLRender),
NULL,
this
);
}
else // Not using OpenGL
#endif // wxUSE_GLCANVAS
{
Connect(wxEVT_PAINT,
wxPaintEventHandler(GraphicsBenchmarkFrame::OnPaint));
}
Connect(wxEVT_SIZE, wxSizeEventHandler(GraphicsBenchmarkFrame::OnSize));
m_bitmapARGB.Create(64, 64, 32);
#if defined(__WXMSW__) || defined(__WXOSX__)
m_bitmapARGB.UseAlpha(true);
#endif // __WXMSW__ || _WXOSX__
m_bitmapRGB.Create(64, 64, 24);
wxBitmap bmpMask(64, 64, 1);
{
wxMemoryDC dc(bmpMask);
dc.SetBackground(*wxBLACK_BRUSH);
dc.Clear();
}
m_bitmapARGBwithMask.Create(64, 64, 32);
#if defined(__WXMSW__) || defined(__WXOSX__)
m_bitmapARGBwithMask.UseAlpha(true);
#endif // __WXMSW__ || __WXOSX__
m_bitmapARGBwithMask.SetMask(new wxMask(bmpMask));
m_bitmapRGBwithMask.Create(64, 64, 24);
m_bitmapRGBwithMask.SetMask(new wxMask(bmpMask));
m_renderer = NULL;
if ( opts.useGC )
{
#ifdef __WXMSW__
if ( opts.renderer == GraphicsBenchmarkOptions::GDIPlus )
m_renderer = wxGraphicsRenderer::GetGDIPlusRenderer();
else if ( opts.renderer == GraphicsBenchmarkOptions::Direct2D )
m_renderer = wxGraphicsRenderer::GetDirect2DRenderer();
else if ( opts.renderer == GraphicsBenchmarkOptions::Cairo )
m_renderer = wxGraphicsRenderer::GetCairoRenderer();
// Check if selected renderer is operational.
if ( m_renderer )
{
wxBitmap bmp(16, 16);
wxMemoryDC memDC(bmp);
wxGraphicsContext* gc = m_renderer->CreateContext(memDC);
if ( !gc )
{
wxPrintf("Couldn't initialize '%s' graphics renderer.\n", m_renderer->GetName().c_str());
m_renderer = NULL;
}
delete gc;
}
#endif // __WXMSW__
if( !m_renderer )
m_renderer = wxGraphicsRenderer::GetDefaultRenderer();
}
Show();
}
#if wxUSE_GLCANVAS
virtual ~GraphicsBenchmarkFrame()
{
delete m_glContext;
}
#endif // wxUSE_GLCANVAS
private:
// Just change the image in some (quick) way to show that it's really being
// updated on screen.
void UpdateRGB(unsigned char* data, int n)
{
for ( int y = 0; y < opts.height; ++y )
{
memset(data, n % 256, 3*opts.width);
data += 3*opts.width;
n++;
}
}
#if wxUSE_GLCANVAS
void OnGLRender(wxPaintEvent& WXUNUSED(event))
{
m_glContext->SetCurrent(*m_glCanvas);
glEnable(GL_TEXTURE_2D);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
wxPrintf("Benchmarking %s: ", "OpenGL images");
fflush(stdout);
wxStopWatch sw;
for ( int n = 0; n < opts.numIters; n++ )
{
UpdateRGB(g_image.GetData(), n);
glTexSubImage2D(GL_TEXTURE_2D, 0,
0, 0, opts.width, opts.height,
GL_RGB, GL_UNSIGNED_BYTE, g_image.GetData());
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(-1.0, -1.0);
glTexCoord2f(0, 1);
glVertex2f(-1.0, 1.0);
glTexCoord2f(1, 1);
glVertex2f(1.0, 1.0);
glTexCoord2f(1, 0);
glVertex2f(1.0, -1.0);
glEnd();
m_glCanvas->SwapBuffers();
}
const long t = sw.Time();
wxPrintf("%ld images done in %ldms = %gus/image or %ld FPS\n",
opts.numIters, t, (1000. * t)/opts.numIters,
(1000*opts.numIters + t - 1)/t);
wxTheApp->ExitMainLoop();
}
#endif // wxUSE_GLCANVAS
void OnPaint(wxPaintEvent& WXUNUSED(event))
{
if ( opts.usePaint )
{
{
wxPaintDC dc(this);
wxGCDC gcdc;
if ( m_renderer )
{
wxGraphicsContext* gc = m_renderer->CreateContext(dc);
gcdc.SetGraphicsContext(gc);
}
BenchmarkDCAndGC("paint", dc, gcdc);
}
// Since some renderers use back buffers and hence
// drawing results are not displayed when the test
// is running then wait a second after graphics
// contents is commited to DC to present the output.
wxSleep(1);
}
if ( opts.useClient )
{
{
wxClientDC dc(this);
wxGCDC gcdc;
if ( m_renderer )
{
wxGraphicsContext* gc = m_renderer->CreateContext(dc);
gcdc.SetGraphicsContext(gc);
}
BenchmarkDCAndGC("client", dc, gcdc);
}
// Since some renderers use back buffers and hence
// drawing results are not displayed when the test
// is running then wait a second after graphics
// contents is commited to DC to present the output.
wxSleep(1);
}
if ( opts.useMemory )
{
{
wxBitmap bmp(opts.width, opts.height);
wxMemoryDC dc(bmp);
wxGCDC gcdc;
if ( m_renderer )
{
wxGraphicsContext* gc = m_renderer->CreateContext(dc);
gcdc.SetGraphicsContext(gc);
}
BenchmarkDCAndGC("default memory", dc, gcdc);
}
{
wxBitmap bmp(opts.width, opts.height, 24);
wxMemoryDC dc(bmp);
wxGCDC gcdc;
if ( m_renderer )
{
wxGraphicsContext* gc = m_renderer->CreateContext(dc);
gcdc.SetGraphicsContext(gc);
}
BenchmarkDCAndGC("RGB memory", dc, gcdc);
}
{
#if defined(__WXMSW__) || defined(__WXOSX__)
wxBitmap bmp(opts.width, opts.height, 32);
bmp.UseAlpha(false);
wxMemoryDC dc(bmp);
wxGCDC gcdc;
if ( m_renderer )
{
wxGraphicsContext* gc = m_renderer->CreateContext(dc);
gcdc.SetGraphicsContext(gc);
}
BenchmarkDCAndGC("0RGB memory", dc, gcdc);
#endif // __WXMSW__ ||__WXOSX__
}
{
wxBitmap bmp(opts.width, opts.height, 32);
#if defined(__WXMSW__) || defined(__WXOSX__)
bmp.UseAlpha(true);
#endif // __WXMSW__ || __WXOSX__
wxMemoryDC dc(bmp);
wxGCDC gcdc;
if ( m_renderer )
{
wxGraphicsContext* gc = m_renderer->CreateContext(dc);
gcdc.SetGraphicsContext(gc);
}
BenchmarkDCAndGC("ARGB memory", dc, gcdc);
}
}
wxTheApp->ExitMainLoop();
}
void BenchmarkDCAndGC(const char* dckind, wxDC& dc, wxGCDC& gcdc)
{
if ( opts.useDC )
{
BenchmarkAll(wxString::Format("%6s DC", dckind), dc);
}
else if ( opts.useGC && gcdc.IsOk() )
{
wxString rendName = gcdc.GetGraphicsContext()->GetRenderer()->GetName();
BenchmarkAll(wxString::Format("%6s GC (%s)", dckind, rendName.c_str()), gcdc);
}
}
void BenchmarkAll(const wxString& msg, wxDC& dc)
{
BenchmarkBitmaps(msg, dc);
BenchmarkImages(msg, dc);
BenchmarkLines(msg, dc);
BenchmarkRawBitmaps(msg, dc);
BenchmarkRectangles(msg, dc);
BenchmarkRoundedRectangles(msg, dc);
BenchmarkCircles(msg, dc);
BenchmarkEllipses(msg, dc);
BenchmarkTextExtent(msg, dc);
BenchmarkPartialTextExtents(msg, dc);
}
void SetupDC(wxDC& dc)
{
if ( opts.mapMode != 0 )
dc.SetMapMode((wxMappingMode)opts.mapMode);
bool setPen = false;
wxPenInfo penInfo(*wxWHITE);
if ( opts.penWidth != 0 )
{
penInfo.Width(opts.penWidth);
setPen = true;
}
if ( opts.penStyle != wxPENSTYLE_INVALID )
{
penInfo.Style(opts.penStyle);
setPen = true;
}
if ( opts.penQuality != wxPEN_QUALITY_DEFAULT )
{
penInfo.Quality(opts.penQuality);
setPen = true;
}
if ( setPen )
dc.SetPen(penInfo);
}
void BenchmarkLines(const wxString& msg, wxDC& dc)
{
if ( !opts.testLines )
return;
SetupDC(dc);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxStopWatch sw;
int x0 = 0,
y0 = 0;
for ( int n = 0; n < opts.numIters; n++ )
{
int x1 = rand() % opts.width,
y1 = rand() % opts.height;
dc.DrawLine(x0, y0, x1, y1);
x0 = x1;
y0 = y1;
}
const long t = sw.Time();
wxPrintf("%ld lines done in %ldms = %gus/line\n",
opts.numIters, t, (1000. * t)/opts.numIters);
// Horizontal lines
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
sw.Start();
x0 = 0;
for ( int n = 0; n < opts.numIters; n++ )
{
int x1 = rand() % opts.width;
int y = rand() % opts.height;
dc.DrawLine(x0, y, x1, y);
x0 = x1;
}
const long t2 = sw.Time();
wxPrintf("%ld horizontal lines done in %ldms = %gus/line\n",
opts.numIters, t2, (1000. * t2) / opts.numIters);
// Vertical lines
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
sw.Start();
y0 = 0;
for ( int n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width;
int y1 = rand() % opts.height;
dc.DrawLine(x, y0, x, y1);
y0 = y1;
}
const long t3 = sw.Time();
wxPrintf("%ld vertical lines done in %ldms = %gus/line\n",
opts.numIters, t3, (1000. * t3) / opts.numIters);
// Cross hair
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
sw.Start();
for ( int n = 0; n < opts.numIters; n++ )
{
x0 = rand() % opts.width;
y0 = rand() % opts.height;
dc.CrossHair(x0, y0);
}
const long t4 = sw.Time();
wxPrintf("%ld cross hairs done in %ldms = %gus/line\n",
opts.numIters, t4, (1000. * t4) / (2*opts.numIters));
}
void BenchmarkRectangles(const wxString& msg, wxDC& dc)
{
if ( !opts.testRectangles )
return;
SetupDC(dc);
dc.SetBrush( *wxRED_BRUSH );
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxStopWatch sw;
for ( int n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width,
y = rand() % opts.height;
dc.DrawRectangle(x, y, 32, 32);
}
const long t = sw.Time();
wxPrintf("%ld rects done in %ldms = %gus/rect\n",
opts.numIters, t, (1000. * t)/opts.numIters);
}
void BenchmarkRoundedRectangles(const wxString& msg, wxDC& dc)
{
if ( !opts.testRectangles )
return;
SetupDC(dc);
dc.SetBrush( *wxCYAN_BRUSH );
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxStopWatch sw;
for ( int n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width,
y = rand() % opts.height;
dc.DrawRoundedRectangle(x, y, 48, 32, 8);
}
const long t = sw.Time();
wxPrintf("%ld rounded rects done in %ldms = %gus/rect\n",
opts.numIters, t, (1000. * t)/opts.numIters);
}
void BenchmarkCircles(const wxString& msg, wxDC& dc)
{
if ( !opts.testCircles )
return;
SetupDC(dc);
dc.SetBrush( *wxGREEN_BRUSH );
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxStopWatch sw;
for ( long n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width,
y = rand() % opts.height;
dc.DrawCircle(x, y, 32);
}
const long t = sw.Time();
wxPrintf("%ld circles done in %ldms = %gus/circle\n",
opts.numIters, t, (1000. * t)/opts.numIters);
}
void BenchmarkEllipses(const wxString& msg, wxDC& dc)
{
if ( !opts.testEllipses )
return;
SetupDC(dc);
dc.SetBrush( *wxBLUE_BRUSH );
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxStopWatch sw;
for ( long n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width,
y = rand() % opts.height;
dc.DrawEllipse(x, y, 48, 32);
}
const long t = sw.Time();
wxPrintf("%ld ellipses done in %ldms = %gus/ellipse\n",
opts.numIters, t, (1000. * t)/opts.numIters);
}
void BenchmarkTextExtent(const wxString& msg, wxDC& dc)
{
if ( !opts.testTextExtent )
return;
SetupDC(dc);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
const wxString str("The quick brown fox jumps over the lazy dog");
wxSize size;
wxStopWatch sw;
for ( long n = 0; n < opts.numIters; n++ )
{
if ( opts.testMultiLineTextExtent )
size += dc.GetMultiLineTextExtent(str);
else
size += dc.GetTextExtent(str);
}
const long t = sw.Time();
wxPrintf("%ld text extent measures done in %ldms = %gus/call\n",
opts.numIters, t, (1000. * t)/opts.numIters);
}
void BenchmarkPartialTextExtents(const wxString& msg, wxDC& dc)
{
if ( !opts.testPartialTextExtents )
return;
SetupDC(dc);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
const wxString str("The quick brown fox jumps over the lazy dog");
wxArrayInt widths;
wxStopWatch sw;
for ( long n = 0; n < opts.numIters; n++ )
{
if ( !dc.GetPartialTextExtents(str, widths) )
{
wxPrintf("ERROR: GetPartialTextExtents() failed\n");
return;
}
}
const long t = sw.Time();
wxPrintf("%ld partial text extents measures done in %ldms = %gus/call\n",
opts.numIters, t, (1000. * t)/opts.numIters);
}
void BenchmarkBitmaps(const wxString& msg, wxDC& dc)
{
if ( !opts.testBitmaps )
return;
SetupDC(dc);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxStopWatch sw;
for ( int n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width,
y = rand() % opts.height;
dc.DrawBitmap(m_bitmapARGB, x, y, true);
}
const long t = sw.Time();
wxPrintf("%ld ARGB bitmaps done in %ldms = %gus/bitmap\n",
opts.numIters, t, (1000. * t)/opts.numIters);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
sw.Start();
for ( int n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width,
y = rand() % opts.height;
dc.DrawBitmap(m_bitmapRGB, x, y, true);
}
const long t2 = sw.Time();
wxPrintf("%ld RGB bitmaps done in %ldms = %gus/bitmap\n",
opts.numIters, t2, (1000. * t2)/opts.numIters);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
sw.Start();
for ( int n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width,
y = rand() % opts.height;
dc.DrawBitmap(m_bitmapARGBwithMask, x, y, true);
}
const long t3 = sw.Time();
wxPrintf("%ld ARGB bitmaps with mask done in %ldms = %gus/bitmap\n",
opts.numIters, t3, (1000. * t3) / opts.numIters);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
sw.Start();
for ( int n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width,
y = rand() % opts.height;
dc.DrawBitmap(m_bitmapRGBwithMask, x, y, true);
}
const long t4 = sw.Time();
wxPrintf("%ld RGB bitmaps with mask done in %ldms = %gus/bitmap\n",
opts.numIters, t4, (1000. * t4) / opts.numIters);
}
void BenchmarkImages(const wxString& msg, wxDC& dc)
{
if ( !opts.testImages )
return;
if ( opts.mapMode != 0 )
dc.SetMapMode((wxMappingMode)opts.mapMode);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxImage image(wxSize(opts.width, opts.height), false /* don't clear */);
wxStopWatch sw;
for ( int n = 0; n < opts.numIters; n++ )
{
UpdateRGB(image.GetData(), n);
dc.DrawBitmap(image, 0, 0);
}
const long t = sw.Time();
wxPrintf("%ld images done in %ldms = %gus/image or %ld FPS\n",
opts.numIters, t, (1000. * t)/opts.numIters,
(1000*opts.numIters + t - 1)/t);
}
void BenchmarkRawBitmaps(const wxString& msg, wxDC& dc)
{
if ( !opts.testRawBitmaps )
return;
if ( opts.mapMode != 0 )
dc.SetMapMode((wxMappingMode)opts.mapMode);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxBitmap bitmap(opts.width, opts.height, 24);
wxNativePixelData data(bitmap);
wxStopWatch sw;
for ( int n = 0; n < opts.numIters; n++ )
{
unsigned char c = n % 256;
{
wxNativePixelData::Iterator p(data);
for ( int y = 0; y < opts.height; ++y )
{
wxNativePixelData::Iterator rowStart = p;
for ( int x = 0; x < opts.width; ++x )
{
p.Red() =
p.Green() =
p.Blue() = c;
++p;
}
p = rowStart;
p.OffsetY(data, 1);
c++;
}
}
dc.DrawBitmap(bitmap, 0, 0);
}
const long t = sw.Time();
wxPrintf("%ld raw bitmaps done in %ldms = %gus/bitmap or %ld FPS\n",
opts.numIters, t, (1000. * t)/opts.numIters,
(1000*opts.numIters + t - 1)/t);
}
wxBitmap m_bitmapARGB;
wxBitmap m_bitmapRGB;
wxBitmap m_bitmapARGBwithMask;
wxBitmap m_bitmapRGBwithMask;
#if wxUSE_GLCANVAS
wxGLCanvas* m_glCanvas;
wxGLContext* m_glContext;
#endif // wxUSE_GLCANVAS
wxGraphicsRenderer* m_renderer;
};
class GraphicsBenchmarkApp : public wxApp
{
public:
virtual void OnInitCmdLine(wxCmdLineParser& parser)
{
static const wxCmdLineEntryDesc desc[] =
{
{ wxCMD_LINE_SWITCH, "", "bitmaps" },
{ wxCMD_LINE_SWITCH, "", "images" },
{ wxCMD_LINE_SWITCH, "", "lines" },
{ wxCMD_LINE_SWITCH, "", "rawbmp" },
{ wxCMD_LINE_SWITCH, "", "rectangles" },
{ wxCMD_LINE_SWITCH, "", "circles" },
{ wxCMD_LINE_SWITCH, "", "ellipses" },
{ wxCMD_LINE_SWITCH, "", "textextent" },
{ wxCMD_LINE_SWITCH, "", "multilinetextextent" },
{ wxCMD_LINE_SWITCH, "", "partialtextextents" },
{ wxCMD_LINE_SWITCH, "", "paint" },
{ wxCMD_LINE_SWITCH, "", "client" },
{ wxCMD_LINE_SWITCH, "", "memory" },
{ wxCMD_LINE_SWITCH, "", "dc" },
{ wxCMD_LINE_SWITCH, "", "gc" },
#if wxUSE_GLCANVAS
{ wxCMD_LINE_SWITCH, "", "gl" },
#endif // wxUSE_GLCANVAS
{ wxCMD_LINE_OPTION, "m", "map-mode", "", wxCMD_LINE_VAL_NUMBER },
{ wxCMD_LINE_OPTION, "p", "pen-width", "", wxCMD_LINE_VAL_NUMBER },
{ wxCMD_LINE_OPTION, "s", "pen-style", "solid | dot | long_dash | short_dash", wxCMD_LINE_VAL_STRING },
{ wxCMD_LINE_OPTION, "", "pen-quality", "default | low | high", wxCMD_LINE_VAL_STRING },
{ wxCMD_LINE_OPTION, "w", "width", "", wxCMD_LINE_VAL_NUMBER },
{ wxCMD_LINE_OPTION, "h", "height", "", wxCMD_LINE_VAL_NUMBER },
{ wxCMD_LINE_OPTION, "I", "images", "", wxCMD_LINE_VAL_NUMBER },
{ wxCMD_LINE_OPTION, "N", "number-of-iterations", "", wxCMD_LINE_VAL_NUMBER },
#ifdef __WXMSW__
{ wxCMD_LINE_OPTION, "r", "renderer", "gdiplus | direct2d | cairo", wxCMD_LINE_VAL_STRING },
#endif // __WXMSW__
{ wxCMD_LINE_NONE },
};
parser.SetDesc(desc);
}
virtual bool OnCmdLineParsed(wxCmdLineParser& parser)
{
if ( parser.Found("m", &opts.mapMode) &&
(opts.mapMode < 1 || opts.mapMode > wxMM_METRIC) )
return false;
if ( parser.Found("p", &opts.penWidth) && opts.penWidth < 1 )
return false;
wxString penStyle;
if ( parser.Found("pen-style", &penStyle) )
{
if ( !penStyle.empty() )
{
if ( penStyle == wxS("solid") )
{
opts.penStyle = wxPENSTYLE_SOLID;
}
else if ( penStyle == wxS("dot") )
{
opts.penStyle = wxPENSTYLE_DOT;
}
else if ( penStyle == wxS("long_dash") )
{
opts.penStyle = wxPENSTYLE_LONG_DASH;
}
else if ( penStyle == wxS("short_dash") )
{
opts.penStyle = wxPENSTYLE_SHORT_DASH;
}
else
{
wxLogError(wxS("Unsupported pen style."));
return false;
}
}
}
wxString penQuality;
if ( parser.Found("pen-quality", &penQuality) )
{
if ( penQuality == "low" )
opts.penQuality = wxPEN_QUALITY_LOW;
else if ( penQuality == "high" )
opts.penQuality = wxPEN_QUALITY_HIGH;
else if ( penQuality != "default" )
{
wxLogError("Unsupported pen quality.");
return false;
}
}
if ( parser.Found("w", &opts.width) && opts.width < 1 )
return false;
if ( parser.Found("h", &opts.height) && opts.height < 1 )
return false;
if ( parser.Found("N", &opts.numIters) && opts.numIters < 1 )
return false;
opts.testBitmaps = parser.Found("bitmaps");
opts.testImages = parser.Found("images");
opts.testLines = parser.Found("lines");
opts.testRawBitmaps = parser.Found("rawbmp");
opts.testRectangles = parser.Found("rectangles");
opts.testCircles = parser.Found("circles");
opts.testEllipses = parser.Found("ellipses");
opts.testTextExtent = parser.Found("textextent");
opts.testMultiLineTextExtent = parser.Found("multilinetextextent");
opts.testPartialTextExtents = parser.Found("partialtextextents");
if ( !(opts.testBitmaps || opts.testImages || opts.testLines
|| opts.testRawBitmaps || opts.testRectangles
|| opts.testCircles || opts.testEllipses
|| opts.testTextExtent || opts.testPartialTextExtents) )
{
// Do everything by default.
opts.testBitmaps =
opts.testImages =
opts.testLines =
opts.testRawBitmaps =
opts.testRectangles =
opts.testCircles =
opts.testEllipses =
opts.testTextExtent =
opts.testPartialTextExtents = true;
}
opts.usePaint = parser.Found("paint");
opts.useClient = parser.Found("client");
opts.useMemory = parser.Found("memory");
if ( !(opts.usePaint || opts.useClient || opts.useMemory) )
{
opts.usePaint =
opts.useClient =
opts.useMemory = true;
}
opts.useDC = parser.Found("dc");
opts.useGC = parser.Found("gc");
#if wxUSE_GLCANVAS
opts.useGL = parser.Found("gl");
if ( opts.useGL )
{
if ( opts.useDC || opts.useGC )
{
wxLogError("Can't use both OpenGL and normal graphics.");
return false;
}
}
else // Not using OpenGL
#endif // wxUSE_GLCANVAS
{
if ( !(opts.useDC || opts.useGC) )
{
opts.useDC =
opts.useGC = true;
}
}
opts.renderer = GraphicsBenchmarkOptions::Default;
#ifdef __WXMSW__
wxString rendererName;
if ( parser.Found("renderer", &rendererName) )
{
if ( !opts.useGC )
{
wxLogError("Renderer can be specified only when using graphics.");
return false;
}
if ( !rendererName.empty() )
{
if ( rendererName == wxS("gdiplus") )
{
opts.renderer = GraphicsBenchmarkOptions::GDIPlus;
}
else if ( rendererName == wxS("direct2d") )
{
opts.renderer = GraphicsBenchmarkOptions::Direct2D;
}
else if ( rendererName == wxS("cairo") )
{
opts.renderer = GraphicsBenchmarkOptions::Cairo;
}
else
{
wxLogError( wxS("Unknown renderer name.") );
return false;
}
}
}
#endif // __WXMSW__
return true;
}
virtual bool OnInit()
{
if ( !wxApp::OnInit() )
return false;
new GraphicsBenchmarkFrame;
return true;
}
};
wxIMPLEMENT_APP_CONSOLE(GraphicsBenchmarkApp);