Widget Canvas "ID" for foreign handlers

LiveCode Builder is a language for extending LiveCode's capabilities, creating new object types as Widgets, and libraries that access lower-level APIs in OSes, applications, and DLLs.

Moderators: LCMark, LCfraser

Post Reply
n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 140
Joined: Mon Mar 12, 2007 12:06 pm

Widget Canvas "ID" for foreign handlers

Post by n.allan » Thu Oct 22, 2015 8:26 am

BTW I'm sorry to keep banging on about foreign handlers....but

I remember a while back that Kevin showed a video of a widget rendering pdfs using foreign handlers.

Is there an ID or context ID for each widget canvas you can pass to a foreign handler to "paint" on?

I am currently trialling a library to render dxf/dwg cad files. It's working after a fashion. I currently pass the windowid of the stack, get a device context for it and paint the dxf drawing. This paints the drawing directly to the client area of the window. Now, this is fine until we have other controls on the screen. Any other controls are wiped out by the foreign paint command. The drawing also dissapears occaisionally but that's a whole other problem.

If we could paint to the canvas of a widget it would help a lot with layering issues and I could paint the cad drawing during the OnPaint() handler as it is supposed to be done.

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1095
Joined: Thu Apr 11, 2013 11:27 am

Re: Widget Canvas "ID" for foreign handlers

Post by LCMark » Thu Oct 22, 2015 10:19 am

@n.allan: Unfortunately things are a little more complicated then that.

LiveCode does not paint directly into the window on any platform. This is because the underlying graphics capabilities of every platform is significantly less than LiveCode needs. Instead, libgraphics does all the heavy lifting of rasterizing into an offscreen buffer, which then gets composited into the window.

The PDF example Kevin demonstrated used the CoreGraphics calls to paint into a buffer then composited that into LiveCode's canvas.

Do you have a link to the library you are trying to use so I can take a look at its API - there are a couple of options here and knowing what the API can do will help me suggest a reasonable approach.

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 140
Joined: Mon Mar 12, 2007 12:06 pm

Re: Widget Canvas "ID" for foreign handlers

Post by n.allan » Thu Oct 22, 2015 3:27 pm

I rather think I'm barkng up the wrong tree here then. The dll is closed source. I do have the api help file if it's any use but there is only one function which paint the dwg file to the device context. Looking at the c++ samples etc...It looks like this should be called in the windows ON_PAINT handler. but since we don't have access to that I had to hack my way through. There is nothing else in the api to render to a context.

Here is the main function in the api:-

Code: Select all

The DrawCAD function draws the CADImage object on the specified device context.

Syntax:

int DrawCAD (
  HANDLE hObject,
  HDC hDC,
  LPRECT lprc
);

Parameters:
hObject 
 Identifies the CADImage object handle.

hDC
 Identifies the device context.
 
lprc
 Points to the RECT structure that contains logical coordinates of the drawing rectangle.
The HOBJECT parameter is created by the dll internally when you load a drawing. I can get it no problem with a foreign handler.
The LPRECT parameter is created by a seperate dll I created in code::blocks in order for LCB to handle structs. The LPRECT members are set using foreign handlers to my own dll. Again no probs here.
The HDC parameter is created by calling GetDC( the windowid of this stack ) using a foreign handler to the Windows API.

My hopes were that I could create a compatible HDC for a widget and pass that along with the rect of the widget in order for the dll to paint to it.

I went back the books again and found imagePixmapID and pixmapID. Hoping I could render to an image box but it looks like they are no longer supported. They always return 0.

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 140
Joined: Mon Mar 12, 2007 12:06 pm

Re: Widget Canvas "ID" for foreign handlers

Post by n.allan » Thu Oct 22, 2015 10:29 pm

OK I just had a look through libgraphics src on git, There is a whole mess of stuff in context.cpp that looks interesting ( but over my head unfortunately ).

I see references to skBitmap objects and context creation. I'm hoping there is some way to create a compatible device context to pass to this CAD library (or any library for that matter )

Do we have access to these skBitmap objects in LCB?

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1095
Joined: Thu Apr 11, 2013 11:27 am

Re: Widget Canvas "ID" for foreign handlers

Post by LCMark » Fri Oct 23, 2015 8:20 am

@n.allan: No you aren't banging up the wrong tree at all! The key thing here is that you need to create an HDC which is tied to a offscreen bitmap which you can get the pointer to the actually memory for the pixels. The engine has to do this on Windows to get the mask to render text, and to copy rendered controls into the Window.

Now, offscreen buffers in windows work by creating a 'bitmap object', then creating a 'compatible DC' for the bitmap, and then binding the bitmap to the DC. In pseudo-C:

Code: Select all

// Create a 'DIB Section' these are GDI bitmaps to which you can access the backing store directly.
// To to this you initialize a 'BITMAPINFO' structure, and use that to generate an HBITMAP, and
// a pointer to the pixels in memory.
HBITMAP t_rgb_bitmap;
void *t_rgb_data;

BITMAPINFO t_bitmapinfo;
memset(&t_bitmapinfo, 0, sizeof(BITMAPINFO));
t_bitmapinfo . bmiHeader . biSize = sizeof(BITMAPINFOHEADER);
t_bitmapinfo . bmiHeader . biCompression = BI_RGB;
t_bitmapinfo . bmiHeader . biWidth = p_bounds . width;
t_bitmapinfo . bmiHeader . biHeight = -p_bounds . height;
t_bitmapinfo . bmiHeader . biPlanes = 1;
t_bitmapinfo . bmiHeader . biBitCount = 32;
t_rgb_bitmap = CreateDIBSection(p_gdicontext, &t_bitmapinfo, DIB_RGB_COLORS, &t_rgb_data, NULL, 0);

// Create an offscreen DC
HDC t_offscreen_dc;
t_offscreen_dc = CreateCompatibleDC(NULL);

// Bind the bitmap into the DC
HDIOBJ t_old_bitmap;
t_old_bitmap = SelectObject(t_offscreen_dc, t_rgb_bitmap);

// Now paint into the bitmap
DrawCad(t_cad_image, t_offscreen_dc, &t_cad_rect);

// Unbind the bitmap from the DC
SelectObject(t_offscreen_dc, t_old_bitmap);

// At this point you have a GDI bitmap (t_rgb_bitmap) and a pointer to its backing store t_rgb_data
For getting this to work in LCB, I suggest you put a function like the following into your support dll (it could be done in pure LCB, but is a little fiddly at the moment!):

Code: Select all

bool Win32CreateOffscreenBitmap(int width, int height, HBITMAP *r_bitmap, void *r_bits)
{
  HBITMAP t_rgb_bitmap;
  t_rgb_bitmap = NULL;

  void *t_rgb_data;
  t_rgb_data = NULL;

  BITMAPINFO t_bitmapinfo;
  memset(&t_bitmapinfo, 0, sizeof(BITMAPINFO));
  t_bitmapinfo . bmiHeader . biSize = sizeof(BITMAPINFOHEADER);
  t_bitmapinfo . bmiHeader . biCompression = BI_RGB;
  t_bitmapinfo . bmiHeader . biWidth = p_bounds . width;
  t_bitmapinfo . bmiHeader . biHeight = -p_bounds . height;
  t_bitmapinfo . bmiHeader . biPlanes = 1;
  t_bitmapinfo . bmiHeader . biBitCount = 32;
  t_rgb_bitmap = CreateDIBSection(p_gdicontext, &t_bitmapinfo, DIB_RGB_COLORS, &t_rgb_data, NULL, 0);

  if (t_rgb_bitmap == NULL)
    return false;

  *r_bitmap = t_rgb_bitmap;
  *r_bits = t_rgb_data;
}
So, with this, you should be able to do something along these lines (the foreign handler bind declarations need to be filled in, and there's no error checking):

Code: Select all

foreign handler Win32CreateOffscreenBitmap(in pWidth as CInt, in pHeight as CInt, out rBitmap as Pointer, our rBits as Pointer) returns bool binds to ...

foreign handler CreateCompatibleDC(in pTarget as optional Pointer) returns Pointer binds to ...
foreign handler SelectObject(in pObject as optional Pointer) returns optional Pointer binds to ...
foreign handler DeleteDC(in pTarget as Pointer) returns nothing binds to ...
foreign handler DeleteObject(in pTarget as Pointer) returns nothing binds to ...

foreign handler MCDataCreate(in pBits as Pointer, in pSize as CUInt, out rData as Data) binds to "<builtin>"

handler RenderCADAsImage(...) returns Image
  variable tBitmap as Pointer
  variable tBits as Pointer
  Win32CreateOffscreenBitmap(<width>, <height>, tBitmap, tBits)

  variable tDC as Pointer
  put CreateCompatibleDC(nothing) into tDC

  variable tOldObject
  put SelectObject(tDC, tBitmap) into tOldObject

  DrawCAD(<cad object>, tDC, <rect>)

  SelectObject(tDC, tOldObject)

  DeleteDC(tDC)

  variable tImageData as Data
  MCDataCreateWithBytes(tBits, <width> * <height> * 4, tImageData)

  -- At this point we've copied the data, so don't need the bitmap anymore.
  DeleteObject(tBitmap)

  -- Create the image
  variable tImage as Image
  put image of size [ <width>, <height> ] with pixels tImageData into tImage

  return tImage
end handler
The RenderCADAsImage() has some places to be filled in (i.e. the CAD object, and width and height), but it should return a Canvas Image object which you can then use to render things in the normal way.

This is slightly inefficient at the moment as the bitmap data is copied a couple of times. However, we should be able to add something to the canvas at some point which allows you to 'lock' a rect of it and return a pointer to the bits, or as a appropriate system object for binding into a DC.

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 140
Joined: Mon Mar 12, 2007 12:06 pm

Re: Widget Canvas "ID" for foreign handlers

Post by n.allan » Sat Oct 24, 2015 10:34 pm

Muahhaha <attempts evil laugh>

I am definately getting something coming up in the widget. It's garbled so I might need to play with the ARGB order of the HBITMAP or it maybe a "rect" issue but still an awesome start!

@LCMark you have mad skils! I pretty much copied and pasted, fixed a couple compiler errors, tweaks and boom! What a genius you are!

I certainly was not expecting all that code and thanks yet again.

I did see a similar implementation to this in some of the Google chrome forums. It looks like they use skia for all the rendering too.

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 140
Joined: Mon Mar 12, 2007 12:06 pm

Re: Widget Canvas "ID" for foreign handlers

Post by n.allan » Wed Oct 28, 2015 9:28 am

I'm having trouble with this foreign handler from libfoundation:

Code: Select all

foreign handler MCDataCreateWithBytes(in pBits as Pointer, in pSize as CUint, out rData as Data) returns CBool binds to "<builtin>"
Is this correct? Every time I call it it, livecode just quits. I looked up the source for MCDataCreateWithBytes on git but I am not really sure what the parameters are supposed to be.

What is the based type base type of MCDataRef& is. Is it a void* ?

I am passing the following parameters:
pBits is a void* from mark's c function above, pSize is the height * width of image *4, rData is a variable tImageData as Data

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1095
Joined: Thu Apr 11, 2013 11:27 am

Re: Widget Canvas "ID" for foreign handlers

Post by LCMark » Tue Nov 03, 2015 10:42 am

@n.allan: Could you paste the code you are using the MCDataCreateWithBytes function with so I can see the context?

The handler declaration looks correct... The only thing that would cause it to crash (I think) is if the pBits parameter did not point to pSize bytes of data. MCDataRef's are pointers, and 'out' parameters map to MCDataRef& (i.e. a reference) in C++.

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 140
Joined: Mon Mar 12, 2007 12:06 pm

Re: Widget Canvas "ID" for foreign handlers

Post by n.allan » Thu Feb 11, 2016 10:28 pm

I could have sworn I replied to this thread?

My "version control" is non existent and as a result I have lost the code for the MCCreateDataWithBytes function along with the c dll I was using at the time.

Anyway I have been away for a while and on return I noticed the new browser widget has a "native layer" handler.

Does native layer return the HWND of the widget (on windows obviously) Could we now grab the DC of this and draw to it?

Post Reply

Return to “LiveCode Builder”