BaseBitmap Manual

About

A BaseBitmap object stores raster graphics image data. It can support multiple alpha channels. The BaseBitmap class is used in many different application areas inside Cinema 4D. An extensions of this class is MultipassBitmap which also supports multiple layers (see MultipassBitmap Manual).

Allocation/Deallocation

A BaseBitmap can be created with the usual tools:

After the BaseBitmap was created it has to be initiated with BaseBitmap::Init(). This can be done by

  • defining the dimensions of the new BaseBitmap
  • or by defining the file name of an image file to load

Also a static version of BaseBitmap::Init() exists that loads a image defined by a Filename into a given BaseBitmap object.

// This example opens a dialog to select an image file.
// The selected file is loaded into a BaseBitmap and displayed
// in the Picture Viewer window.
Filename selectedImageFile;
// select image file
if (selectedImageFile.FileSelect(FILESELECTTYPE_IMAGES, FILESELECT_LOAD, "Select Image"))
{
// load image file into the given BaseBitmap
if (bitmap && bitmap->Init(selectedImageFile) == IMAGERESULT_OK)
{
// show BaseBitmap in the Picture Viewer
ShowBitmap(bitmap);
}
}
// This example creates a new BaseBitmap with Full HD resolution.
// If the BaseBitmap could be created, it is filled with a color.
if (!bitmap)
return false;
// initialize the BaseBitmap with the given dimensions
const Int32 width = 1920;
const Int32 height = 1080;
const Int32 depth = 24;
const IMAGERESULT res = bitmap->Init(width, height, depth);
if (res != IMAGERESULT_OK)
return false;
// loop through all pixels
Random randomGen;
for (Int32 x = 0; x < width; ++x)
{
for (Int32 y = 0; y < height; ++y)
{
// set to a random color
const Int32 red = Int32(randomGen.Get01() * COLORTOINT_MULTIPLIER);
const Int32 green = Int32(randomGen.Get01() * COLORTOINT_MULTIPLIER);
const Int32 blue = Int32(randomGen.Get01() * COLORTOINT_MULTIPLIER);
bitmap->SetPixel(x, y, red, green, blue);
}
}
// show BaseBitmap in the Picture Viewer
ShowBitmap(bitmap);

AutoBitmap can be used to create a BaseBitmap from an image file in the plugin resource path or a resource ID. This is typically used in a "Register" function, see Register.

// This example loads registered and local icons using AutoBitmap.
// load a registered icon ("Cube")
AutoBitmap cubeIcon(5159);
BaseBitmap* bitmap = cubeIcon;
if (bitmap)
ShowBitmap(bitmap);
// load an image file in the plugin's "res" folder
AutoBitmap localIcon("icon.tif");
if (localIcon)
ShowBitmap(localIcon);

Copy

The content of a BaseBitmap can be copied and with these functions:

// This example creates two clones of the given source bitmap.
// The two clones contain the left and right part of the bitmap.
// get original bitmap dimensions
const Int32 width = sourceBitmap->GetBw();
const Int32 height = sourceBitmap->GetBh();
// calculate left half width
const Int32 leftHalfWidth = width / 2;
// create clone with the left half of the bitmap
BaseBitmap* leftBitmap = sourceBitmap->GetClonePart(0, 0, leftHalfWidth, height);
if (leftBitmap)
{
ShowBitmap(leftBitmap);
BaseBitmap::Free(leftBitmap);
}
// create clone with the right half of the bitmap
const Int32 rightHalfWidth = width - leftHalfWidth;
BaseBitmap* rightBitmap = sourceBitmap->GetClonePart(leftHalfWidth, 0, rightHalfWidth, height);
if (rightBitmap)
{
ShowBitmap(rightBitmap);
BaseBitmap::Free(rightBitmap);
}

Read-Only Properties

The basic properties of a BaseBitmap are accessed with:

// This example reads every line of the given BaseBitmap and
// inverts the RBG color.
// only handle 8-bit RGB bitmaps in this example
const COLORMODE colorMode = bitmap->GetColorMode();
if (colorMode != COLORMODE_RGB)
return false;
// get bit depth and buffer size for one line
const Int32 bitsPerPixel = bitmap->GetBt();
const Int32 bytesPerPixel = bitsPerPixel / 8;
const Int32 bufferSize = bitmap->GetBpz();
// allocate memory for one line
PIX* lineBuffer = NewMemClear(PIX, bufferSize);
if (!lineBuffer)
return false;
// get bitmap dimensions
const Int32 width = bitmap->GetBw();
const Int32 height = bitmap->GetBh();
// loop through all lines
for (Int32 y = 0; y <= height; ++y)
{
// read full line from the bitmap
bitmap->GetPixelCnt(0, y, width, lineBuffer, bytesPerPixel, COLORMODE_RGB, PIXELCNT_0);
// this is a custom function
InvertLineRGB(lineBuffer, width);
// write full line back into the bitmap
bitmap->SetPixelCnt(0, y, width, lineBuffer, bytesPerPixel, COLORMODE_RGB, PIXELCNT_0);
}
// delete line memory
DeleteMem(lineBuffer);

The used color profile is managed with:

See also ColorProfile and ColorProfileConvert.

// This example sets the color profile according to the current
// settings of the given BaseDocument. The BaseBitmap is then drawn using
// this color profile inside a GeUserArea::DrawMsg() function.
// get linear workflow settings
BaseContainer* const docData = doc->GetDataInstance();
const Bool linearWorkflow = docData->GetBool(DOCUMENT_LINEARWORKFLOW);
// set profile
if (linearWorkflow)
_bitmap->SetColorProfile(ColorProfile::GetDefaultLinearRGB());
else
_bitmap->SetColorProfile(ColorProfile::GetDefaultSRGB());
// draw bitmap using the profile
const Int32 drawMode = BMP_APPLY_COLORPROFILE;
DrawBitmap(_bitmap, 0, 0, width, height, 0, 0, width, height, drawMode);

Functionality

Bitmap

The content of a BaseBitmap can be scaled into another BaseBitmap:

Warning
MultipassBitmap overwrites these functions. The overwritten functions will scale the layers but not the alpha channels.
// This example creates a new bitmap with twice the size of the source bitmap.
// The new bitmap is filled with the scaled content of the source bitmap.
// create new bitmap
AutoAlloc<BaseBitmap> scaledBitmap;
if (!scaledBitmap)
return true;
// get source bitmap dimensions
const Int32 originalWidth = sourceBitmap->GetBw();
const Int32 originalHeight = sourceBitmap->GetBh();
// initialize new bitmap with twice the size
const Int32 scale = 2;
const Int32 scaledWidth = originalWidth * scale;
const Int32 scaledHeight = originalHeight * scale;
const IMAGERESULT res = scaledBitmap->Init(scaledWidth, scaledHeight);
if (res == IMAGERESULT_OK)
{
// fill new bitmap with scaled content of the source bitmap
const Int32 defaultBrightness = 256;
sourceBitmap->ScaleIt(scaledBitmap, defaultBrightness, true, false);
}

One can perform basic drawing operations with these functions:

Note
For more advanced drawing operations use a GeClipMap, see GeClipMap Manual.
// This example fills a BaseBitmap and draws a shape.
// fill the bitmap with white pixels
bitmap->Clear(255, 255, 255);
// set the pen to black
bitmap->SetPen(0, 0, 0);
// draw lines
bitmap->Line(100, 100, 300, 100);
bitmap->Line(100, 200, 300, 200);
// draw arcs
const Float quarterCircle = DegToRad(90.0);
const Float threeQuarterCircle = DegToRad(270.0);
bitmap->Arc(300, 150, 50.0, quarterCircle, -quarterCircle, 20);
bitmap->Arc(100, 150, 50.0, quarterCircle, threeQuarterCircle, 20);

Properties

A BaseBitmap is used as the target buffer of a rendering process. Details of that rendering process are stored with the bitmap itself and are displayed by the Picture Viewer. So these settings are mostly only relevant to the rendering pipeline were the given VPBuffer can be cast into a BaseBitmap / MultipassBitmap.

The settings are:

// This example adds a BitmapButton to the layout of the GeDialog.
// Depending on the pixel ratio a low-res or high-res bitmap file is loaded
// and applied to the BitmapButton.
void* customGUI = AddCustomGui(1000, CUSTOMGUI_BITMAPBUTTON, "", BFH_SCALEFIT, 300, 300, settings);
BitmapButtonCustomGui* bitmapButtonGUI = static_cast<BitmapButtonCustomGui*>(customGUI);
if (bitmapButtonGUI)
{
// allocate bitmap
if (bitmap)
{
String filename = "";
// get pixel ratio
const Float pixelRatio = GetPixelRatio();
// check if Retina or not
if (pixelRatio == 1.0)
filename = "lowRes.png";
else
filename = "highRes.png";
// load bitmap (GetFullFilename() is just a custom utility function)
if (bitmap->Init(GetFullFilename(filename)) == IMAGERESULT_OK)
{
// store ratio
bitmap->SetData(BASEBITMAP_DATA_GUIPIXELRATIO, pixelRatio);
// apply to BitmapButton
bitmapButtonGUI->SetImage(bitmap, true, false);
}
}
}

Dirty

A BaseBitmap stores an incremental dirty state that is used in some scenarios to indicate that the BaseBitmap has changed.

// This example changes the given BaseBitmap and uses dirty flags
// to detect the change.
// check dirty state
UInt32 dirtyState = bitmap->GetDirty();
GePrint("Dirty State: " + String::UIntToString(dirtyState));
// edit bitmap and make dirty
bitmap->Clear(255, 255, 255);
bitmap->SetDirty();
// check dirty state
dirtyState = bitmap->GetDirty();
GePrint("Dirty State: " + String::UIntToString(dirtyState));

Pixel Data

The internally stored bitmap data of a BaseBitmap can be accessed with:

Typically used pixel formats can be defined with:

  • PIX: 8-bit integer pixel type.
  • PIX_C: 8-bit integer pixel type.
  • PIX_W: 16-bit integer pixel type.
  • PIX_F: 32-bit float pixel type.

See also COLORBYTES for macros defining bytes per pixel and COLORMODE for a definition of the color mode.

// This example creates and fills a 32-bit RGBf BaseBitmap.
if (!bitmap)
return false;
// define bitmap dimensions and bit depth
const Int32 width = 1024;
const Int32 height = 1024;
const Int32 pixelBytes = COLORBYTES_RGBf; // RGBf format
const Int32 pixelBits = pixelBytes * 8;
// initialize the BaseBitmap with the given dimensions and bit depth
const IMAGERESULT res = bitmap->Init(width, height, pixelBits);
if (res != IMAGERESULT_OK)
return false;
// allocate memory for one line
const Int32 bufferSize = width * 3;
PIX_F* lineBuffer = NewMemClear(PIX_F, bufferSize);
if (!lineBuffer)
return false;
// loop through all lines
for (Int32 y = 0; y <= height; ++y)
{
// shade of red based on the position
const Float32 red = Float32(y) / Float32(height);
Int32 offset = 0;
for (Int32 x = 0; x < width; ++x)
{
// shade of green based on the position
const Float32 green = Float32(x) / Float32(width);
// fill buffer
lineBuffer[offset] = red;
lineBuffer[offset + 1] = green;
lineBuffer[offset + 2] = 0.0;
offset += 3;
}
// write full line into the bitmap
bitmap->SetPixelCnt(0, y, width, (UChar*)lineBuffer, pixelBytes, COLORMODE_RGBf, PIXELCNT_0);
}
// delete line memory
DeleteMem(lineBuffer);

Alpha Channels

A BaseBitmap can contain up to four alpha channels. They are represented also with BaseBitmap instances:

// This example adds an alpha channel to the given BaseBitmap
// and stores the bitmap as a PNG file.
// fill image
bitmap->Clear(255, 255, 255);
// add alpha channel
BaseBitmap* alphaChannel = bitmap->AddChannel(true, false);
if (alphaChannel)
{
// set alpha value for each pixel
for (Int32 x = 0; x < width; ++x)
{
for (Int32 y = 0; y < height; ++y)
{
// create horizontal alpha gradient
const Float alphaValueFloat = (COLORTOINT_MULTIPLIER * Float(x)) / Float(width);
const Int32 alphaValueInt = Int32(alphaValueFloat);
bitmap->SetAlphaPixel(alphaChannel, x, y, alphaValueInt);
}
}
}
// save as PNG with alpha
Filename saveImageFile;
if (saveImageFile.FileSelect(FILESELECTTYPE_IMAGES, FILESELECT_SAVE, "Save as PNG"))
{
bitmap->Save(saveImageFile, FILTER_PNG, nullptr, SAVEBIT_ALPHA);
}

Convert

BaseBitmap is the base class of MultipassBitmap. If a pointer to a BaseBitmap is handed over one can check if it is actually a MultipassBitmap:

// This example lets the user select an image file that will be loaded.
// If the loaded image contains layers the layout count is printed.
Filename selectedImageFile;
// select image file
if (selectedImageFile.FileSelect(FILESELECTTYPE_IMAGES, FILESELECT_LOAD, "Select Image"))
{
BaseBitmap* bitmap = nullptr;
// load image file
const IMAGERESULT result = BaseBitmap::Init(bitmap, selectedImageFile);
// check success
if (result == IMAGERESULT_OK)
{
// check if it is a MultipassBitmap
if (bitmap->IsMultipassBitmap())
{
MultipassBitmap* multipassBitmap = static_cast<MultipassBitmap*>(bitmap);
// get layer count
const Int32 layerCount = multipassBitmap->GetLayerCount();
GePrint("Layer Count: " + String::IntToString(layerCount));
}
else
{
// no MultipassBitmap, no layers
GePrint("No layers");
}
}
}

Disc I/O

A BaseBitmap can read the content of an image file and save its content to an image file.

// This example let's the user select an image file.
// The loaded bitmap data is then saved in another image file format.
Filename selectedImageFile;
// select an image file
if (selectedImageFile.FileSelect(FILESELECTTYPE_IMAGES, FILESELECT_LOAD, "Select Image File"))
{
// load image file into the given BaseBitmap
if (bitmap && bitmap->Init(selectedImageFile) == IMAGERESULT_OK)
{
// save bitmap data in different formats
// if the original file is no JPEG, save as JPEG
if (selectedImageFile.CheckSuffix("jpg") == false)
{
Filename jpgFileName = selectedImageFile;
jpgFileName = GeFilterSetSuffix(jpgFileName, FILTER_JPG);
BaseContainer jpgSettings;
jpgSettings.SetInt32(JPGSAVER_QUALITY, 80);
bitmap->Save(jpgFileName, FILTER_JPG, &jpgSettings, SAVEBIT_0);
}
// if the original file is no PNG, save as PNG
if (selectedImageFile.CheckSuffix("png") == false)
{
Filename pngFileName = selectedImageFile;
pngFileName = GeFilterSetSuffix(pngFileName, FILTER_PNG);
bitmap->Save(pngFileName, FILTER_PNG, nullptr, SAVEBIT_0);
}
}
}
Note
An alternative way of loading image files is to use SendPainterCommand() with PAINTER_LOADTEXTURE.

A BaseBitmap can also be stored in a HyperFile using:

Display

A BaseBitmap or MultipassBitmap can easily displayed in the Picture Viewer:

// This example opens a dialog to select an image file.
// The selected file is loaded into a BaseBitmap and displayed
// in the Picture Viewer window.
Filename selectedImageFile;
// select image file
if (selectedImageFile.FileSelect(FILESELECTTYPE_IMAGES, FILESELECT_LOAD, "Select Image"))
{
// load image file into the given BaseBitmap
if (bitmap && bitmap->Init(selectedImageFile) == IMAGERESULT_OK)
{
// show BaseBitmap in the Picture Viewer
ShowBitmap(bitmap);
}
}

Further Reading