SOLVED BaseDraw::DrawTexture alpha issue

I am trying to draw onto the viewport with some alpha values but don't get the expected value.
To test out I simply draw a white rectangle as background and on top of that I draw a black line with a varying alpha value. Basically building a gradient going from full transparency (top) to full opaque (bottom).

To compare to what would be expected I've drawn a Photoshop gradient at the right.
To me it seems the first N lines are not drawn at all, or their alpha value prevent the lines from being visible, while the rest of the "gradient" doesn't seem to be linear.

Main code of the "gradient"

for (Int32 i = 0; i <= 255; ++i)
{
	clipMap->SetColor(0, 0, 0, i);
	clipMap->Line(10, 10+i, width-10, 10+i);
}

This is the result with R20, but similar results obtained with R21. Have not tested more recent releases.

Alpha Gradient.png

Am I missing something about the "gradient" implementation, or is there another setting missing?

Here's the full plugin implementation:

// ========================
// 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 MYSCENEHOOK_PLUGIN_ID	1000000




Bool DrawTheStuff(BaseDraw *bd, Int32 x, Int32 y)
{
	AutoAlloc<GeClipMap> clipMap;
	if (!clipMap) return false;

	Int32 width = 300;
	Int32 height = 275;

	// now the dimensions are known
	// intialize clipmap for the actual drawing
	if (clipMap->Init(width, height, 32) != IMAGERESULT::OK)
		return false;
	clipMap->BeginDraw();
	// fill background with white
	clipMap->SetColor(255, 255, 255, 255);
	clipMap->FillRect(0, 0, width, height);

	// draw black stripes over background with different alpha values,
	// leaving a border of 10 pixels of background at each side
	for (Int32 i = 0; i <= 255; ++i)
	{
		clipMap->SetColor(0, 0, 0, i);
		clipMap->Line(10, 10+i, width-10, 10+i);
	}

	clipMap->EndDraw();
	const BaseBitmap* bitmap = clipMap->GetBitmap();
	if (bitmap)
	{
		maxon::Vector *positions;
		iferr(positions = NewMem(maxon::Vector, 4))
			return false;
		maxon::Vector *colors = nullptr; // NewMem(maxon::Vector, 4);
		maxon::Vector *normals = nullptr; // NewMem(maxon::Vector, 4);
		maxon::Vector *uvcoords;
		iferr(uvcoords = NewMem(maxon::Vector, 4))
		{
			DeleteMem(positions);
			return false;
		}

		// center image on top of the point
		x -= (width / 2);
		y -= (height / 2);

		positions[0] = maxon::Vector(x, y, 0);
		positions[1] = maxon::Vector(x + width, y, 0);
		positions[2] = maxon::Vector(x + width, y + height, 0);
		positions[3] = maxon::Vector(x, y + height, 0);

		uvcoords[0] = maxon::Vector(0, 0, 0);
		uvcoords[1] = maxon::Vector(1, 0, 0);
		uvcoords[2] = maxon::Vector(1, 1, 0);
		uvcoords[3] = maxon::Vector(0, 1, 0);

		// draw texture in screen space
		const Matrix currentMg = bd->GetMg();
		bd->SetMatrix_Screen();
		bd->SetLightList(BDRAW_SETLIGHTLIST_NOLIGHTS);
		const DRAW_ALPHA        alpha = DRAW_ALPHA::NORMAL; //DRAW_ALPHA::NONE
#if API_VERSION < 23000
		const DRAW_TEXTUREFLAGS texture = DRAW_TEXTUREFLAGS::NONE;
#else
		const DRAW_TEXTUREFLAGS texture = DRAW_TEXTUREFLAGS::INTERPOLATION_NEAREST | DRAW_TEXTUREFLAGS::TEMPORARY;
#endif
		bd->DrawTexture(bitmap, positions, colors, normals, uvcoords, 4, alpha, texture);

		DeleteMem(positions);
		//DeleteMem(colors);
		//DeleteMem(normals);
		DeleteMem(uvcoords);

		// restore the matrix
		bd->SetMatrix_Matrix(nullptr, currentMg);
	}

	return true;
}


// ====================================
// SceneHook
// ====================================

class MySceneHook : public SceneHookData
{
	INSTANCEOF(MySceneHook, SceneHookData)

public:
	MySceneHook();
	virtual Bool Draw(BaseSceneHook *node, BaseDocument *doc, BaseDraw *bd, BaseDrawHelp *bh, BaseThread *bt, SCENEHOOKDRAW flags);

	static NodeData *Alloc(void) { return NewObjClear(MySceneHook); }
};

MySceneHook::MySceneHook() : SceneHookData()
{
}

Bool MySceneHook::Draw(BaseSceneHook *node, BaseDocument *doc, BaseDraw *bd, BaseDrawHelp *bh, BaseThread *bt, SCENEHOOKDRAW flags)
{
	if (!node || !doc || !bd || !bh)
		return false;
	if (flags != SCENEHOOKDRAW::DRAW_PASS)
		return true;

	Int32 lLeft, lTop, lRight, lBottom;
	bd->GetFrame(&lLeft, &lTop, &lRight, &lBottom);

	bd->SetMatrix_Screen();

	DrawTheStuff(bd, SAFEINT32((lLeft + lRight) * 0.5), SAFEINT32((lTop + lBottom) * 0.5));


	// restore basedraw matrix
	bd->SetMatrix_Matrix(nullptr, Matrix());

	return true;
}

// ====================================
// Plugin Main 
// ====================================
Bool PluginStart(void)
{
	ApplicationOutput("Test"_s);

	RegisterSceneHookPlugin(MYSCENEHOOK_PLUGIN_ID, "MySceneHook"_s, 0, MySceneHook::Alloc, EXECUTIONPRIORITY_GENERATOR, 0);

	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,
your code seems to work on R22.2, there's no alpha hole below a certain value. I will ask our dev team if there's a workaround but i got huge doubt.

About the gradient not being the same that's probably because cinema4D is using linear workflow.
Converting the alpha value to linear should fix the issue.

	for (Int32 i = 0; i <= 255; ++i)
	{
		Float linearAlpha = Pow(Float(i) / 255.0, .45);
		clipMap->SetColor(1, 0, 0, linearAlpha * 255);
		clipMap->Line(10, 10 + i, width - 10 , 10 + i);
                
                // draw only the half for comparaison	
		clipMap->SetColor(0, 1, 0, i);
		clipMap->Line(10, 10 + i, (width - 10) /2, 10 + i);
	

Cheers,
Manuel

@m_magalhaes
Thanks for the tip to convert the alpha to linear value.
It is not perfect, but it does reduce the "gap" to a much smaller percentage.

Linear Alpha Gradient.png

With the original value I have a gap of 19%. Any alpha value below 20% opacity is just drawn as full transparent.
With the alpha value converted to linear this is now reduced to a gap of only 2%
I can live with this limitation.

It would be nice to hear from devs if there is a workaround to get a full gradient.
But I am already setting this topic as "solved".

I'm probably forgetting something for the conversion, i asked the dev to confirm. I really got doubt for a workaround for the version before R22.1.

What about if you just disable the linear workflow?

@m_magalhaes

Gradients.png

Did some more testing, using the original code and the conversion code, with linear workflow on and off.

When I look at the original code with linear workflow OFF, this most resembles the gradient from Photoshop. Except for the gap at the top .. obviously.

The result from conversion code with linear workflow ON is what I am using now, as this provides the smallest gap. And to my eyes that result looks the most linear.