Hello @wickedp,
Thank you for reaching out to us. For GeClipMap::Init(BaseBitmap* bm)
:
- Is this copying the bmp into the clip map?
- Is the clip map now working on the original bitmap?
- No
- Yes
This also means that you cannot deallocate the bitmap for the lifetime of the GeClipmap
, or more specifically, as long as you intend to call methods on it. This is what is meant by 'the caller owns the pointed bitmap'; it is a polite way of saying 'make sure that this thing stays alive'.
Regarding the casting subject. Inheritance is not always necessary for casting to work, I assume this is where your question came from, and the relevant factor is memory layout. Sometimes we construct types to be layout compliant to other types to allow for casting (without an inheritance relation), the pairs (VPBuffer
, MultiPassBitmap
) and (Filename
, maxon::Url
) would be two examples which can be cast without an inheritance relation, for the latter it is however not advisable, you should use MaxonConvert
. For GeClipmap
this is not true, it just carries a private field for the managed bitmap.
Find below an example which demonstrates the behavior.
Cheers,
Ferdinand
Result (shown is here the first bitmap, i.e., source
, it also contains the red square):

source == result = true
Code:
/// @brief Demonstrates the ownership of bitmaps associated with a GeClipMap drawing canvas and the
/// bitmap object lifetime guarantees a caller must make.
static maxon::Result<void> RunGeClipmapTest(BaseDocument* doc)
{
// The bitmap we are going to use as a source. We are not using scope based memory management,
// AutoAlloc, but handle the bitmap ourselves, to make a point about ownership.
BaseBitmap* source;
iferr_scope;
finally
{
// Free #source when we exit the function normally or through an error.
BaseBitmap::Free(source);
};
// Allocate memory for #source and load an image into it.
Filename file {"/Users/f_hoppe/Documents/matcopy.gif"_s};
source = BaseBitmap::Alloc();
if (!source)
return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
if (source->Init(file) != IMAGERESULT::OK)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not load image file into bitmap."_s);
// Allocate a GeClipMap canvas to draw into and init it with #source, it will copy the pointer
// and not the object/memory itself.
AutoAlloc<GeClipMap> canvas;
if (!canvas)
return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
if (canvas->Init(source) != IMAGERESULT::OK)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);
// Doing something like this will lead to crashes in release mode or stops in debug mode, #canvas
// relies on the object, the memory region, pointed to by #source for the calls made below.
// BaseBitmap::Free(source);
// ---> Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
canvas->BeginDraw();
canvas->SetColor(255, 0, 0);
canvas->FillRect(0, 0, 100, 100);
canvas->EndDraw();
BaseBitmap* const result = canvas->GetBitmap();
if (!result)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);
// Both source and result will contain the red square in the top left corner because they are the
// same object.
ApplicationOutput("source == result = @"_s, source == result);
ShowBitmap(source);
ShowBitmap(result);
return maxon::OK;
}