[proposal] Attach DIB section buffer to gdImage

[email protected] (Mateusz Loskot)
Newsgroups php.gd.devel
Message-ID <[email protected]>
Hi,

Some (quite long) time ago, I was asking about how to use libgd
as GDI replacement and how to use it to draw on Windows device-context.
Here, I'd like to introduce simple single-function extension to libgd
that solves this task.

I created new function called gdImageTrueColorAttachBuffer.
This function does not use any elements specific to Windows
and perhaps it could be used on other purposes, not only under Windows.

The idea behind the gdImageTrueColorAttachBuffer is simple.
In Windows, CreateDIBSection allocates linear buffer for pixels
but libgd uses 2-dim buffer, so my function attaches chunks of the
linear buffer as separate rows to gdImage object.
It's also possible to control orientation of pixel rows, normal or
up-side-down by using negative value of 'stride' parameter.


Here is the gdImageTrueColorAttachBuffer function code:

//////////////////////////////////////////////////////////////////////
BGD_DECLARE(gdImagePtr)
gdImageTrueColorAttachBuffer(int* buffer, int sx, int sy, int stride)
{
  int i;
  int height;
  int* rowptr;
  gdImagePtr im;
  im = (gdImage *) gdMalloc (sizeof (gdImage));
  if (!im) {
    return 0;
  }
  memset (im, 0, sizeof (gdImage));

  if (overflow2(sizeof (int *), sy)) {
    return 0;
  }
  im->tpixels = (int **) gdMalloc (sizeof (int *) * sy);
  if (!im->tpixels) {
    free(im);
    return 0;
  }
  im->polyInts = 0;
  im->polyAllocated = 0;
  im->brush = 0;
  im->tile = 0;
  im->style = 0;

  height = sy;
  rowptr = buffer;
  if (stride < 0)
  {
	  int startoff = (height - 1) * stride;
	  rowptr = buffer - startoff;
  }

  i = 0;
  while (height--)
  {
	  im->tpixels[i] = rowptr;
	  rowptr += stride;
	  i++;
  }

  im->sx = sx;
  im->sy = sy;
  im->transparent = (-1);
  im->interlace = 0;
  im->trueColor = 1;
  im->saveAlphaFlag = 0;
  im->alphaBlendingFlag = 1;
  im->thick = 1;
  im->AA = 0;
  im->cx1 = 0;
  im->cy1 = 0;
  im->cx2 = im->sx - 1;
  im->cy2 = im->sy - 1;
  return im;
}
//////////////////////////////////////////////////////////////////////


Following is WM_PAINT message handler presenting
how to use the function:


////////////////////////////////////////////////////////////////////////////
/ WM_PAINT message handler

PAINTSTRUCT ps;
memset(&ps, 0, sizeof(ps));
HDC dc = ::BeginPaint(m_hWnd, &ps);

RECT rc;
GetClientRect(&rc);

int width = rc.right - rc.left;
int height = rc.bottom - rc.top;

// Configure bitmap properties
BITMAPINFO bmp_info;
ZeroMemory(&bmp_info, sizeof(BITMAPINFO));
bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmp_info.bmiHeader.biWidth = width;
bmp_info.bmiHeader.biHeight = height;
bmp_info.bmiHeader.biPlanes = 1;
bmp_info.bmiHeader.biBitCount = 32;
bmp_info.bmiHeader.biCompression = BI_RGB;
bmp_info.bmiHeader.biSizeImage = 0;
bmp_info.bmiHeader.biXPelsPerMeter = 0;
bmp_info.bmiHeader.biYPelsPerMeter = 0;
bmp_info.bmiHeader.biClrUsed = 0;
bmp_info.bmiHeader.biClrImportant = 0;

// Create memory device context
HDC mem_dc = ::CreateCompatibleDC(dc);

// bits points to a shared buffer of pixels
void* bits = NULL;
HBITMAP bmp = ::CreateDIBSection(mem_dc, &bmp_info, DIB_RGB_COLORS,
(void**)&bits, 0, 0);

// Selecting the object before doing anything allows you to use libgd
// together with native Windows GDI.
HBITMAP temp = (HBITMAP)::SelectObject(mem_dc, bmp);


// Start of GD drawing
int stride = ((width * 1 + 3) >> 2) << 2;

gdImagePtr im = { 0 };

// Attach shared buffer of pixels to GD image
// Negative stride places 0,0 in upper-left corner
im = gdImageTrueColorAttachBuffer((int*)bits, width, height, -stride);

int white = gdImageColorAllocate(im, 255, 255, 255);
int black = gdImageColorAllocate(im, 0, 0, 0);
int blue = gdImageColorAllocate(im, 0, 0, 255);
// Allocate the color red, 50% transparent.
int red = gdImageColorAllocateAlpha(im, 255, 0, 0, 64);

// Erase background with white color
gdImageFilledRectangle(im, 0, 0, width, height, white);

// Draw a dashed line from the upper left corner to the lower right corner.
gdImageFilledRectangle(im, 25, 25, 100, 100, blue);
gdImageChar(im, gdFontGetGiant(), 35, 35, 'Q', white);
gdImageFilledRectangle(im, 50, 50, 75, 175, red);
gdImageLine(im, 0, 0, 150, 150, black);

char* s = "Hello world!";
gdImageString(im, gdFontGetLarge(),
    im->sx / 2 - (strlen(s) * gdFontGetLarge()->w / 2),
    im->sy / 2 - gdFontGetLarge()->h / 2,
    (unsigned char*)s, black);


// Copy drawing from memory context (shared bitmap buffer) to screen DC.
::BitBlt(dc, rc.left, rc.top, width, height, mem_dc, 0, 0, SRCCOPY);

// Free resources
gdImageDetachBuffer(im);
::SelectObject(mem_dc, temp);
::DeleteObject(bmp);
::DeleteObject(mem_dc);
::EndPaint(m_hWnd, &ps);
////////////////////////////////////////////////////////////////////////////


I'd be thankful if anyone could review my idea and source code
and judge if such function is useful to be included in the libgd code.

I'm looking for your criticism and comments.


Cheers
-- 
Mateusz Loskot
http://mateusz.loskot.net
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.