[Date Prev][Date Next][Thread Prev][Thread Next][Thread Index]
Re: [XaraXtreme-dev] Cairo Port
- From: Luke Hart <lukeh@xxxxxxxx>
- Date: Tue, 08 May 2007 16:49:58 +0100
- Subject: Re: [XaraXtreme-dev] Cairo Port
Jonas Diemer wrote:
Alex Bligh schrieb:
Also, the code above must certainly be able to get at the image buffer
contents somehow in order to be able to actually display them. But I'm
not sure how that happens.
The image buffer is allocated (and, I believe, blanked) in the
application.
For extra bonus confusion value the bitmap bit itself needs to be in
windows DIB format. Given that it's (almost) exclusively used in 32 bit
mode these days, this is simply one 32-bit word per pixel, and row
alignment etc. aren't a problem, so we are merely talking byte
ordering and
"where T goes" considerations.
Well, I have added code to the GDraw_SetDIBitmap function, which I
think should be the place to set up a cairo surface to draw on.
AFAIK, a cairo_t context requires a surface to be created, so I create
the cairo_t here as well. Hence, a cairo_t context does not perfectly
map to the GDRAW context...
So far, I ignore calls with Bitmap==NULL, I don't know if that is the
expected behaviour though.
It looks like, cairo and gdraw use the same bitmap format. I could set
up a surface to draw on like this:
cairo_surface_t *surface = cairo_image_surface_create_for_data(
Bitmap, // This is the Bitmap pointer passed to
GDraw_SetDIBitmap
CAIRO_FORMAT_ARGB32,
BitmapInfo->biWidth,
BitmapInfo->biHeight,
4*BitmapInfo->biWidth /*stride is 4*width bytes*/ );
I have sucessfully drawn some shapes to the "stroke shape / pressure
profile" selector by just drawing something directly from
GDraw_SetDIBitmap. However, I have not yet been able to draw to the
main canvas (which shows the actual image)...
I know, I am moving forward in very little steps, since I am new to
all this :-)
Jonas
Jonas,
I've attached a couple of source files from a performance testing
application I wrote to compare CDraw and Cairo (amongst other vector
libraries). They should give you some idea about what calls to CDraw are
(roughly) equivalent to which Cairo calls.
The calls I used to setup the bitmap surface are cairo_create and
cairo_set_target_image. An other thing you should note is that CDraw
expects 16.16 fixed point numbers, whilst Cairo expects floating point
numbers. Although the test harness are obviously very simplistic (and
very limited in coverage), there should be some code that you can re-use.
Luke
// GdiPlusPerf.cpp: implementation of the CCDrawPerf class.
//
//////////////////////////////////////////////////////////////////////
#include "StdWx.h"
#include "CDrawPerf.h"
static CCDrawPerf g_CDrawPerf;
const int FX = 14 ;
CCDrawPerf::CCDrawPerf() :
SetMemoryHandlers(NULL),
SetDIBitmap(NULL),
SetMatrix(NULL),
SetAntialiasFlag(NULL),
BuildGraduationTable32(NULL),
SetGraduation(NULL),
SetColour(NULL),
StrokePath(NULL),
FillPath(NULL)
{
HMODULE hModule = ::LoadLibrary("CDraw.dll");
if(NULL == hModule)
return;
SetMemoryHandlers = PFNSetMemoryHandlers(::GetProcAddress(hModule, MAKEINTRESOURCE(32)));
SetDIBitmap = PFNSetDIBitmap(::GetProcAddress(hModule, MAKEINTRESOURCE(3)));
SetMatrix = PFNSetMatrix(::GetProcAddress(hModule, MAKEINTRESOURCE(12)));
SetAntialiasFlag = PFNSetAntialiasFlag(::GetProcAddress(hModule, MAKEINTRESOURCE(4)));
BuildGraduationTable32 = PFNBuildGraduationTable32(::GetProcAddress(hModule, MAKEINTRESOURCE(72)));
BuildTransparencyTable = PFNBuildTransparencyTable(::GetProcAddress(hModule, MAKEINTRESOURCE(48)));
SetGraduation = PFNSetGraduation(::GetProcAddress(hModule, MAKEINTRESOURCE(26)));
SetColour = PFNSetColour(::GetProcAddress(hModule, MAKEINTRESOURCE(23)));
SetTransparency = PFNSetTransparency(::GetProcAddress(hModule, MAKEINTRESOURCE(24)));
SetTransparentGraduation = PFNSetTransparentGraduation(::GetProcAddress(hModule, MAKEINTRESOURCE(47)));
StrokePath = PFNStrokePath(::GetProcAddress(hModule, MAKEINTRESOURCE(7)));
FillPath = PFNFillPath(::GetProcAddress(hModule, MAKEINTRESOURCE(6)));
// We won't add the plugin if the DLL isn't present!
g_PluginManager.AddPlugin(this);
}
wxString CCDrawPerf::GetHumanName()
{
return _T("CDraw");
}
static inline void ReportError(LONG lError, PCSTR pszFunction)
{
if(-1 == lError)
{
char pszTmp[256];
sprintf(pszTmp, "%s, Error = %d\n", pszFunction, GetLastError());
OutputDebugString(pszTmp);
}
}
static void * __cdecl MemoryAlloc(const ULONG Size)
{
return PBYTE(malloc(Size));
}
static void __cdecl MemoryFree(cpcVOID pData)
{
free((void *)pData);
}
bool CCDrawPerf::Initialise(CPluginManager::SRunConfig &Config)
{
m_pConfig = &Config;
if(NULL == SetMemoryHandlers)
{
::MessageBox(::GetActiveWindow(), "Unable to load 'CDraw.dll', please make sure dll is available and re-run application", "CDraw error", MB_OK);
return false;
}
const unsigned cbStride = m_pConfig->m_cX * 4;
const unsigned cbBitmap = cbStride * m_pConfig->m_cY;
ReportError(SetMemoryHandlers(MemoryAlloc, MemoryFree), "SetMemoryHandlers");
BITMAPINFOHEADER bi;
memset(&bi, 0, sizeof(bi));
bi.biSize = sizeof(bi);
bi.biWidth = m_pConfig->m_cX;
bi.biHeight = m_pConfig->m_cY;
bi.biBitCount = 32;
bi.biPlanes = 1;
bi.biCompression = 0x80000001; // BI_RGB;
// Create the bitmap and clear to white
m_pBits.reserve(cbBitmap);
memset(&m_pBits.front(), 0xFF, cbBitmap);
ReportError(SetDIBitmap(&bi, &m_pBits.front(), eFormat16BPP(0)), "SetDIBitmap");
// Reformat the points into the format we expect
CPluginManager::SPointVector::const_iterator iter(Config.m_vecPoints.begin()), end(Config.m_vecPoints.end());
m_vecBezier.clear();
m_vecType.clear();
bool fFirst = true;
for(; iter != end; ++iter)
{
POINT pt;
pt.x = LONG(iter->m_flX * 0x10000);
pt.y = LONG(iter->m_flY * 0x10000);
m_vecBezier.push_back(pt);
m_vecType.push_back(PT_BEZIERTO);
}
m_vecType[0] = PT_MOVETO;
// Setup the identity matrix
GMATRIX matrix;
matrix.A_X = 1 << FX;
matrix.A_Y = 0;
matrix.B_X = 0;
matrix.B_Y = -1 << FX;
matrix.C_X = __int64(0);
matrix.C_Y = __int64(Config.m_cY) << (16 + FX);
ReportError(SetMatrix(&matrix), "SetMatrix");
// Enable anti-alias if requested
ReportError(SetAntialiasFlag(Config.m_fAntialiase ? -1 : FALSE), "SetAntialiasFlag");
// Build grad tables
RGBT rgbBlack = {0x00, 0x00, 0x00, 0xFF};
RGBT rgbWhite = {0xFF, 0xFF, 0xFF, 0xFF};
m_GradTable.Length = 0x400;
ReportError(BuildGraduationTable32(rgbBlack, rgbWhite, 0, &m_GradTable),
"BuildGraduationTable32");
return true;
}
void CCDrawPerf::RenderBezier()
{
if(m_pConfig->m_fGradFill)
{
DWORD dwStyle = m_pConfig->m_fTransparent ? 0x7F0400 : 0x000;
POINT pt = {0, 0};
POINT ptBR = {m_pConfig->m_cX << FX, m_pConfig->m_cY << FX};
POINT ptNorm = {m_pConfig->m_cX << FX, -m_pConfig->m_cY << FX};
ReportError(SetGraduation(dwStyle, &m_GradTable, &pt, &ptBR, &ptNorm),
"SetGraduation");
}
else
if(m_pConfig->m_fTransparent)
{
RGBT rgb = {0x00, 0x00, 0x00, 0x7F};
ReportError(SetTransparency(rgb, 1), "SetTransparency");
}
else
{
RGB rgb = {0x00, 0x00, 0x00};
ReportError(SetColour(rgb), "SetColour");
}
if(m_pConfig->m_fOutline)
{
ReportError(StrokePath(&m_vecBezier[0], &m_vecType[0], m_vecBezier.size(),
FALSE, 4 << FX, CAP_BUTT, JOIN_MITRE, NULL), "StrokePath");
}
else
{
ReportError(FillPath(&m_vecBezier[0], &m_vecType[0], m_vecBezier.size(), WINDING_ALTERNATE),
"FillPath");
}
}
wxBitmap *CCDrawPerf::GetBitmap(wxWindow *pWindow)
{
// Create a new wxBitmap and embed our bimap handle
std::auto_ptr<wxBitmap> pBitmap(new wxBitmap(m_pConfig->m_cX, m_pConfig->m_cY, 24));
// Create a DIB and stuff this into the bitmap (for Windows only)
BITMAPINFO bi;
memset(&bi, 0, sizeof(bi));
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biWidth = m_pConfig->m_cX;
bi.bmiHeader.biHeight = m_pConfig->m_cY;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biCompression = BI_RGB;
HBITMAP hBitmap = CreateDIBitmap(GetDC(NULL), &bi.bmiHeader, CBM_INIT, &m_pBits.front(), &bi, 0);
pBitmap->SetHBITMAP(WXHBITMAP(hBitmap));
return pBitmap.release();
}
unsigned CCDrawPerf::GetRenderCaps()
{
return eAntialias | eOutline | eGradient | eTransparent;
}
// CairoPerf.cpp: implementation of the CCairoPerf class.
//
//////////////////////////////////////////////////////////////////////
#include "StdWx.h"
#include "CairoPerf.h"
static CCairoPerf g_CairoPerf;
CCairoPerf::CCairoPerf() : m_pCr(NULL), m_pPat(NULL)
{
g_PluginManager.AddPlugin(this);
}
CCairoPerf::~CCairoPerf()
{
Reset();
}
void CCairoPerf::Reset()
{
if(NULL != m_pCr)
{
cairo_destroy(m_pCr);
m_pCr = NULL;
}
if(NULL != m_pPat)
{
cairo_pattern_destroy(m_pPat);
m_pPat = NULL;
}
}
wxString CCairoPerf::GetHumanName()
{
return _T("Cairo");
}
bool CCairoPerf::Initialise(CPluginManager::SRunConfig &Config)
{
m_pConfig = &Config;
Reset();
m_pCr = cairo_create();
// Setup the bit buffer
m_pBits.resize(Config.m_cX * Config.m_cY * 4);
cairo_set_target_image(m_pCr, &m_pBits[0], CAIRO_FORMAT_RGB24, Config.m_cX, Config.m_cY,
Config.m_cX * 4);
// Clear to white
cairo_set_rgb_color(m_pCr, 1, 1, 1);
cairo_set_alpha(m_pCr, 1);
cairo_rectangle(m_pCr, 0, 0, Config.m_cX, Config.m_cY);
cairo_fill(m_pCr);
// Setup the gradient
double flAlpha = m_pConfig->m_fTransparent ? .5 : 1;
m_pPat = cairo_pattern_create_linear(0.0, 0.0, Config.m_cX, Config.m_cY);
cairo_pattern_add_color_stop(m_pPat, 0, 0, 0, 0, flAlpha);
cairo_pattern_add_color_stop(m_pPat, 1, 1, 1, 1, flAlpha);
return true;
}
void CCairoPerf::RenderBezier()
{
if(m_pConfig->m_vecPoints.empty())
return;
unsigned cPoint = m_pConfig->m_vecPoints.size();
const CPluginManager::SPoint *pPoint = &m_pConfig->m_vecPoints.front();
// Trace-out the polygon path
cairo_move_to(m_pCr, pPoint[0].m_flX, pPoint[0].m_flY);
for(unsigned ord = 1; ord < cPoint; ord += 3)
{
cairo_curve_to(m_pCr,
pPoint[ord].m_flX, pPoint[ord].m_flY,
pPoint[ord + 1].m_flX, pPoint[ord + 1].m_flY,
pPoint[ord + 2].m_flX, pPoint[ord + 2].m_flY);
}
// Setup colour and transparency
if(m_pConfig->m_fGradFill)
{
cairo_set_pattern(m_pCr, m_pPat);
}
else
{
cairo_set_rgb_color(m_pCr, 0, 0, 0);
if(m_pConfig->m_fTransparent)
cairo_set_alpha(m_pCr, .5);
else
cairo_set_alpha(m_pCr, 1);
}
// Finally do the fill or outline
if(m_pConfig->m_fOutline)
cairo_stroke(m_pCr);
else
cairo_fill(m_pCr);
}
wxBitmap *CCairoPerf::GetBitmap(wxWindow *pWindow)
{
#if defined(__WXMSW__)
// Create wxBitmap
std::auto_ptr<wxBitmap> pBitmap(new wxBitmap(m_pConfig->m_cX, m_pConfig->m_cY, 32));
// Create a DIB and stuff this into the bitmap (for Windows only)
BITMAPINFO bi;
memset(&bi, 0, sizeof(bi));
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biWidth = m_pConfig->m_cX;
bi.bmiHeader.biHeight = -m_pConfig->m_cY;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biCompression = BI_RGB;
HBITMAP hBitmap = CreateDIBitmap(GetDC(NULL), &bi.bmiHeader, CBM_INIT, &m_pBits[0], &bi, 0);
pBitmap->SetHBITMAP(WXHBITMAP(hBitmap));
#elif defined(__WXGTK__)
// Blat alpha
unsigned *pBits = (unsigned *)&m_pBits[0];
for(unsigned ord = 0; ord < m_pConfig->m_cX * m_pConfig->m_cY; ++ord, ++pBits)
{
*pBits |= 0xFF000000;
}
// Create wxBitmap
std::auto_ptr<wxBitmap> pBitmap(new wxBitmap(m_pConfig->m_cX, m_pConfig->m_cY, -1));
GdkPixbuf *pPixbuf = gdk_pixbuf_new_from_data(&m_pBits[0],
GDK_COLORSPACE_RGB,
TRUE, 8,
m_pConfig->m_cX, m_pConfig->m_cY,
m_pConfig->m_cX * 4,
NULL, NULL);
GdkPixmap *pPixmap = gdk_pixmap_new(pWindow->m_widget->window, m_pConfig->m_cX, m_pConfig->m_cY, -1);
gdk_draw_pixbuf(pPixmap, NULL, pPixbuf, 0, 0, 0, 0, -1, -1,
GDK_RGB_DITHER_MAX, 0, 0);
pBitmap->SetPixmap(pPixmap);
#endif
return pBitmap.release();
}
unsigned CCairoPerf::GetRenderCaps()
{
return eAntialias | eOutline | eGradient | eTransparent;
}