Files
2026-08-16 18:24:52 +07:00

372 lines
14 KiB
C++

// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#include "cbitmap.h"
#include "cdrawcontext.h"
#include "ccolor.h"
#include "algorithm.h"
#include "platform/iplatformbitmap.h"
#include "platform/platformfactory.h"
#include <cassert>
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CBitmap Implementation
//-----------------------------------------------------------------------------
/*! @class CBitmap
@section changes_version_4 Changes in 4.0
In Version 4.0 CBitmap was simplified. Previous versions supported drawing a color transparent of the bitmap. Since CBitmap supports
alpha drawing of bitmaps since some time, it's now the only way of drawing a bitmap with some parts transparent.
@section supported_file_formats Supported file formats
File format support is handled in a platform dependent way. On Windows GDI+ is used to import images. On Mac OS X CoreGraphics is used to import them.
For cross platform compatibility it is recommended to use PNG files.
@section loading Loading Bitmaps
You load a bitmap via a CResourceDescription which can hold a string or a number.
If you use names, you need to use the real filename with extension. Then it gets automaticly
loaded on Mac OS X out of the Resources folder of the vst bundle. On Windows you also specify the resource in the .rc file with the real filename.
@code
// using a number
1001 PNG DISCARDABLE "bmp01001.png"
// using a string
RealFileName.png PNG DISCARDABLE "RealFileName.png"
@endcode
@code
CBitmap* bitmap1 = new CBitmap (1001); // number
CBitmap* bitmap2 = new CBitmap ("RealFileName.png"); // string
@endcode
*/
//-----------------------------------------------------------------------------
CBitmap::CBitmap ()
{
}
//-----------------------------------------------------------------------------
CBitmap::CBitmap (const CResourceDescription& desc)
: resourceDesc (desc)
{
if (auto platformBitmap = getPlatformFactory ().createBitmap (desc))
bitmaps.emplace_back (platformBitmap);
}
//-----------------------------------------------------------------------------
CBitmap::CBitmap (CCoord width, CCoord height)
{
if (auto platformBitmap = getPlatformFactory ().createBitmap ({width, height}))
bitmaps.emplace_back (platformBitmap);
}
//------------------------------------------------------------------------
CBitmap::CBitmap (CPoint size, double scaleFactor)
{
size.x *= scaleFactor;
size.y *= scaleFactor;
size.makeIntegral ();
if (auto platformBitmap = getPlatformFactory ().createBitmap (size))
{
platformBitmap->setScaleFactor (scaleFactor);
bitmaps.emplace_back (platformBitmap);
}
}
//-----------------------------------------------------------------------------
CBitmap::CBitmap (const PlatformBitmapPtr& platformBitmap)
{
bitmaps.emplace_back (platformBitmap);
}
//-----------------------------------------------------------------------------
void CBitmap::draw (CDrawContext* context, const CRect& rect, const CPoint& offset, float alpha)
{
drawClipped (context, rect, [&] () {
context->drawBitmap (this, rect, offset, alpha);
});
}
//-----------------------------------------------------------------------------
CCoord CBitmap::getWidth () const
{
if (auto pb = getPlatformBitmap ())
return pb->getSize ().x / pb->getScaleFactor ();
return 0;
}
//-----------------------------------------------------------------------------
CCoord CBitmap::getHeight () const
{
if (auto pb = getPlatformBitmap ())
return pb->getSize ().y / pb->getScaleFactor ();
return 0;
}
//------------------------------------------------------------------------
CPoint CBitmap::getSize () const
{
CPoint p;
if (auto pb = getPlatformBitmap ())
{
auto scaleFactor = pb->getScaleFactor ();
p = pb->getSize ();
p.x /= scaleFactor;
p.y /= scaleFactor;
}
return p;
}
//-----------------------------------------------------------------------------
auto CBitmap::getPlatformBitmap () const -> PlatformBitmapPtr
{
return bitmaps.empty () ? nullptr : bitmaps[0];
}
//-----------------------------------------------------------------------------
void CBitmap::setPlatformBitmap (const PlatformBitmapPtr& bitmap)
{
if (bitmaps.empty ())
bitmaps.emplace_back (bitmap);
else
bitmaps[0] = bitmap;
}
//-----------------------------------------------------------------------------
bool CBitmap::addBitmap (const PlatformBitmapPtr& platformBitmap)
{
double scaleFactor = platformBitmap->getScaleFactor ();
CPoint size = getSize ();
CPoint bitmapSize = platformBitmap->getSize ();
bitmapSize.x /= scaleFactor;
bitmapSize.y /= scaleFactor;
if (size != bitmapSize)
{
vstgui_assert (size == bitmapSize, "wrong bitmap size");
return false;
}
for (const auto& bitmap : bitmaps)
{
if (bitmap->getScaleFactor () == scaleFactor || bitmap == platformBitmap)
{
vstgui_assert (bitmap->getScaleFactor () != scaleFactor && bitmap != platformBitmap);
return false;
}
}
bitmaps.emplace_back (platformBitmap);
return true;
}
//-----------------------------------------------------------------------------
auto CBitmap::getBestPlatformBitmapForScaleFactor (double scaleFactor) const -> PlatformBitmapPtr
{
if (bitmaps.empty ())
return nullptr;
auto bestBitmap = bitmaps[0];
double bestDiff = std::abs (scaleFactor - bestBitmap->getScaleFactor ());
for (const auto& bitmap : bitmaps)
{
if (bitmap->getScaleFactor () == scaleFactor)
return bitmap;
else if (std::abs (scaleFactor - bitmap->getScaleFactor ()) <= bestDiff && bitmap->getScaleFactor () > bestBitmap->getScaleFactor ())
{
bestBitmap = bitmap;
bestDiff = std::abs (scaleFactor - bitmap->getScaleFactor ());
}
}
return bestBitmap;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CMultiFrameBitmap::CMultiFrameBitmap (const CResourceDescription& desc,
CMultiFrameBitmapDescription multiFrameDesc)
: CBitmap (desc), description (multiFrameDesc)
{
}
//-----------------------------------------------------------------------------
bool CMultiFrameBitmap::setMultiFrameDesc (CMultiFrameBitmapDescription desc)
{
if (desc.frameSize.x * desc.framesPerRow > getSize ().x)
return false;
if (desc.frameSize.y * (desc.numFrames / desc.framesPerRow) > getSize ().y)
return false;
description = desc;
return true;
}
//-----------------------------------------------------------------------------
CMultiFrameBitmapDescription CMultiFrameBitmap::getMultiFrameDesc () const { return description; }
//-----------------------------------------------------------------------------
CPoint CMultiFrameBitmap::getFrameSize () const { return description.frameSize; }
//-----------------------------------------------------------------------------
uint16_t CMultiFrameBitmap::getNumFrames () const { return description.numFrames; }
//-----------------------------------------------------------------------------
uint16_t CMultiFrameBitmap::getNumFramesPerRow () const { return description.framesPerRow; }
//-----------------------------------------------------------------------------
CRect CMultiFrameBitmap::calcFrameRect (uint32_t frameIndex) const
{
if (getNumFrames () == 0)
return CRect ({}, getSize ());
if (frameIndex >= getNumFrames ())
frameIndex = getNumFrames () - 1;
auto rowIndex = frameIndex / getNumFramesPerRow ();
auto colIndex = frameIndex - (rowIndex * getNumFramesPerRow ());
CRect r;
r.left = getFrameSize ().x * colIndex;
r.right = r.left + getFrameSize ().x;
r.top = getFrameSize ().y * rowIndex;
r.bottom = r.top + getFrameSize ().y;
return r;
}
//-----------------------------------------------------------------------------
void CMultiFrameBitmap::drawFrame (CDrawContext* context, uint16_t frameIndex, CPoint pos)
{
auto fr = calcFrameRect (frameIndex);
auto r = CRect (pos, getFrameSize ());
draw (context, r, fr.getTopLeft ());
}
//-----------------------------------------------------------------------------
uint16_t CMultiFrameBitmap::normalizedValueToFrameIndex (float value) const
{
return normalizedToSteps<float, uint16_t> (value, getNumFrames () - 1);
}
//-----------------------------------------------------------------------------
float CMultiFrameBitmap::frameIndexToNormalizedValue (uint16_t frameIndex) const
{
return stepsToNormalized<float, uint16_t> (frameIndex, getNumFrames () - 1);
}
//-----------------------------------------------------------------------------
// CNinePartTiledBitmap Implementation
//-----------------------------------------------------------------------------
/*! @class CNinePartTiledBitmap
A nine-part tiled bitmap is tiled in nine parts which are drawing according to its part offsets:
- top left corner
- top right corner
- bottom left corner
- bottom right corner
- top edge, repeated as often as necessary and clipped appropriately
- left edge, dto.
- right edge, dto.
- bottom edge, dto.
- center, repeated horizontally and vertically as often as necessary
@verbatim
|------------------------------------------------------------------------------------------------|
| Top-Left Corner | <---- Top Edge ----> | Top-Right Corner |
|--------------------|-----------------------------------------------------|---------------------|
| ^ | ^ | ^ |
| | | | | | |
| Left Edge | <---- Center ----> | Right Edge |
| | | | | | |
| v | v | v |
|--------------------|-----------------------------------------------------|---------------------|
| Bottom-Left Corner | <---- Bottom Edge ----> | Bottom-Right Corner |
|------------------------------------------------------------------------------------------------|
@endverbatim
*/
//-----------------------------------------------------------------------------
CNinePartTiledBitmap::CNinePartTiledBitmap (const CResourceDescription& desc, const CNinePartTiledDescription& offsets)
: CBitmap (desc)
, offsets (offsets)
{
}
//-----------------------------------------------------------------------------
CNinePartTiledBitmap::CNinePartTiledBitmap (const PlatformBitmapPtr& platformBitmap, const CNinePartTiledDescription& offsets)
: CBitmap (platformBitmap)
, offsets (offsets)
{
}
//-----------------------------------------------------------------------------
void CNinePartTiledBitmap::draw (CDrawContext* inContext, const CRect& inDestRect, const CPoint& offset, float inAlpha)
{
inContext->drawBitmapNinePartTiled (this, inDestRect, offsets, inAlpha);
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
CBitmapPixelAccess::CBitmapPixelAccess ()
: bitmap (nullptr)
, pixelAccess (nullptr)
, currentPos (nullptr)
, address (nullptr)
, bytesPerRow (0)
, maxX (0)
, maxY (0)
, x (0)
, y (0)
{
}
//------------------------------------------------------------------------
void CBitmapPixelAccess::init (CBitmap* _bitmap, IPlatformBitmapPixelAccess* _pixelAccess)
{
bitmap = _bitmap;
pixelAccess = _pixelAccess;
address = currentPos = pixelAccess->getAddress ();
bytesPerRow = pixelAccess->getBytesPerRow ();
auto size = bitmap->getPlatformBitmap ()->getSize ();
maxX = static_cast<uint32_t> (size.x) - 1;
maxY = static_cast<uint32_t> (size.y) - 1;
}
/// @cond ignore
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
template <int32_t redPosition, int32_t greenPosition, int32_t bluePosition, int32_t alphaPosition>
class CBitmapPixelAccessOrder : public CBitmapPixelAccess
{
public:
void getColor (CColor& c) const override
{
c.red = currentPos[redPosition];
c.green = currentPos[greenPosition];
c.blue = currentPos[bluePosition];
c.alpha = currentPos[alphaPosition];
}
void setColor (const CColor& c) override
{
currentPos[redPosition] = c.red;
currentPos[greenPosition] = c.green;
currentPos[bluePosition] = c.blue;
currentPos[alphaPosition] = c.alpha;
}
};
/// @endcond
//------------------------------------------------------------------------
CBitmapPixelAccess* CBitmapPixelAccess::create (CBitmap* bitmap, bool alphaPremultiplied)
{
if (bitmap == nullptr || bitmap->getPlatformBitmap () == nullptr)
return nullptr;
auto pixelAccess = bitmap->getPlatformBitmap ()->lockPixels (alphaPremultiplied);
if (pixelAccess == nullptr)
return nullptr;
CBitmapPixelAccess* result = nullptr;
switch (pixelAccess->getPixelFormat ())
{
case IPlatformBitmapPixelAccess::kARGB: result = new CBitmapPixelAccessOrder<1,2,3,0> (); break;
case IPlatformBitmapPixelAccess::kRGBA: result = new CBitmapPixelAccessOrder<0,1,2,3> (); break;
case IPlatformBitmapPixelAccess::kABGR: result = new CBitmapPixelAccessOrder<3,2,1,0> (); break;
case IPlatformBitmapPixelAccess::kBGRA: result = new CBitmapPixelAccessOrder<2,1,0,3> (); break;
}
if (result)
result->init (bitmap, pixelAccess);
return result;
}
} // VSTGUI