Quartz2D display rendering driver for Mac
Implement a Quartz 2D (aka Core Graphics) output renderer for the Wx interface as a subclass of BasicDrawingPanel called Quartz2DDrawingPanel. Split BasicDrawingPanel's DrawArea() into DrawArea() and DrawImage(), with DrawImage() receiving both the wxPaintDC and the wxImage, the wxImage is created with a direct pointer to the frame buffer when possible (24bpp). Implement Quartz2DDrawingPanel in macsupport.mm based on the code here: http://www.cocoabuilder.com/archive/cocoa/309165-how-to-quickly-paint-to-cocoa-view-from-bitmap-in-memory.html and here: http://stackoverflow.com/questions/2261177/cgimage-from-byte-array the GetData() method of wxImage is used to avoid copying the frame buffer. Add RND_QUARTZ2D to the renderers enum and update all config stuff and the XRC to support it. As well as the DrawingPanel instantiation code in GameArea::OnIdle().
This commit is contained in:
parent
60e98506e7
commit
0e6c1b66e3
|
@ -9,6 +9,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void DrawArea(wxWindowDC& dc);
|
void DrawArea(wxWindowDC& dc);
|
||||||
|
virtual void DrawImage(wxWindowDC& dc, wxImage* im);
|
||||||
|
|
||||||
DECLARE_CLASS()
|
DECLARE_CLASS()
|
||||||
};
|
};
|
||||||
|
@ -64,4 +65,15 @@ protected:
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__WXMAC__)
|
||||||
|
class Quartz2DDrawingPanel : public BasicDrawingPanel {
|
||||||
|
public:
|
||||||
|
Quartz2DDrawingPanel(wxWindow* parent, int _width, int _height);
|
||||||
|
virtual void DrawImage(wxWindowDC& dc, wxImage* im);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DECLARE_CLASS()
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* GAME_DRAWING_H */
|
#endif /* GAME_DRAWING_H */
|
||||||
|
|
|
@ -3301,8 +3301,12 @@ bool MainFrame::BindControls()
|
||||||
// validator just for this, and spinctrl is good enough.
|
// validator just for this, and spinctrl is good enough.
|
||||||
getgtc("DefaultScale", gopts.video_scale);
|
getgtc("DefaultScale", gopts.video_scale);
|
||||||
getsc("MaxScale", maxScale);
|
getsc("MaxScale", maxScale);
|
||||||
/// Advanced
|
/// Basic
|
||||||
getrbi("OutputSimple", gopts.render_method, RND_SIMPLE);
|
getrbi("OutputSimple", gopts.render_method, RND_SIMPLE);
|
||||||
|
getrbi("OutputQuartz2D", gopts.render_method, RND_QUARTZ2D);
|
||||||
|
#if !defined(__WXMAC__)
|
||||||
|
rb->Hide();
|
||||||
|
#endif
|
||||||
getrbi("OutputOpenGL", gopts.render_method, RND_OPENGL);
|
getrbi("OutputOpenGL", gopts.render_method, RND_OPENGL);
|
||||||
#ifdef NO_OGL
|
#ifdef NO_OGL
|
||||||
rb->Hide();
|
rb->Hide();
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
#include <wx/rawbmp.h>
|
||||||
|
|
||||||
#include "wxvbam.h"
|
#include "wxvbam.h"
|
||||||
|
#include "drawing.h"
|
||||||
|
|
||||||
double HiDPIAware::HiDPIScaleFactor()
|
double HiDPIAware::HiDPIScaleFactor()
|
||||||
{
|
{
|
||||||
|
@ -37,3 +40,66 @@ void HiDPIAware::GetRealPixelClientSize(int* x, int* y)
|
||||||
*x = backing_size.width;
|
*x = backing_size.width;
|
||||||
*y = backing_size.height;
|
*y = backing_size.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS(Quartz2DDrawingPanel, BasicDrawingPanel)
|
||||||
|
|
||||||
|
Quartz2DDrawingPanel::Quartz2DDrawingPanel(wxWindow* parent, int _width, int _height)
|
||||||
|
: BasicDrawingPanel(parent, _width, _height)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Quartz2DDrawingPanel::DrawImage(wxWindowDC& dc, wxImage* im)
|
||||||
|
{
|
||||||
|
NSView* view = (NSView*)(GetWindow()->GetHandle());
|
||||||
|
|
||||||
|
size_t w = std::ceil(width * scale);
|
||||||
|
size_t h = std::ceil(height * scale);
|
||||||
|
size_t size = w * h * 3;
|
||||||
|
|
||||||
|
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, im->GetData(), size, NULL);
|
||||||
|
|
||||||
|
CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
|
||||||
|
|
||||||
|
CGImageRef image = CGImageCreate(
|
||||||
|
w, h, 8, 24, w * 3, color_space,
|
||||||
|
kCGBitmapByteOrderDefault,
|
||||||
|
provider, NULL, true, kCGRenderingIntentDefault
|
||||||
|
);
|
||||||
|
|
||||||
|
// draw the image
|
||||||
|
|
||||||
|
[view lockFocus];
|
||||||
|
|
||||||
|
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
||||||
|
|
||||||
|
CGContextSaveGState(context);
|
||||||
|
|
||||||
|
CGContextSetAllowsAntialiasing(context, false);
|
||||||
|
CGContextSetRGBFillColor(context, 0, 0, 0, 1.0);
|
||||||
|
CGContextSetRGBStrokeColor(context, 0, 0, 0, 1.0);
|
||||||
|
|
||||||
|
CGContextTranslateCTM(context, 0, view.bounds.size.height);
|
||||||
|
CGContextScaleCTM(context, 1.0, -1.0);
|
||||||
|
|
||||||
|
CGContextDrawImage(context, view.bounds, image);
|
||||||
|
|
||||||
|
CGContextRestoreGState(context);
|
||||||
|
|
||||||
|
// have to draw something on the dc or it doesn't allow the frame to appear,
|
||||||
|
// I don't know of any better way to do this.
|
||||||
|
{
|
||||||
|
wxCoord w, h;
|
||||||
|
dc.GetSize(&w, &h);
|
||||||
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||||
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||||
|
dc.DrawRectangle(w-2, h-2, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
[view unlockFocus];
|
||||||
|
|
||||||
|
// and release everything
|
||||||
|
|
||||||
|
CGDataProviderRelease(provider);
|
||||||
|
CGColorSpaceRelease(color_space);
|
||||||
|
CGImageRelease(image);
|
||||||
|
}
|
||||||
|
|
|
@ -157,6 +157,8 @@ opt_desc opts[] = {
|
||||||
INTOPT("Display/MaxThreads", "Multithread", wxTRANSLATE("Maximum number of threads to run filters in"), gopts.max_threads, 1, 8),
|
INTOPT("Display/MaxThreads", "Multithread", wxTRANSLATE("Maximum number of threads to run filters in"), gopts.max_threads, 1, 8),
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
ENUMOPT("Display/RenderMethod", "", wxTRANSLATE("Render method; if unsupported, simple method will be used"), gopts.render_method, wxTRANSLATE("simple|opengl|cairo|direct3d")),
|
ENUMOPT("Display/RenderMethod", "", wxTRANSLATE("Render method; if unsupported, simple method will be used"), gopts.render_method, wxTRANSLATE("simple|opengl|cairo|direct3d")),
|
||||||
|
#elif defined(__WXMAC__)
|
||||||
|
ENUMOPT("Display/RenderMethod", "", wxTRANSLATE("Render method; if unsupported, simple method will be used"), gopts.render_method, wxTRANSLATE("simple|opengl|cairo||quartz2d")),
|
||||||
#else
|
#else
|
||||||
ENUMOPT("Display/RenderMethod", "", wxTRANSLATE("Render method; if unsupported, simple method will be used"), gopts.render_method, wxTRANSLATE("simple|opengl|cairo")),
|
ENUMOPT("Display/RenderMethod", "", wxTRANSLATE("Render method; if unsupported, simple method will be used"), gopts.render_method, wxTRANSLATE("simple|opengl|cairo")),
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -989,20 +989,22 @@ void GameArea::OnIdle(wxIdleEvent& event)
|
||||||
case RND_SIMPLE:
|
case RND_SIMPLE:
|
||||||
panel = new BasicDrawingPanel(this, basic_width, basic_height);
|
panel = new BasicDrawingPanel(this, basic_width, basic_height);
|
||||||
break;
|
break;
|
||||||
|
#ifdef __WXMAC__
|
||||||
|
case RND_QUARTZ2D:
|
||||||
|
panel = new Quartz2DDrawingPanel(this, basic_width, basic_height);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
#ifndef NO_OGL
|
#ifndef NO_OGL
|
||||||
|
|
||||||
case RND_OPENGL:
|
case RND_OPENGL:
|
||||||
panel = new GLDrawingPanel(this, basic_width, basic_height);
|
panel = new GLDrawingPanel(this, basic_width, basic_height);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifndef NO_CAIRO
|
#ifndef NO_CAIRO
|
||||||
|
|
||||||
case RND_CAIRO:
|
case RND_CAIRO:
|
||||||
panel = new CairoDrawingPanel(this, basic_width, basic_height);
|
panel = new CairoDrawingPanel(this, basic_width, basic_height);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
|
|
||||||
case RND_DIRECT3D:
|
case RND_DIRECT3D:
|
||||||
panel = new DXDrawingPanel(this, basic_width, basic_height);
|
panel = new DXDrawingPanel(this, basic_width, basic_height);
|
||||||
break;
|
break;
|
||||||
|
@ -1380,6 +1382,7 @@ void DrawingPanel::DrawingPanelInit()
|
||||||
|
|
||||||
// this is not 2.8 compatible, sorry
|
// this is not 2.8 compatible, sorry
|
||||||
w->Bind(wxEVT_PAINT, &DrawingPanel::PaintEv, this);
|
w->Bind(wxEVT_PAINT, &DrawingPanel::PaintEv, this);
|
||||||
|
w->Bind(wxEVT_ERASE_BACKGROUND, &DrawingPanel::EraseBackground, this);
|
||||||
|
|
||||||
did_init = true;
|
did_init = true;
|
||||||
}
|
}
|
||||||
|
@ -1403,6 +1406,11 @@ void DrawingPanel::PaintEv(wxPaintEvent& ev)
|
||||||
DrawOSD(dc);
|
DrawOSD(dc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DrawingPanel::EraseBackground(wxEraseEvent& ev)
|
||||||
|
{
|
||||||
|
// do nothing, do not allow propagation
|
||||||
|
}
|
||||||
|
|
||||||
// In order to run filters in parallel, they have to run from the method of
|
// In order to run filters in parallel, they have to run from the method of
|
||||||
// a wxThread-derived class
|
// a wxThread-derived class
|
||||||
// threads are only created once, if possible, to avoid thread creation
|
// threads are only created once, if possible, to avoid thread creation
|
||||||
|
@ -1912,17 +1920,16 @@ BasicDrawingPanel::BasicDrawingPanel(wxWindow* parent, int _width, int _height)
|
||||||
|
|
||||||
void BasicDrawingPanel::DrawArea(wxWindowDC& dc)
|
void BasicDrawingPanel::DrawArea(wxWindowDC& dc)
|
||||||
{
|
{
|
||||||
wxBitmap* bm;
|
wxImage* im;
|
||||||
|
|
||||||
if (systemColorDepth == 24) {
|
if (systemColorDepth == 24) {
|
||||||
// never scaled, no borders, no transformations needed
|
// never scaled, no borders, no transformations needed
|
||||||
wxImage im(width, height, todraw, true);
|
im = new wxImage(width, height, todraw, true);
|
||||||
bm = new wxBitmap(im);
|
|
||||||
} else if (out_16) {
|
} else if (out_16) {
|
||||||
// scaled by filters, top/right borders, transform to 24-bit
|
// scaled by filters, top/right borders, transform to 24-bit
|
||||||
wxImage im(std::ceil(width * scale), std::ceil(height * scale), false);
|
im = new wxImage(std::ceil(width * scale), std::ceil(height * scale), false);
|
||||||
uint16_t* src = (uint16_t*)todraw + (int)std::ceil((width + 2) * scale); // skip top border
|
uint16_t* src = (uint16_t*)todraw + (int)std::ceil((width + 2) * scale); // skip top border
|
||||||
uint8_t* dst = im.GetData();
|
uint8_t* dst = im->GetData();
|
||||||
|
|
||||||
for (int y = 0; y < std::ceil(height * scale); y++) {
|
for (int y = 0; y < std::ceil(height * scale); y++) {
|
||||||
for (int x = 0; x < std::ceil(width * scale); x++, src++) {
|
for (int x = 0; x < std::ceil(width * scale); x++, src++) {
|
||||||
|
@ -1933,14 +1940,12 @@ void BasicDrawingPanel::DrawArea(wxWindowDC& dc)
|
||||||
|
|
||||||
src += 2; // skip rhs border
|
src += 2; // skip rhs border
|
||||||
}
|
}
|
||||||
|
|
||||||
bm = new wxBitmap(im);
|
|
||||||
} else // 32-bit
|
} else // 32-bit
|
||||||
{
|
{
|
||||||
// scaled by filters, top/right borders, transform to 24-bit
|
// scaled by filters, top/right borders, transform to 24-bit
|
||||||
wxImage im(std::ceil(width * scale), std::ceil(height * scale), false);
|
im = new wxImage(std::ceil(width * scale), std::ceil(height * scale), false);
|
||||||
uint32_t* src = (uint32_t*)todraw + (int)std::ceil((width + 1) * scale); // skip top border
|
uint32_t* src = (uint32_t*)todraw + (int)std::ceil((width + 1) * scale); // skip top border
|
||||||
uint8_t* dst = im.GetData();
|
uint8_t* dst = im->GetData();
|
||||||
|
|
||||||
for (int y = 0; y < std::ceil(height * scale); y++) {
|
for (int y = 0; y < std::ceil(height * scale); y++) {
|
||||||
for (int x = 0; x < std::ceil(width * scale); x++, src++) {
|
for (int x = 0; x < std::ceil(width * scale); x++, src++) {
|
||||||
|
@ -1951,18 +1956,23 @@ void BasicDrawingPanel::DrawArea(wxWindowDC& dc)
|
||||||
|
|
||||||
++src; // skip rhs border
|
++src; // skip rhs border
|
||||||
}
|
}
|
||||||
|
|
||||||
bm = new wxBitmap(im);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DrawImage(dc, im);
|
||||||
|
|
||||||
|
delete im;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BasicDrawingPanel::DrawImage(wxWindowDC& dc, wxImage* im)
|
||||||
|
{
|
||||||
double sx, sy;
|
double sx, sy;
|
||||||
int w, h;
|
int w, h;
|
||||||
GetClientSize(&w, &h);
|
GetClientSize(&w, &h);
|
||||||
sx = w / (width * scale);
|
sx = w / (width * scale);
|
||||||
sy = h / (height * scale);
|
sy = h / (height * scale);
|
||||||
dc.SetUserScale(sx, sy);
|
dc.SetUserScale(sx, sy);
|
||||||
dc.DrawBitmap(*bm, 0, 0);
|
wxBitmap bm(*im);
|
||||||
delete bm;
|
dc.DrawBitmap(bm, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NO_OGL
|
#ifndef NO_OGL
|
||||||
|
|
|
@ -416,10 +416,13 @@ enum ifbfunc {
|
||||||
};
|
};
|
||||||
|
|
||||||
// make sure and keep this in sync with opts.cpp!
|
// make sure and keep this in sync with opts.cpp!
|
||||||
enum renderer { RND_SIMPLE,
|
enum renderer {
|
||||||
|
RND_SIMPLE,
|
||||||
RND_OPENGL,
|
RND_OPENGL,
|
||||||
RND_CAIRO,
|
RND_CAIRO,
|
||||||
RND_DIRECT3D };
|
RND_DIRECT3D,
|
||||||
|
RND_QUARTZ2D,
|
||||||
|
};
|
||||||
|
|
||||||
// likewise
|
// likewise
|
||||||
enum audioapi { AUD_SDL,
|
enum audioapi { AUD_SDL,
|
||||||
|
@ -635,7 +638,8 @@ public:
|
||||||
virtual wxWindow* GetWindow() { return dynamic_cast<wxWindow*>(this); }
|
virtual wxWindow* GetWindow() { return dynamic_cast<wxWindow*>(this); }
|
||||||
virtual void Delete() { (dynamic_cast<wxWindow*>(this))->Destroy(); }
|
virtual void Delete() { (dynamic_cast<wxWindow*>(this))->Destroy(); }
|
||||||
|
|
||||||
void PaintEv(wxPaintEvent& ev);
|
virtual void PaintEv(wxPaintEvent& ev);
|
||||||
|
virtual void EraseBackground(wxEraseEvent& ev);
|
||||||
protected:
|
protected:
|
||||||
virtual void DrawArea(wxWindowDC&) = 0;
|
virtual void DrawArea(wxWindowDC&) = 0;
|
||||||
virtual void DrawOSD(wxWindowDC&);
|
virtual void DrawOSD(wxWindowDC&);
|
||||||
|
|
|
@ -35,6 +35,13 @@
|
||||||
<flag>wxALL</flag>
|
<flag>wxALL</flag>
|
||||||
<border>5</border>
|
<border>5</border>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="sizeritem">
|
||||||
|
<object class="wxRadioButton" name="OutputQuartz2D">
|
||||||
|
<label>Quartz2D</label>
|
||||||
|
</object>
|
||||||
|
<flag>wxALL</flag>
|
||||||
|
<border>5</border>
|
||||||
|
</object>
|
||||||
<object class="sizeritem">
|
<object class="sizeritem">
|
||||||
<object class="wxRadioButton" name="OutputOpenGL">
|
<object class="wxRadioButton" name="OutputOpenGL">
|
||||||
<label>OpenGL</label>
|
<label>OpenGL</label>
|
||||||
|
|
Loading…
Reference in New Issue