GeClipMap::Blit()
-
On 13/06/2013 at 09:02, xxxxxxxx wrote:
User Information:
Cinema 4D Version: 13.061
Platform: Windows ;
Language(s) : C++ ;---------
Hello out there,
I'm currently trying to build a 2D-image which is composed from several individual images. The use case is like in Photoshop: having a background image in the bottom layer, stacking additional images on top of that background by using certain levels of transparency and blit-modes.
Doing that in 3D-space by stacking Plane objects with textures in a certain z-order to get the same visual result is no option for me.
After checking the classes BaseBitmap and BaseDraw I found that GeClipMap exactly offers what I need (including easy text+clipping support). Especially the method GeClipMap::Blit() should offer me the possibility to subsequently draw images (and also fractions of them) on top of each other by using different blit-modes.
It seems to me, that Blit() doesn't work after I tried lots of parameter combinations, changing values, moving the Blit() calls from the beginning towards the end within the BeginDraw/EndDraw section. I also added a BeginDraw/EndDraw section for each involved GeClipMap and performed some dummy drawing actions on those images - no success. Has anyone made other - successful - experiences with Blit() ? I found no examples/hints in the SDK or in this forum+Google.
I'm using a couple of GeClipMaps, one acting as background image, the other ones (containing some red/green/blue dummy content, some pixels are fully transparent, others are fully opaque) are drawn on top of the background, followed by some test operations like drawing a line plus text. The result is finally drawn into the editor view by using BaseDraw::DrawTexture() (used a code example from ScottA which works pretty fine - thanks to ScottA).
My C++ code looks like this:In my Plugin class definition:
...
private:
GeClipMap* cmp1; // Pointer to the working GeClipMap where all comes together
GeClipMap* cmp_red; // Ptr to GeClipMap which will contain a red test image
GeClipMap* cmp_green; // Ptr to GeClipMap which will contain a green test image
GeClipMap* cmp_blue; // Ptr to GeClipMap which will contain a blue test imageIn my Plugin-Init() function: doing the Alloc + Init of the GeClipMaps
cmp1 = GeClipMap::Alloc();
cmp_red = GeClipMap::Alloc();
cmp_green = GeClipMap::Alloc();
cmp_blue = GeClipMap::Alloc();
res = cmp1->Init("d:\\Background_240x240.png",0, &ismovie);
res = cmp_red->Init("d:\\Red_100x100.png",0, &ismovie);
res = cmp_green->Init("d:\\Green_100x100.png",0, &ismovie);
res = cmp_blue->Init("d:\\Blue_100x100.png",0, &ismovie);In my Plugin-Draw() function:
cmp1->BeginDraw();
cmp1->SetDrawMode(GE_CM_DRAWMODE::GE_CM_DRAWMODE_BLEND, 128);
cmp1->Blit(10,10,(const GeClipMap&)cmp_red, 10,10, 80,80, GE_CM_BLIT::GE_CM_BLIT_COL);
cmp1->Blit(10,10,(const GeClipMap&)cmp_green, 10,10, 80,80, GE_CM_BLIT::GE_CM_BLIT_COPY);
cmp1->Blit(10,10,(const GeClipMap&)cmp_blue, 10,10, 80,80, GE_CM_BLIT::GE_CM_BLIT_FG);
cmp1->SetDrawMode(GE_CM_DRAWMODE::GE_CM_DRAWMODE_BLEND, 255);
cmp1->SetColor(178,240,0,255);
cmp1->Line(0,0,100,100);
cmp1->TextAt(30,30, "Hello");
cmp1->EndDraw();
... // setting up padr+cadr+vnadr+uvadr
bd->DrawTexture(cmp1->GetBitmap(), padr, cadr, vnadr, uvadr, 4, DRAW_ALPHA::DRAW_ALPHA_FROM_IMAGE, DRAW_TEXTUREFLAGS_0);Within my Plugin-Init() I check the return values of all GeClipMap::Init() calls - all return OK.
Within my Plugin-Draw() everything works fine (text+line appears). The final DrawTexture() call paints the content of my cmp1 GeClipMap as texture onto a plane in 3D space - looks fine, but the expected output from any of the 3 subsequent Blit() calls is missing. I used different blit modes for each call to test if maybe one of them brings something on the screen (i.e. first into cmp1).
Drawing the GeClipMaps via DrawTexture too (one after another) works, but that's not what I want because the BaseDraw class doesn't provide the same powerful blend features as GeClipMap does.
Can anyone tell me if my code is buggy or post some working code?
Many thanks in advance.
FrozenFritz -
On 13/06/2013 at 16:14, xxxxxxxx wrote:
I'm having a hard time figuring out exactly what the problem you're trying to solve.
You mentioned that you only see the background image and not the other images(red,green,blue)
Is this the problem you're trying to solve?This is what I get when I use a Blit() with a background image, three images with different colors, some Text, and a line.
I scaled the color layers so you can see each of them.
The result looks like I would expect. With each color on it's own stacked layer. Depending on the order they are written in the code.
And the BG is visible through all of them.
Are your results different than mine?
-ScottA
-
On 14/06/2013 at 01:05, xxxxxxxx wrote:
Hello ScottA,
these are very good news. So Blit() works in general.
Yes, your output looks different than mine. I would expect to see an output similar to yours.
I only see the BG-image (in my case a transparent image with content "Default 240x240") with a line and text ("Hello") on it. The red+green+blue images are missing.
I start a new try with rgb-images having no alpha channel, all pixels fully opaque (ordinary PNG created with MS Paint) and post my results.
FrozenFritz -
On 14/06/2013 at 05:10, xxxxxxxx wrote:
Hi,
good news now from my side: the mistake was in my code. I passed a pointer of the GeClipMap object to the Blit() function.
The correct line of code should look like this:
cmp1->Blit(10,10, *cmp_red , 10,10, 80,80, GE_CM_BLIT::GE_CM_BLIT_COL);
My cast operator was completely wrong, so playing around with cast operators should be done with care if one is not completely sure.
Giblet gave that hint to me in a parallel post that was initially created by WickedP (https://developers.maxon.net/forum/topic/7190/8229_bitmap-to-geclipmap). Many thanks again to Giblet from here and of course also to ScottA for spending time to investigate on my problem.
I will post the complete source code of my - with your help now working - Blit example to make this post to something useful for the forum.
FrozenFritz -
On 14/06/2013 at 07:08, xxxxxxxx wrote:
so here's the promised code (please add NULL-pointer checks when using that code in your projects) to create the following output:
#include "c4d.h"
#include "c4d_symbols.h"
#include "tgeclipmap.h"
#include "lib_description.h"
#include "lib_clipmap.h"
#include "customgui_priority.h"
class GeClipMapTag : public TagData
{
public:
virtual Bool Init(GeListNode *node);
virtual void Free(GeListNode *node);
virtual Bool Draw(BaseTag *tag, BaseObject *op, BaseDraw *bd, BaseDrawHelp *bh);
virtual EXECUTIONRESULT Execute(BaseTag *tag, BaseDocument *doc, BaseObject *op, BaseThread *bt, LONG priority, EXECUTIONFLAGS flags);
static NodeData *Alloc(void) { return gNew GeClipMapTag; }
virtual Bool Message(GeListNode *node, LONG type, void *data);
private:
GeClipMap* cmp1;
GeClipMap* cmp_red;
GeClipMap* cmp_green;
GeClipMap* cmp_blue;
};
Bool GeClipMapTag::Message(GeListNode *node, LONG type, void *data)
{
return TRUE;
}
Bool GeClipMapTag::Init(GeListNode *node)
{
IMAGERESULT res;
Bool ismovie=false;
cmp1 = GeClipMap::Alloc();
cmp_red = GeClipMap::Alloc();
cmp_green = GeClipMap::Alloc();
cmp_blue = GeClipMap::Alloc();
res = cmp1->Init("d:\\Surface_default_240x240_transparent.png",0, &ismovie);
GePrint("BG_Init = " + LongToString(res));
res = cmp_red->Init("d:\\Red_100x100_opaque.png",0, &ismovie);
GePrint("Red_Init = " + LongToString(res));
res = cmp_green->Init("d:\\Green_100x100_opaque.png",0, &ismovie);
GePrint("Green_Init = " + LongToString(res));
res = cmp_blue->Init("d:\\Blue_100x100_opaque.png",0, &ismovie);
GePrint("Blue_Init = " + LongToString(res));
return TRUE;
}
void GeClipMapTag::Free(GeListNode *node)
{
GeClipMap::Free(cmp1);
GeClipMap::Free(cmp_red);
GeClipMap::Free(cmp_green);
GeClipMap::Free(cmp_blue);
}
Bool GeClipMapTag::Draw(BaseTag *tag, BaseObject *op, BaseDraw *bd, BaseDrawHelp *bh)
{
cmp1->BeginDraw();
cmp1->SetDrawMode(GE_CM_DRAWMODE::GE_CM_DRAWMODE_BLEND, 200);
cmp1->Blit( 0, 0, *cmp_red, 0,0, 99,99, GE_CM_BLIT::GE_CM_BLIT_COPY);
cmp1->Blit(20,20, *cmp_green, 20,20, 80,80, GE_CM_BLIT::GE_CM_BLIT_COPY);
cmp1->Blit(40,40, *cmp_blue, 0,0, 99,99, GE_CM_BLIT::GE_CM_BLIT_COPY);
cmp1->SetDrawMode(GE_CM_DRAWMODE::GE_CM_DRAWMODE_BLEND, 255);
cmp1->SetColor(178,240,0,255);
cmp1->Line(0,0,100,100);
cmp1->TextAt(30,30, "Hello");
cmp1->EndDraw();
int xpos = 0;
int ypos = 0;
int width = 240;
int height = 240;
// Array with vertices for rectangular area
// lower left corner upper left corner upper right corner lower right corner
Vector padr[] = {(Vector(xpos,ypos,0)),(Vector(xpos,ypos+height,0)),(Vector(xpos+width,ypos+height,0)),(Vector(xpos+width,ypos,0))};
// Array with color vectors
Vector cadr[] = {(Vector(1,1,1)),(Vector(1,1,1)),(Vector(1,1,1)),(Vector(1,1,1))};
// Array with normals of vertices
Vector vnadr[] = {(Vector(0,0,1)),(Vector(0,0,1)),(Vector(0,0,1)),(Vector(0,0,1))};
// Array with texture UVs
Vector uvadr[] = {(Vector(0,1,0)),(Vector(0,0,0)),(Vector(1,0,0)),(Vector(1,1,0))};
bd->DrawTexture(cmp1->GetBitmap(), padr, cadr, vnadr, uvadr, 4, DRAW_ALPHA::DRAW_ALPHA_FROM_IMAGE, DRAW_TEXTUREFLAGS_0);
return TRUE;
}
EXECUTIONRESULT GeClipMapTag::Execute(BaseTag *tag, BaseDocument *doc, BaseObject *op, BaseThread *bt, LONG priority, EXECUTIONFLAGS flags)
{
return EXECUTIONRESULT_OK;
}
#define PID_GECLIPMAP_TAG 10001
Bool RegisterGeClipMapTagPlugin(void)
{
String name=GeLoadString(IDS_GECLIPMAP); if (!name.Content()) return TRUE;
return RegisterTagPlugin(PID_GECLIPMAP_TAG,name,TAG_EXPRESSION|TAG_VISIBLE,GeClipMapTag::Alloc,"Tgeclipmap",AutoBitmap("GeClipMapTag.tif"),0);
} -
On 14/06/2013 at 08:56, xxxxxxxx wrote:
Glad you figured it out.
Your code looks really similar to mine. Which makes sense. But there's one thing I have a problem with in mine. And that's freeing the memory.
In my code version. I can free cmp_red, cmp_green, cmp_blue using a class destructor. But I can't free cmp1 in that destructor. C4D crashes if I do that.I noticed that you are not using a destructor to free you allocations. But using this method for freeing them instead: void BlitTag::Free(GeListNode *node){}
I assume you are calling this method in the main.cpp file someplace?
Could you please post your main. cpp code so I can see how you're doing that?
I have been having a lot of problems with freeing bitmap memory. And this might be the solution I have been looking for.-ScottA
*Edit- Never Mind.
I see that tag plugins have there own "destructor" of sorts in the form the overloaded Free() method. Which don't need to be called. -
On 17/06/2013 at 00:04, xxxxxxxx wrote:
related to your "*Edit": yes exactly, you're right. The life-cycle of a tag is like this:
Tag gets created by attaching it to any object
- Constructor
- Init()
- many Draw(), Message() and Execute() calls caused by user interaction or what ever
User deletes the tag from the object where it was attached
- Free()
- ~Destructor
The Init() and Free() methods are foreseen for the plugin developer to do the ressource handling (e.g. alloc+free of memory). All these 4 methods get invoked automatically by Cinema.
One hint to my posted code:
I do the initialization of cmp1 (=loading of background image) within Init(). Within Draw() I do the "composition work", i.e. drawing new images (cmp_red/green/blue) on top of such background, so cmp1 gets modified in Daw() by the Blit() calls (plus line & text). The drawing result looks fine - as it should - after the first Draw() call. But when playing arround (changing scene cam by move/zoom/rotate), the image starts getting ugly. The reason is, that with each new Draw() the drawing operations are performed on the previous Draw() result again and again without a new initialization (loading background) of cmp1, so cmp1 gets kind of "saturated" more and more. The alternative is doing all that drawing within Init() and only keep the DrawTexture() call in Draw(), because once cmp1 was built there's no need to redraw it again and again in each Draw(). If it's necessary reacting on changes because either the content of cmp_red/green/blue or the text changes (content/color/position), then the drawing has to remain in Draw() but it should be enclosed by a statement like "if(anything_changed==true)".
FrozenFritz -
On 17/06/2013 at 08:50, xxxxxxxx wrote:
Originally posted by xxxxxxxx
User deletes the tag from the object where it was attached
- Free()
- ~DestructorWhen I use a ~Destructor() that destructor actually gets called while the plugin is still running. Not when the plugin is deleted.
So If I put my BaseBitmap::Free() stuff in a ~Destructor() I often can't free them properly. And C4D locks up.
If I put BaseBitmap::Free(cmp1) in a ~Destructor(). C4D locks up.
If I put BaseBitmap::Free(cmp1) in a Free() method. It works properly.
So Free() is doing something different, or being called at a different time, than a standard ~Destrustor().Bitmaps are very low level in the C++ SDK. So that makes them extremely dangerous memory leaking machines. They are so dangerous I think they should be documented in red in the docs.
As a beginner. Freeing Bitmap memory has caused me a lot of grief. More grief than any other thing in the SDK.
Because freeing them in a more complex scenario using arrays and the hyperfile system together is a horrible mess. And I can never manage to free the memory properly.-ScottA