Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Non-Custom UI Drawing

Engaged ,
Dec 02, 2012 Dec 02, 2012

Hi all.

My plugin requires certain things drawing to the comp, including circles, lines and (hopefully) text. However they are *not* part of a custom UI - in other words they can stay on-screen during a RAM preview, for example.

I've been looking into using Drawbot, but I'm not sure I can use it for this purpose, the reason being how do I get a drawing ref using suites.EffectCustomUISuite1()->PF_GetDrawingReference() without having a context (looks like the context is passed as part of a UI event, which I won't have)?

I've already written a bunch of code to draw pixels directly to the screen, which is working great so far, and I can go ahead and write functions for drawing shapes using this method; I was just wondering if I'm supposed to use Drawbot for non UI drawing, or if "rolling your own" is the way to go here.

Thanks,

Christian

TOPICS
SDK
4.1K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Community Expert , Jan 19, 2013 Jan 19, 2013

hey christian.

it's not too complicated. let's give it a go.

basically, AE doesn't know, nor does it care what how you fill the output buffer.

so it's mostly not an AE question, but rather an OS drawing tools question.

the only part that concerns the AE API is the part where you copy your result into the output buffer. (we'll get to that)since you can only use OS drawing tools on OS drawing contexts, that's exactly what we'll do.


during the render call, you start the process by creating a new drawing

...
Translate
Community Expert ,
Dec 03, 2012 Dec 03, 2012

hi christian.

your question is a bit fuzzy... i don't get if you're asking how to draw an interface even when AE is not asking for it,

or if you're asking how to use the system's drawing tools to draw into the rendered image.

so, what's drawbot really for?

it's only for graphic overlay of interfaces.

up to cs4, AE would hand you a copy of the platform specific graphic buffer of your target window, and you would draw on that buffer however you wanted. (usually using the OS API)

since cs5, you no longer have direct access to that graphic buffer, and you have no choice but to use drawbot to access it.

drawbot also offers some drawing tools so that you can fulfill most taks using solely it.

however, you can draw with whatever tools you want, and only copy the result to AE's graphic buffer using drawbot.

if youre trying to use these tools to render the output image of your plug-in, then drawbot is not your tool of choice, as it operates only on AE's internal interface graphics buffers.

i hope this answers your question.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Dec 03, 2012 Dec 03, 2012

Hi Shachar,

Thanks for responding.

I was asking about drawing directly into the effect's output image, not anything to do with custom UI. So you've answered my question - Drawbot is not the tool I need here. I've already started writing the code to draw the required shapes to the screen (just basic antialiased lines and shapes).

I was wondering how I'd handle doing what Drawbot does for pre-CS5 versions anyway (the guide is quite vague about this), so if I can get away with not using it, all the better!

Thanks for your help.

Christian

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 19, 2013 Jan 19, 2013

Hi all.

I'm bumping this up in the hope that I can find a solution to my problem.

To be clear, this is not a custom comp UI thing. I need to draw anti-aliased shapes, like lines & circles with various line thicknesses, and more importantly, text, into the effect output buffer. Drawing shapes is one thing I can probably do using the code I've already written, although it may not be the fastest way of doing things (or look the best!). What I'm completely in the dark about is how to render text. There's nothing in the SDK documentation about how to do this, with the exception of DrawBot (and we've already established cannot be used to draw into the output buffer).

I know it must be possible as there are standard AE effects that draw anti-aliased lines and shapes (Beam, Ellipse, Write-on) and older ones that render text (Basic Text). Question is, do these effects make use of OS drawing libraries like GDI+ and Quartz, and if so, how?

Thanks, Christian

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jan 19, 2013 Jan 19, 2013

hey christian.

it's not too complicated. let's give it a go.

basically, AE doesn't know, nor does it care what how you fill the output buffer.

so it's mostly not an AE question, but rather an OS drawing tools question.

the only part that concerns the AE API is the part where you copy your result into the output buffer. (we'll get to that)since you can only use OS drawing tools on OS drawing contexts, that's exactly what we'll do.


during the render call, you start the process by creating a new drawing bitmap context (i'll demonstrate on quartz. you'll get the general notion). you'll probably want to create it at the same size as the plug-in's output buffer. it's up to you.

allocate memory for the graphics context using the memory suite and use that memory handle with CGBitmapContextCreate(). (lookup some sample code. there's some periferial stuff to do there)

now that you have an os graphics buffer, you can start drawing.

CGContextShowTextAtPoint() will draw a string,

CGContextDrawPath() will draw a line, ect...

so.

now you have an invisible os graphics buffer filled with goodies you want to copy into the output buffer.

how do you do that?

well, remember that memory handle we mentioned earlier?

it now contains the filled buffer you just drew in. this is how you get the base address for the os graphics context.

osBufferBaseAddress = suites.HandleSuite1()->host_lock_handle(osBufferMemHandle);

take a look at the CCU sample (in the render function) to see how to access the output buffer directly in ram, and run through it.

just copy the value of each pixel from the context to the output buffer as the pixel pointer progresses. (it's all there in the CCU sample)

after the copying is done, rid of the context, free the memory, and you're done.

any questions?

no?

good.

🙂


Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 20, 2013 Jan 20, 2013

Thanks Shachar.

With a bit of help from Toby Fleischer I figured it out for myself yesterday, originally using the CImg library - which is essentially the same principle but with very limited drawing capabilities - and later with Quartz. I think I got too hung up on the whole "context" thing, which as it turns out is simply just a place to draw your stuff. Very straightforward as it turns out!

Thanks for putting the detiled response - useful for other newbies who want to do he same thing. I also was using "malloc" rather than the SDK HandleSuite, which I've now corrected thanks to your answer.

Christian

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Mar 10, 2013 Mar 10, 2013

I've got everything working fine with Quartz on Mac. It was easy and straightforward, pretty much like everything else on the Mac!

However on PC GDI+ is proving to be rather a difficult nut to crack. The Microsoft documentation isn't very good, at least in explaining how to create a drawing context on anything that isn't a window created in a C++ program - therein lies my problem - how do I get a HWND, which is required (as far as I know) to create my HDC (which I assume is my drawing context)?

From the GDI+ documentation, you first start up GDI+ as follows:

GdiplusStartupInput gdiplusStartupInput;

ULONG_PTR           gdiplusToken;

// Initialize GDI+.

GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

OK. Then, in order to create the HDC:

HDC          hdc;

PAINTSTRUCT  ps;

  

hdc = BeginPaint(hWnd, &ps);

So I figure that hWnd is a handle. I have a handle to the memory I've allocated for the bitmap, so I figure (and it's a long shot) that I can cast the memory handle as a HWND and use that. It executes OK - and by that I mean it doesn't crash - but the BeginPaint() function returns NULL, so that can't be right!

On another thread in this forum, the only other mention of GDI+ uses the following code:

HDC hdc;

PF_GET_CGRAF_DATA((*(event_extraP->contextH))->cgrafptr, PF_CGrafData_HDC, reinterpret_cast<void**>(&hdc));

Gdiplus::Graphics g(hdc);

But this is clearly(?) from a custom UI, hence the event_extraP pointer, and my project has no knowledge of the PF_GET_CGRAF_DATA callback.

If I can create the context then the rest is probably pretty easy to continue with. Any help gratefully received!

Thanks, Christian

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Mar 10, 2013 Mar 10, 2013

Update!

This code:

    HDC hdc = GetDC(NULL);

     HBITMAP bmp = CreateCompatibleBitmap(hdc, ae_worldP->width, ae_worldP->height);

kind of works. The only thing is, the correctly drawn lines, etc. are drawn in the top-left of the computer screen, over the AE menu, project window, etc! I didn't think that was possible!

So I'm on the right track I think - just need to not draw directly to the screen but to an offscreen bitmap I can then copy to my output world.

Thanks, C

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Mar 12, 2013 Mar 12, 2013

So I've been trying various things to get this to work, unfortunately to no avail. Here's my latest code that kicks things off:

HDC hdc = CreateCompatibleDC(NULL);

// bitmap_dataP is my allocated memory for width*height*4 bytes

HBITMAP bmp_temp = CreateBitmap(ae_worldP->width, ae_worldP->height, 1, 32, bitmap_dataP);

HBITMAP canvas = reinterpret_cast<HBITMAP>(SelectObject(hdc, bmp_temp));

The code compiles and runs, but I get nothing but the odd bit of noise when I copy the bitmap_dataP into my AE output. I must be close to the right solution, right?

Any ideas?

Thanks, Christian

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Mar 16, 2013 Mar 16, 2013

Never mind - I've got it working.

The whole HWND and HDC things were a red herring - you don't need them, instead you just create a bitmap as I did above then use that to create a new Graphics object, which is the equivalent of the Context in Quartz.

Christian

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 16, 2013 Mar 16, 2013

good job!

and thanks for reporting back for future generations.

(i'm serious. not everyone does that)

🙂

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jun 28, 2013 Jun 28, 2013

This was extremely useful thread for me. I managed to get hold of the Windows GDI, and the only problem I have is that I can't figure out how to obtain the pointer to the bitmap buffer to do the copying.

If I do it like this:

HDC hdc = CreateCompatibleDC ( NULL );

HBITMAP bitmap = CreateBitmap (width, height, 1, 32, NULL);

HBITMAP canvas = reinterpret_cast<HBITMAP>(SelectObject ( hdc , bitmap ) );

/* drawing code here

....

*/

void * osBufferBaseAddress = suites.HandleSuite1()->host_lock_handle( (PF_Handle) canvas );

I get a crash, and even then I could not iterate through void* type. On the other hand, if I don't cast canvas into PF_Handle, I can't get the host_lock_handle to work.

I would be grateful for ideas how to solve this issue.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Aug 21, 2013 Aug 21, 2013
LATEST

Sorry for the late reply here.

You don't need to obtain a pointer to a HDC (I think that is what causes the crash). Instead you create a blank bitmap and from that you create the canvas. Here's a heavily abridged version of my code (I use a structure to store the persistent data, with separate functions to setup, draw and copy back)

A_long   bitmap_bytes_per_rowL   = width * 4,

              bitmap_byte_countL      = bitmap_bytes_per_rowL * height;     // width and height are the width and height of your canvas

   

// Assign enough memory for the bitmap where we'll draw our stuff

PF_Handle memH = suites.HandleSuite1()->host_new_handle(bitmap_byte_countL);

void *bitmap_dataP = suites.HandleSuite1()->host_lock_handle(drawing_dataP->memH);

// Set up GDI+ drawing space

Bitmap *tempImg = new Bitmap(width, height, bitmap_bytes_per_rowL, PixelFormat32bppARGB,

                                                    reinterpret_cast<BYTE *>(bitmap_dataP));

Graphics *canvas = Graphics::FromImage(tempImg);

// Use drawing functions on canvas, e.g.

Pen pen(Color(c8.alpha, c8.blue, c8.green, c8.red), static_cast<REAL>(line_widthF));

canvas->DrawLine(&pen, static_cast<REAL>(x0F), static_cast<REAL>(y0F), static_cast<REAL>(x1F), static_cast<REAL>(y1F));

// Release the UI specific drawing space

delete canvas;

// Copy memory back to AE world (in variable ae_worldP)

A_u_char * final_bitmapP = reinterpret_cast<A_u_char *>(bitmap_dataP);

A_long cur_pxL = 0;

PF_Pixel8       px, *px_outP;

for(register A_long yL=0; yL<height; yL++) {

    for(register A_long xL=0; xL<width; xL++) {

         cur_pxL = 4 * ((width * yL) + xL);

         px.red =  final_bitmapP[cur_pxL];

         px.green =  final_bitmapP[cur_pxL + 1];

         px.blue =  final_bitmapP[cur_pxL + 2];

         px.alpha =  final_bitmapP[cur_pxL + 3];

               

         px_outP = (PF_Pixel8*)((char*)ae_worldP->data + (yL * ae_worldP->rowbytes) + (xL * sizeof(PF_Pixel8)));

         *px_outP = px;

    }

}

// Release memory handle

suites.HandleSuite1()->host_dispose_handle(memH);

Hope that helps.

Christian

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines