The OdGiRasterImage interface gives the ability to implement raster image creation from various types of source data in a few steps, and the resulting raster image will be accepted by all image processing functionality, similar to a raster image loaded from file source. In this article, we will create a gradient image from scratch. We will use the four corner pixel colors that were extracted using the steps previously described in Part 1 of this series.
Create a class inherited from the OdGiRasterImage interface and add some class members to store image parameters:
class GeneratedRasterImage : public OdGiRasterImage
{
protected:
OdUInt32 m_pixW, m_pixH;
PixelFormatInfo m_pf;
ODCOLORREF m_cornerColors[4];
Implement all required abstract pure-virtual methods that are defined in the OdGiRasterImage interface:
public:
virtual OdUInt32 pixelWidth() const { return m_pixW; }
virtual OdUInt32 pixelHeight() const { return m_pixH; }
virtual OdUInt32 colorDepth() const { return 24; }
virtual OdUInt32 numColors() const { return 0; }
virtual ODCOLORREF color(OdUInt32 /*colorIndex*/) const { return 0; }
virtual OdUInt32 paletteDataSize() const { return 0; }
virtual void paletteData(OdUInt8* /*bytes*/) const { }
virtual PixelFormatInfo pixelFormat() const { return m_pf; }
virtual OdUInt32 scanLinesAlignment() const { return 4; }
Here we simply return the image width, height and pixel format which will be set during raster image construction. Use 24-bits per pixel color depth for the image. Palette-related methods are required for indexed images only (with color depths less than or equal to 8-bits per pixel), so simply stub them.
Add image construction methods which will be required to initialize data inside the GeneratedRasterImage class:
GeneratedRasterImage() : m_pixW(1), m_pixH(1) {}
void configureImage(OdUInt32 nWidth, OdUInt32 nHeight, const PixelFormatInfo &pf, const ODCOLORREF *pColors)
{
m_pixW = nWidth; m_pixH = nHeight; m_pf = pf;
for (int i = 0; i < 4; i++)
m_cornerColors[i] = pColors[i];
}
Finally implement the scanLines image methods:
virtual const OdUInt8* scanLines() const { return NULL; }
virtual void scanLines(OdUInt8* scnLines, OdUInt32 firstScanline, OdUInt32 numLines = 1) const
{
OdUInt32 scanLen = scanLineSize();
for (OdUInt32 i = firstScanline; i < firstScanline + numLines; i++)
{
OdUInt8 *pScanLine = scnLines + ((i - firstScanline) * scanLen);
for (OdUInt32 j = 0; j < m_pixW; j++, pScanLine += 3)
computePixel(j, i, pScanLine);
}
}
We don’t store pixel data inside our generated raster image class, so the direct scanlines accessor method will always return null in our case. The copy-based scanLines method simply calls the internal computePixel method for each pixel. The computePixel method uses four input colors to interpolate a gradient inside the generated image surface and puts the computed gradient colors into an output pixels buffer:
void computePixel(OdUInt32 x, OdUInt32 y, OdUInt8 *pOutput) const
{
// Interpolate colors by X-axis
const double interpolateX = double(x) / (m_pixW - 1);
const ODCOLORREF bottom = ODRGB((1.0 - interpolateX) * ODGETRED(m_cornerColors[0]) + interpolateX * ODGETRED(m_cornerColors[1]),
(1.0 - interpolateX) * ODGETGREEN(m_cornerColors[0]) + interpolateX * ODGETGREEN(m_cornerColors[1]),
(1.0 - interpolateX) * ODGETBLUE(m_cornerColors[0]) + interpolateX * ODGETBLUE(m_cornerColors[1]));
const ODCOLORREF top = ODRGB((1.0 - interpolateX) * ODGETRED(m_cornerColors[2]) + interpolateX * ODGETRED(m_cornerColors[3]),
(1.0 - interpolateX) * ODGETGREEN(m_cornerColors[2]) + interpolateX * ODGETGREEN(m_cornerColors[3]),
(1.0 - interpolateX) * ODGETBLUE(m_cornerColors[2]) + interpolateX * ODGETBLUE(m_cornerColors[3]));
// Interpolate colors in Y-axis
const double interpolateY = double(y) / (m_pixH - 1);
const ODCOLORREF color = ODRGB((1.0 - interpolateY) * ODGETRED(bottom) + interpolateY * ODGETRED(top),
(1.0 - interpolateY) * ODGETGREEN(bottom) + interpolateY * ODGETGREEN(top),
(1.0 - interpolateY) * ODGETBLUE(bottom) + interpolateY * ODGETBLUE(top));
pOutput[0] = ODGETRED(color); pOutput[1] = ODGETGREEN(color); pOutput[2] = ODGETBLUE(color);
}
The example image class implementation is complete. Now create an instance of the GeneratedRasterImage class, initialize it and store the output raster image for file saving or further experiments:
OdGiRasterImagePtr pOutputImage;
{
OdSmartPtr<GeneratedRasterImage> pImage = OdRxObjectImpl<GeneratedRasterImage>::createObject();
pImage->configureImage(256, 256, pInputImage->pixelFormat(), inputColors);
pOutputImage = pImage;
}
Now we can store our generated raster image using code from “Saving raster images in a file” in Part 1 of this series, and look at the resulting file: