Working with Color Palette Overrides during Vectorization

Andrew Markovich

November 01, 2018

Typically during graphics vectorization of a database, the vectorizer uses the color palette that is set to the rendering device to resolve indexed colors (ACI color indexes in a .dwg database, DgnIndex colors in a .dgn database, etc.). Sometimes (for some specific objects) it might be necessary to override this palette completely or partially. For example, this functionality may be helpful for block insert vectorization to avoid re-creation of block content that has different colors and to draw the block with different palette colors instead.

For palette overrides, there are two new methods in the OdGiSubEntityTraits class:

virtual bool pushPaletteOverride(const OdGiPalette* pOverride); 

and

virtual void popPaletteOverride();

These two methods must always be called in a pair. If your drawable object pushes the palette override, it is must call the popPaletteOverride method afterward — this is important to maintain stable vectorization results.

Using palette overrides during entity vectorization

This example demonstrates how to use palette overrides in drawable graphics output.

bool subWorldDraw(OdGiWorldDraw* pWd) const
{ // Create palette override
  OdGiPalettePtr pPalette = OdGiPalette::createDynamic();
  // Set first palette color (typically red for ACI) as blue.
  pPalette->setColor(1, ODRGB(0, 0, 255));
  // Inform palette that we will use this color for overriding.
  pPalette->setEntryActivity(1, true);
  // Set fifth palette color (typically blue for ACI) as red.
  pPalette->setColor(5, ODRGB(255, 0, 0));
  // Inform palette that we will use this color for overriding.
  pPalette->setEntryActivity(5, true);
  // Set up palette override
  pWd->subEntityTraits().pushPaletteOverride(pPalette);
  // Draw some geometry
  pWd->subEntityTraits().setColor(1);
  pWd->geometry().circle(OdGePoint3d::kOrigin, 10.0, OdGeVector3d::kZAxis);
  pWd->subEntityTraits().setColor(5);
  pWd->geometry().circle(OdGePoint3d::kOrigin, 15.0, OdGeVector3d::kZAxis);
  // Disable palette override
  pWd->subEntityTraits().popPaletteOverride(pPalette);
  return true;
}

In this example, only two colors in the palette are overridden: the first color (which was red before) is overridden as blue, and the fifth color (which was blue before) is overridden as red. After, we push the palette override to the palette overrides stack and draw two circles. The smaller circle is drawn as blue and the other as red. If we disable the palette override, the colors are exchanged.

This code can be simplified using a special helper that calls the popPaletteOverride method in the destructor automatically. This helper is very useful when source code for drawing geometry contains many branches:

  // Set up palette override
  OdGiPaletteOverrideHelper poh(pWd->subEntityTraits(), *pPalette);
  // Draw some geometry
  pWd->subEntityTraits().setColor(1);
  pWd->geometry().circle(OdGePoint3d::kOrigin, 10.0, OdGeVector3d::kZAxis);
  pWd->subEntityTraits().setColor(5);
  pWd->geometry().circle(OdGePoint3d::kOrigin, 15.0, OdGeVector3d::kZAxis);
  return true;
}

This use-case is possible, but it isn’t very helpful since in this context we can simply use other color indexes for geometry drawing. The best use-case where palette overrides are helpful is when drawing other (already available) objects with overridden colors without changing the objects themselves.

For example, you can draw a block with overridden palette colors from a custom entity:

// Set up palette override
OdGiPaletteOverrideHelper poh(pWd->subEntityTraits(), *pPalette);
// Draw some geometry
OdDbDatabasePtr pDb(pWd->context()->database());
pWd->geometry().draw(OdDbBlockTable::cast(pDb->getBlockTableId().openObject())->getAt(OD_T("TestBlock"), OdDb::kForRead));

Using palette overrides with overruling

Use palette overrides together with overruling functionality to override a palette for all or part of objects in the database being vectorized:

class BlockRefDrawableOverrule : public OdGiDrawableOverrule
{
  public:
    BlockRefDrawableOverrule()
    {
      OdRxOverrule::setIsOverruling(true);
      // Attach overrule to OdDbBlockReference entity
      OdRxOverrule::addOverrule(OdDbBlockReference::desc(), this);
    }
    ~BlockRefDrawableOverrule()
    {
      OdRxOverrule::removeOverrule(OdDbBlockReference::desc(), this);
      OdRxOverrule::setIsOverruling(false);
    }

    virtual bool isApplicable(const OdRxObject* /*pOverruledSubject*/) const { return true; }

    virtual bool worldDraw(const OdGiDrawable* pSubject, OdGiWorldDraw *wd)
    {
      OdGiPalettePtr pPalette;
      // Since subSetAttributes for drawable already called we can get current drawable layer from subEntityTraits
      OdDbStub *layerId = wd->subEntityTraits().layer();
      OdDbBlockTableRecordPtr pLayer = OdDbObjectId(layerId).openObject();
      if (pLayer->getName() == OD_T("Layer1"))
      { // Set inverted palette for block references onto Layer1.
        pPalette = OdGiPalette::createDynamic();
        for (OdUInt32 nClr = 0; nClr < 256; nClr++)
        {
          ODCOLORREF clr = OdCmEntityColor::lookUpRGB((OdUInt8)nClr);
          clr = ODRGB(ODGETBLUE(clr), ODGETGREEN(clr), ODGETRED(clr));
          clr = (clr & 0xFF000000) | (~clr & 0x00FFFFFF); // Invert color avoiding alpha channel
          pPalette->setColor((OdInt32)nClr, clr);
          pPalette->setEntryActivity((OdInt32)nClr, true);
        }
      }
      else if (pLayer->getName() == OD_T("Layer2"))
      { // Set palette which exchanges blue and red color channels for block references onto Layer2.
        pPalette = OdGiPalette::createDynamic();
        for (OdUInt32 nClr = 0; nClr < 256; nClr++)
        {
          ODCOLORREF clr = OdCmEntityColor::lookUpRGB((OdUInt8)nClr);
          clr = ODRGB(ODGETBLUE(clr), ODGETGREEN(clr), ODGETRED(clr));
          // Exchange red and blue color channels.
          clr = (clr & 0xFF00FF00) | ((clr & 0x00FF0000) >> 16) | ((clr & 0x000000FF) << 16);
          pPalette->setColor((OdInt32)nClr, clr);
          pPalette->setEntryActivity((OdInt32)nClr, true);
        }
      }
      else if (pLayer->getName() == OD_T("Layer3"))
      { // Set grayscale palette override for block references onto Layer3
        pPalette = OdGiPalette::createDynamic();
        // Override first 8 palette colors using grayscale colors gradient.
        pPalette->install(OdGiGrayRamp::createDynamic(8));
      }
      // Set up palette override
      if (!pPalette.isNull())
        wd->subEntityTraits().pushPaletteOverride(pPalette);
      // Call subWorldDraw of original entity
      const bool bResult = OdGiDrawableOverrule::worldDraw(pSubject, wd);
      // Pop palette override
      if (!pPalette.isNull())
        wd->subEntityTraits().popPaletteOverride();
      return bResult; 
    }
};

This class enables overruling in the constructor and disables it in the destructor. For each block reference being vectorized, the worldDraw method is called, and it places the palette override in the palette overrides stack, calls the original entity drawing method, and removes the palette override from the stack.

In this example, we use layer names from the original entity to understand which palette override needs to be placed in the stack — this is one of the possible use-cases.

Example of rendering with palette overrides:

image1

 

In this example we have a block reference that uses the first six palette index colors for different nested entities (the original colors are shown in the top-left corner of the picture).

For block references placed on “Layer1”, we negate all 256 colors in the palette (results are shown in the top-right corner of the picture).

For block references placed on “Layer2”, we exchange red and blue color channels in the palette colors (results are shown in the bottom-left corner of the picture).

For block references placed on “Layer3”, we use a grayscale gradient generator to fill the first eight colors in the palette override (results are shown in the bottom-right corner of the picture).

Conclusion

This functionality is supported by all ODA vectorization modules. It has been available since the ODA 19.4 intermediate release and can be used with all ODA products.