[Date Prev][Date Next][Thread Prev][Thread Next][Thread Index]

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;
}