Blit to MultipassBitmap GeClipMap not working



  • In continuation to the MultipassBitmap problem I had with R16, it now seems I might have related issues with R20.

    Following is the full implementation of a dummy plugin to reproduce the issue.

    It's a Command plugin, when executed it will create a single layer bitmap, and a multi layer one. Both will be displayed into the Picture Viewer.
    Both bitmaps will have a background, on top of that a red diagonal cross, on top of that a green circle at the center, on top of that a blue rectangle in the top left quarter of the bitmap. And finally, on top of that a bitmap (the plugin's icon) being blitted in the top left corner.
    The single layer bitmap will have all drawing in a single "layer", while the multi layer bitmap does have each different drawing context in a separate layer.

    The single layer bitmap is drawn without any issue, but the multi layer one has issues when blitting.
    I get a "Cinema 4D has triggered a breakpoint" when performing the Blit() for the MultipassBitmap ... and resulting bitmap has no blitted bitmap. Still, the same method is used to blit on regular BaseBitmap, and this works as expected and does not report any problem.

    I can only assume I am doing something wrong, but I cannot figure out what exactly.

    // ========================
    // Cinema 4D C++ plugin
    //
    // PluginName: Test
    // Dummy "empty" plugin
    // ========================
    
    // Main.cpp
    
    #include "c4d.h"
    #include "lib_clipmap.h"
    
    // Dummy IDs - for demonstration purposes only
    #define MYCOMMAND_PLUGIN_ID	1000000
    
    // ====================================
    // CommandData
    // ====================================
    
    class MyCommand : public CommandData
    {
    	INSTANCEOF(MyCommand, CommandData)
    
    public:
    	virtual Bool Execute(BaseDocument* doc);
    
    	// Custom methods
    
    protected:
    	void DrawBackground(GeClipMap* clipmap);
    	void DrawCross(GeClipMap* clipmap);
    	void DrawCircle(GeClipMap* clipmap);
    	void DrawRectangle(GeClipMap* clipmap);
    	void DrawBitmap(GeClipMap* clipmap);
    
    	Bool WriteSingleLayer();
    	Bool WriteMultiLayer();
    };
    
    Bool MyCommand::Execute(BaseDocument* doc)
    {
    	WriteSingleLayer();
    	WriteMultiLayer();
    
    	return TRUE;
    }
    
    
    void MyCommand::DrawBackground(GeClipMap* clipmap)
    {
    	Vector color(128);
    	clipmap->SetColor(SAFEINT32(color.x), SAFEINT32(color.y), SAFEINT32(color.z), 255);
    	clipmap->FillRect(0, 0, clipmap->GetBw() - 1, clipmap->GetBh() - 1);
    }
    
    void MyCommand::DrawCross(GeClipMap* clipmap)
    {
    	Vector color(255,0,0);
    	clipmap->SetColor(SAFEINT32(color.x), SAFEINT32(color.y), SAFEINT32(color.z), 255);
    	clipmap->Line(0, 0, clipmap->GetBw() - 1, clipmap->GetBh() - 1);
    	clipmap->Line(0, clipmap->GetBh() - 1, clipmap->GetBw() - 1, 0);
    }
    
    void MyCommand::DrawCircle(GeClipMap* clipmap)
    {
    	Vector color(0,255,0);
    	clipmap->SetColor(SAFEINT32(color.x), SAFEINT32(color.y), SAFEINT32(color.z), 255);
    	clipmap->FillEllipse(100, 100, clipmap->GetBw() - 101, clipmap->GetBh() - 101);
    }
    
    void MyCommand::DrawRectangle(GeClipMap* clipmap)
    {
    	Vector color(0,0,255);
    	clipmap->SetColor(SAFEINT32(color.x), SAFEINT32(color.y), SAFEINT32(color.z), 255);
    	clipmap->FillRect(0, 0, clipmap->GetBw() / 2, clipmap->GetBh() / 2);
    }
    
    void MyCommand::DrawBitmap(GeClipMap* clipmap)
    {
    	AutoBitmap bitmap("icon.png"_s);
    	if (bitmap)
    	{
    		// use a clipmap for drawing/blitting the bitmap
    		AutoAlloc<GeClipMap> bitmapClipmap;
    		if (!bitmapClipmap)
    			return;
    		if (bitmapClipmap->Init(bitmap) != IMAGERESULT::OK)
    			return;
    
    		clipmap->Blit(0, 0, *bitmapClipmap, 0, 0, bitmapClipmap->GetBw() - 1, bitmapClipmap->GetBh() - 1, GE_CM_BLIT::COPY);
    	}
    }
    
    
    Bool MyCommand::WriteSingleLayer()
    {
    	AutoAlloc<BaseBitmap> outputBitmap;
    	if (!outputBitmap)
    		return false;
    	const Int32 bitdepth = 32;
    	const Int32 bitmapSize = 512;
    	IMAGERESULT res = outputBitmap->Init(bitmapSize, bitmapSize, bitdepth);
    	if (res != IMAGERESULT::OK)
    		return false;
    
    	AutoAlloc<GeClipMap> clipMap;
    	if (clipMap)
    	{
    		res = clipMap->Init(outputBitmap);
    		if (res != IMAGERESULT::OK)
    			return false;
    
    		clipMap->BeginDraw();
    
    		DrawBackground(clipMap);
    		DrawCross(clipMap);
    		DrawCircle(clipMap);
    		DrawRectangle(clipMap);
    		DrawBitmap(clipMap);
    
    		clipMap->EndDraw();
    	}
    
    	// open the resulting bitmap into the Picture Viewer
    	ShowBitmap(outputBitmap);
    
    	return true;
    }
    
    Bool MyCommand::WriteMultiLayer()
    {
    	const Int32 bitdepth = 32;
    	const Int32 bitmapSize = 512;
    	const COLORMODE colorMode = COLORMODE::ARGB;
    	MultipassBitmap* multiBitmap = MultipassBitmap::Alloc(bitmapSize, bitmapSize, colorMode);
    	if (!multiBitmap)
    		return false;
    
    	// get the first layer which is automatically created at allocation time
    	MultipassBitmap* layer = multiBitmap->GetLayerNum(0);
    	if (!layer)
    	{
    		MultipassBitmap::Free(multiBitmap);
    		return false;
    	}
    	layer->SetParameter(MPBTYPE::NAME, "Background");
    	layer->SetParameter(MPBTYPE::SAVE, true);
    	layer->SetParameter(MPBTYPE::SHOW, true);
    	// draw onto this layer via a ClipMap
    	AutoAlloc<GeClipMap> clipMap;
    	if (clipMap && (clipMap->Init(layer) == IMAGERESULT::OK))
    	{
    		clipMap->BeginDraw();
    
    		DrawBackground(clipMap);
    
    		clipMap->EndDraw();
    	}
    
    	// reuse the clipMap used for the background layer (reinit for each layer)
    
    	MultipassBitmap* crossLayer = multiBitmap->AddLayer(nullptr, colorMode);
    	if (crossLayer)
    	{
    		crossLayer->SetParameter(MPBTYPE::NAME, "Cross"_s);
    		crossLayer->SetParameter(MPBTYPE::SAVE, true);
    		crossLayer->SetParameter(MPBTYPE::SHOW, true);
    		//crossLayer->SetParameter(MPBTYPE::BLENDMODE, LAYER_NORMAL);
    
    		if (clipMap->Init(crossLayer) == IMAGERESULT::OK)
    		{
    			clipMap->BeginDraw();
    
    			DrawCross(clipMap);
    
    			clipMap->EndDraw();
    		}
    	}
    
    	MultipassBitmap* circleLayer = multiBitmap->AddLayer(nullptr, colorMode);
    	if (circleLayer)
    	{
    		circleLayer->SetParameter(MPBTYPE::NAME, "Circle"_s);
    		circleLayer->SetParameter(MPBTYPE::SAVE, true);
    		circleLayer->SetParameter(MPBTYPE::SHOW, true);
    		//circleLayer->SetParameter(MPBTYPE::BLENDMODE, LAYER_NORMAL);
    
    		if (clipMap->Init(circleLayer) == IMAGERESULT::OK)
    		{
    			clipMap->BeginDraw();
    
    			DrawCircle(clipMap);
    
    			clipMap->EndDraw();
    		}
    	}
    
    	MultipassBitmap* rectangleLayer = multiBitmap->AddLayer(nullptr, colorMode);
    	if (rectangleLayer)
    	{
    		rectangleLayer->SetParameter(MPBTYPE::NAME, "Rectangle"_s);
    		rectangleLayer->SetParameter(MPBTYPE::SAVE, true);
    		rectangleLayer->SetParameter(MPBTYPE::SHOW, true);
    		//rectangleLayer->SetParameter(MPBTYPE::BLENDMODE, LAYER_NORMAL);
    
    		if (clipMap->Init(rectangleLayer) == IMAGERESULT::OK)
    		{
    			clipMap->BeginDraw();
    
    			DrawRectangle(clipMap);
    
    			clipMap->EndDraw();
    		}
    	}
    
    	MultipassBitmap* bitmapLayer = multiBitmap->AddLayer(nullptr, colorMode);
    	if (bitmapLayer)
    	{
    		bitmapLayer->SetParameter(MPBTYPE::NAME, "Bitmap"_s);
    		bitmapLayer->SetParameter(MPBTYPE::SAVE, true);
    		bitmapLayer->SetParameter(MPBTYPE::SHOW, true);
    		//bitmapLayer->SetParameter(MPBTYPE::BLENDMODE, LAYER_NORMAL);
    
    		if (clipMap->Init(bitmapLayer) == IMAGERESULT::OK)
    		{
    			clipMap->BeginDraw();
    
    			DrawBitmap(clipMap);	// <--- performing the blit function reports a "Cinema 4D has triggered a breakpoint"
    
    			clipMap->EndDraw();
    		}
    	}
    
    	// open the resulting bitmap into the Picture Viewer
    	ShowBitmap(multiBitmap);
    
    	MultipassBitmap::Free(multiBitmap);
    	return true;
    }
    
    
    
    
    
    Bool RegisterMyCommand(void)
    {
    	return RegisterCommandPlugin(MYCOMMAND_PLUGIN_ID, "Test"_s, 0, AutoBitmap("icon.png"_s), "Test"_s, NewObjClear(MyCommand));
    }
    
    // ====================================
    // Plugin Main 
    // ====================================
    Bool PluginStart(void)
    {
    	ApplicationOutput("Test"_s);
    	RegisterMyCommand();
    
    	return TRUE;
    }
    void PluginEnd(void)
    {
    }
    Bool PluginMessage(Int32 id, void * data)
    {
    	switch (id) {
    	case C4DPL_INIT_SYS:
    		if (!g_resource.Init())
    			return FALSE;
    		return TRUE;
    	case C4DMSG_PRIORITY:
    		return TRUE;
    	case C4DPL_BUILDMENU:
    		break;
    	case C4DPL_ENDACTIVITY:
    		return TRUE;
    	}
    	return FALSE;
    }
    
    
    


  • Hi Daniel, thanks for reaching out us.

    Thanks a lot for the testing plugin but, unfortunately, even using your code I haven’t been able to reproduce it.
    Compiling your plugin with both our testing revision as well as with the official one I didn’t experienced Cinema triggering the breakpoint.

    I’ve tested on Cinema r20.057, Xcode 9.3 and High Sierra 10.13.6.

    Can you share some detail on your dev. environment? This might help me to match exact your configuration and get luckier in reproducing the issue.

    Best, Riccardo



  • Hi Daniel,
    I just want to rectify my previous post and I confirm that I’m able to reproduce the issue on Windows 10 and Visual Studio 2015.

    I’ll dig down further to see what is causing the different behavior between Mac and Windows and I let you know asap.

    Best, Riccardo



  • @r_gigante
    Sorry I didn't post any platform related information, or even added a windows tag. As I thought this wasn't a platform dependent issue, I did omit any (so I thought) irrelevant tags.

    Just to add, I still am on R20.030 ... for no particular reason. Will need to update to the latest version when I find the time.



  • @r_gigante
    I can confirm no problems exist on macOS, the output contains the blitted bitmap, as expected.
    I thus assume the provided implementation is OK, and should work on Windows ... in theory.
    Looking forward to you conclusion.



  • Hi Daniel,

    I filed a bug report with regard to this unexpected behavior. I will keep you updated on resolution.

    Best, Riccardo



  • I was doing a search through the forum related to AutoBitmap and came along this topic.
    What's the current status regarding R20.059, was it fixed in the latest update?
    And what about R21?



  • Hi Daniel, thanks for following up.

    Honestly, and I would say with huge surprise, I can't reproduce anymore the issue at this time. I've tried rerunning your code on:

    • R20.011
    • R20.030
    • R20.059
    • R21.022
      and on none of these I was able to hit the break point on Windows 10.

    Can you kindly check again and see if it's still the case on your side? I would really like to dive more and understand what's happened in the meantime.

    Cheers, R



  • @r_gigante
    I will try to check again, later today.
    However, I noticed you mentioned -early in the thread- you could reproduce it with Visual Studio 2015.
    Might it be the case you are currently using 2017 instead, and therefore don't encounter the issue anymore?
    For R20 I am still developing with 2015 on my laptop (windows 8.1) and use another machine (windows 10) for R21 with Visual Studio 2017.
    I will try to check on both development environments.



  • @C4DS I've used VS2015 as well to run the tests.

    Cheers



  • @r_gigante

    Tested on R20.057 with VS2015 on Windows 8.1 -> still get the exception, and no output
    Tested on R21.026 with VS2017 on Windows 10 -> same exception, and no output.



  • Hi Daniel, I should strongly apologize here because due to an overlook of mine I forget to copy the test bitmap in the resource folder resulting in the code not to be executed and the breakpoint not to be hit.

    Sorry about that.

    I've looked more in detail and in order to get it done you have to actually set the draw mode to GE_CM_DRAWMODE::BLEND by calling

    		// use a clipmap for drawing/blitting the bitmap
    		AutoAlloc<GeClipMap> bitmapClipmap;
    		if (!bitmapClipmap)
    			return;
    		if (bitmapClipmap->Init(bitmap) != IMAGERESULT::OK)
    			return;
    		clipmap->SetDrawMode(GE_CM_DRAWMODE::BLEND, GE_CM_SRC_MAX_OPACITY);
    		clipmap->Blit(0, 0, *bitmapClipmap, 0, 0, bitmapClipmap->GetBw() - 1, bitmapClipmap->GetBh() - 1, GE_CM_BLIT::COPY);
    
    

    This will ensure that upon calling the GE_CM_BLIT::COPY the GeClipMap->Blit() function using GE_CM_BLIT::COPY works properly.

    Unfortunately this doesn't completely solve the issue, because it seems that initing a GeClipMap with a MultipassBitmap created via AddLayer has some issue I still need to investigate.

    I'll get back on Monday, hopefully, with more details.

    Again sorry for the confusion and have a nice weekend!



  • @r_gigante

    Without the line

    clipmap->SetDrawMode(GE_CM_DRAWMODE::BLEND, GE_CM_SRC_MAX_OPACITY);
    

    I could continue after the "Cinema 4D has triggered a breakpoint" messagebox, now with that extra line I get a plain access violation. (tried on R20 with VS2015)



  • Exactly, that's what I meant by

    initing a GeClipMap with a MultipassBitmap created via AddLayer has some issue"

    Without clipmap->SetDrawMode(GE_CM_DRAWMODE::BLEND, GE_CM_SRC_MAX_OPACITY); you hit a default case in a switch statement which fires a critical stop whilst using it you actually try to blit to the GeClipMap but now you end up in a memory issue.

    I'll get back to you later next week
    Cheers, R


Log in to reply