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:
|
||||
void DrawArea(wxWindowDC& dc);
|
||||
virtual void DrawImage(wxWindowDC& dc, wxImage* im);
|
||||
|
||||
DECLARE_CLASS()
|
||||
};
|
||||
|
@ -64,4 +65,15 @@ protected:
|
|||
};
|
||||
#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 */
|
||||
|
|
|
@ -3301,8 +3301,12 @@ bool MainFrame::BindControls()
|
|||
// validator just for this, and spinctrl is good enough.
|
||||
getgtc("DefaultScale", gopts.video_scale);
|
||||
getsc("MaxScale", maxScale);
|
||||
/// Advanced
|
||||
/// Basic
|
||||
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);
|
||||
#ifdef NO_OGL
|
||||
rb->Hide();
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include <wx/rawbmp.h>
|
||||
|
||||
#include "wxvbam.h"
|
||||
#include "drawing.h"
|
||||
|
||||
double HiDPIAware::HiDPIScaleFactor()
|
||||
{
|
||||
|
@ -37,3 +40,66 @@ void HiDPIAware::GetRealPixelClientSize(int* x, int* y)
|
|||
*x = backing_size.width;
|
||||
*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),
|
||||
#ifdef __WXMSW__
|
||||
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
|
||||
ENUMOPT("Display/RenderMethod", "", wxTRANSLATE("Render method; if unsupported, simple method will be used"), gopts.render_method, wxTRANSLATE("simple|opengl|cairo")),
|
||||
#endif
|
||||
|
|
|
@ -989,20 +989,22 @@ void GameArea::OnIdle(wxIdleEvent& event)
|
|||
case RND_SIMPLE:
|
||||
panel = new BasicDrawingPanel(this, basic_width, basic_height);
|
||||
break;
|
||||
#ifdef __WXMAC__
|
||||
case RND_QUARTZ2D:
|
||||
panel = new Quartz2DDrawingPanel(this, basic_width, basic_height);
|
||||
break;
|
||||
#endif
|
||||
#ifndef NO_OGL
|
||||
|
||||
case RND_OPENGL:
|
||||
panel = new GLDrawingPanel(this, basic_width, basic_height);
|
||||
break;
|
||||
#endif
|
||||
#ifndef NO_CAIRO
|
||||
|
||||
case RND_CAIRO:
|
||||
panel = new CairoDrawingPanel(this, basic_width, basic_height);
|
||||
break;
|
||||
#endif
|
||||
#ifdef __WXMSW__
|
||||
|
||||
case RND_DIRECT3D:
|
||||
panel = new DXDrawingPanel(this, basic_width, basic_height);
|
||||
break;
|
||||
|
@ -1380,6 +1382,7 @@ void DrawingPanel::DrawingPanelInit()
|
|||
|
||||
// this is not 2.8 compatible, sorry
|
||||
w->Bind(wxEVT_PAINT, &DrawingPanel::PaintEv, this);
|
||||
w->Bind(wxEVT_ERASE_BACKGROUND, &DrawingPanel::EraseBackground, this);
|
||||
|
||||
did_init = true;
|
||||
}
|
||||
|
@ -1403,6 +1406,11 @@ void DrawingPanel::PaintEv(wxPaintEvent& ev)
|
|||
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
|
||||
// a wxThread-derived class
|
||||
// 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)
|
||||
{
|
||||
wxBitmap* bm;
|
||||
wxImage* im;
|
||||
|
||||
if (systemColorDepth == 24) {
|
||||
// never scaled, no borders, no transformations needed
|
||||
wxImage im(width, height, todraw, true);
|
||||
bm = new wxBitmap(im);
|
||||
im = new wxImage(width, height, todraw, true);
|
||||
} else if (out_16) {
|
||||
// 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
|
||||
uint8_t* dst = im.GetData();
|
||||
uint8_t* dst = im->GetData();
|
||||
|
||||
for (int y = 0; y < std::ceil(height * scale); y++) {
|
||||
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
|
||||
}
|
||||
|
||||
bm = new wxBitmap(im);
|
||||
} else // 32-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
|
||||
uint8_t* dst = im.GetData();
|
||||
uint8_t* dst = im->GetData();
|
||||
|
||||
for (int y = 0; y < std::ceil(height * scale); y++) {
|
||||
for (int x = 0; x < std::ceil(width * scale); x++, src++) {
|
||||
|
@ -1951,18 +1956,23 @@ void BasicDrawingPanel::DrawArea(wxWindowDC& dc)
|
|||
|
||||
++src; // skip rhs border
|
||||
}
|
||||
|
||||
bm = new wxBitmap(im);
|
||||
}
|
||||
|
||||
DrawImage(dc, im);
|
||||
|
||||
delete im;
|
||||
}
|
||||
|
||||
void BasicDrawingPanel::DrawImage(wxWindowDC& dc, wxImage* im)
|
||||
{
|
||||
double sx, sy;
|
||||
int w, h;
|
||||
GetClientSize(&w, &h);
|
||||
sx = w / (width * scale);
|
||||
sy = h / (height * scale);
|
||||
dc.SetUserScale(sx, sy);
|
||||
dc.DrawBitmap(*bm, 0, 0);
|
||||
delete bm;
|
||||
wxBitmap bm(*im);
|
||||
dc.DrawBitmap(bm, 0, 0);
|
||||
}
|
||||
|
||||
#ifndef NO_OGL
|
||||
|
|
|
@ -416,10 +416,13 @@ enum ifbfunc {
|
|||
};
|
||||
|
||||
// make sure and keep this in sync with opts.cpp!
|
||||
enum renderer { RND_SIMPLE,
|
||||
enum renderer {
|
||||
RND_SIMPLE,
|
||||
RND_OPENGL,
|
||||
RND_CAIRO,
|
||||
RND_DIRECT3D };
|
||||
RND_DIRECT3D,
|
||||
RND_QUARTZ2D,
|
||||
};
|
||||
|
||||
// likewise
|
||||
enum audioapi { AUD_SDL,
|
||||
|
@ -635,7 +638,8 @@ public:
|
|||
virtual wxWindow* GetWindow() { return dynamic_cast<wxWindow*>(this); }
|
||||
virtual void Delete() { (dynamic_cast<wxWindow*>(this))->Destroy(); }
|
||||
|
||||
void PaintEv(wxPaintEvent& ev);
|
||||
virtual void PaintEv(wxPaintEvent& ev);
|
||||
virtual void EraseBackground(wxEraseEvent& ev);
|
||||
protected:
|
||||
virtual void DrawArea(wxWindowDC&) = 0;
|
||||
virtual void DrawOSD(wxWindowDC&);
|
||||
|
|
|
@ -35,6 +35,13 @@
|
|||
<flag>wxALL</flag>
|
||||
<border>5</border>
|
||||
</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="wxRadioButton" name="OutputOpenGL">
|
||||
<label>OpenGL</label>
|
||||
|
|
Loading…
Reference in New Issue