Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "blend.h"
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
// TPoint structure
Toshihiro Shimizu 890ddd
#include "tgeometry.h"
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
// Palette - pixel functions
Toshihiro Shimizu 890ddd
#include "tpalette.h"
Toshihiro Shimizu 890ddd
#include "tpixelutils.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include <vector></vector>
Shinya Kitaoka 6526c7
#include <memory></memory>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=================================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//===========================
Toshihiro Shimizu 890ddd
//    Blur pattern class
Toshihiro Shimizu 890ddd
//---------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//! The BlurPattern class delineates the idea of a 'blur'
Toshihiro Shimizu 890ddd
//! pattern from a number of random sample points taken
Toshihiro Shimizu 890ddd
//! in a neighbourhood of the blurred pixel. The pattern
Toshihiro Shimizu 890ddd
//! develops in a radial manner if specified, so that possible
Toshihiro Shimizu 890ddd
//! 'obstacles' in the blur can be identified.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class BlurPattern {
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  typedef std::vector<tpoint> SamplePath;</tpoint>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  std::vector<tpoint> m_samples;</tpoint>
Shinya Kitaoka 120a6e
  std::vector<samplepath> m_samplePaths;</samplepath>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  BlurPattern(double distance, unsigned int samplesCount, bool radial);
Shinya Kitaoka 120a6e
  ~BlurPattern() {}
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
// Builds the specified number of samples count, inside the specified distance
Shinya Kitaoka 120a6e
// from the origin. If the pattern is radial, paths to the samples points are
Shinya Kitaoka 120a6e
// calculated.
Shinya Kitaoka 120a6e
BlurPattern::BlurPattern(double distance, unsigned int samplesCount,
Shinya Kitaoka 120a6e
                         bool radial) {
Shinya Kitaoka 120a6e
  const double randFactor = 2.0 * distance / RAND_MAX;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_samples.resize(samplesCount);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Build the samples
Shinya Kitaoka 120a6e
  unsigned int i;
Shinya Kitaoka 120a6e
  for (i = 0; i < samplesCount; ++i) {
Shinya Kitaoka 120a6e
    // NOTE: The following method ensures a perfectly flat probability
Shinya Kitaoka 120a6e
    // distribution.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TPoint candidatePoint(tround(rand() * randFactor - distance),
Shinya Kitaoka 120a6e
                          tround(rand() * randFactor - distance));
Shinya Kitaoka 120a6e
    double distanceSq = sq(distance);
Shinya Kitaoka 120a6e
    while (sq(candidatePoint.x) + sq(candidatePoint.y) > distanceSq)
Shinya Kitaoka 120a6e
      candidatePoint = TPoint(tround(rand() * randFactor - distance),
Shinya Kitaoka 120a6e
                              tround(rand() * randFactor - distance));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_samples[i] = candidatePoint;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_samplePaths.resize(samplesCount);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // If necessary, build the paths
Shinya Kitaoka 120a6e
  if (radial) {
Shinya Kitaoka 120a6e
    for (i = 0; i < samplesCount; ++i) {
Shinya Kitaoka 120a6e
      TPoint &sample = m_samples[i];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      int l = std::max(abs(sample.x), abs(sample.y));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_samplePaths[i].reserve(l);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      double dx = sample.x / (double)l;
Shinya Kitaoka 120a6e
      double dy = sample.y / (double)l;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      double x, y;
Shinya Kitaoka 120a6e
      int j;
Shinya Kitaoka 120a6e
      for (j = 0, x = dx, y = dy; j < l; x += dx, y += dy, ++j)
Shinya Kitaoka 120a6e
        m_samplePaths[i].push_back(TPoint(tround(x), tround(y)));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=================================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=================================
Toshihiro Shimizu 890ddd
//    Raster Selection classes
Toshihiro Shimizu 890ddd
//---------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct SelectionData {
Shinya Kitaoka 120a6e
  UCHAR m_selectedInk : 1;
Shinya Kitaoka 120a6e
  UCHAR m_selectedPaint : 1;
Shinya Kitaoka 120a6e
  UCHAR m_pureInk : 1;
Shinya Kitaoka 120a6e
  UCHAR m_purePaint : 1;
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=================================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
// Implements an array of selection infos using bitfields. It seems that
Shinya Kitaoka 120a6e
// bitfields are more optimized than
Shinya Kitaoka 120a6e
// using raw bits and bitwise operators, and use just the double of the space
Shinya Kitaoka 120a6e
// required with bit arrays.
Shinya Kitaoka 120a6e
class SelectionArrayPtr {
Shinya Kitaoka 120a6e
  std::unique_ptr<selectiondata[]> m_buffer;</selectiondata[]>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  inline void allocate(unsigned int count) {
Shinya Kitaoka 120a6e
    m_buffer.reset(new SelectionData[count]);
Shinya Kitaoka 120a6e
    memset(m_buffer.get(), 0, count * sizeof(SelectionData));
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  inline void destroy() { m_buffer.reset(); }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  inline SelectionData *data() const { return m_buffer.get(); }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  inline SelectionData *data() { return m_buffer.get(); }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=================================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// Bitmap used to store blend color selections and pure color informations.
Shinya Kitaoka 120a6e
class SelectionRaster {
Shinya Kitaoka 120a6e
  SelectionArrayPtr m_selection;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int m_wrap;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  SelectionRaster(TRasterCM32P cm);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void updateSelection(TRasterCM32P cm, const BlendParam ¶m);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  SelectionData *data() const { return m_selection.data(); }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  SelectionData *data() { return m_selection.data(); }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void destroy() { m_selection.destroy(); }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool isSelectedInk(int xy) const {
Shinya Kitaoka 120a6e
    return (m_selection.data() + xy)->m_selectedInk;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool isSelectedInk(int x, int y) const {
Shinya Kitaoka 120a6e
    return isSelectedInk(x + y * m_wrap);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool isSelectedPaint(int xy) const {
Shinya Kitaoka 120a6e
    return (m_selection.data() + xy)->m_selectedPaint;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool isSelectedPaint(int x, int y) const {
Shinya Kitaoka 120a6e
    return isSelectedPaint(x + y * m_wrap);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool isPureInk(int xy) const { return (m_selection.data() + xy)->m_pureInk; }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool isPureInk(int x, int y) const { return isPureInk(x + y * m_wrap); }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool isPurePaint(int xy) const {
Shinya Kitaoka 120a6e
    return (m_selection.data() + xy)->m_purePaint;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool isPurePaint(int x, int y) const { return isPurePaint(x + y * m_wrap); }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool isToneColor(int xy) const { return !(isPureInk(xy) || isPurePaint(xy)); }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool isToneColor(int x, int y) const { return isToneColor(x + y * m_wrap); }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
inline UCHAR linearSearch(const int *v, unsigned int vSize, int k) {
Shinya Kitaoka 120a6e
  const int *vEnd = v + vSize;
Shinya Kitaoka 120a6e
  for (; v < vEnd; ++v)
Shinya Kitaoka 120a6e
    if (*v == k) return 1;
Shinya Kitaoka 120a6e
  return 0;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
// I've seen the std::binary_search go particularly slow... perhaps it was the
Shinya Kitaoka 120a6e
// debug mode,
Toshihiro Shimizu 890ddd
// but I guess this is the fastest version possible.
Shinya Kitaoka 120a6e
inline UCHAR binarySearch(const int *v, unsigned int vSize, int k) {
Shinya Kitaoka 120a6e
  // NOTE: v.size() > 0 due to external restrictions. See SelectionRaster's
Shinya Kitaoka 120a6e
  // constructor.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int a = -1, b, c = vSize;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  for (b = c >> 1; b != a; b = (a + c) >> 1) {
Shinya Kitaoka 120a6e
    if (v[b] == k)
Shinya Kitaoka 120a6e
      return 1;
Shinya Kitaoka 120a6e
    else if (k < v[b])
Shinya Kitaoka 120a6e
      c = b;
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      a = b;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  return 0;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
SelectionRaster::SelectionRaster(TRasterCM32P cm) {
Shinya Kitaoka 120a6e
  unsigned int lx = cm->getLx(), ly = cm->getLy(), wrap = cm->getWrap();
Shinya Kitaoka 120a6e
  unsigned int size = lx * ly;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_wrap = lx;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_selection.allocate(size);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  cm->lock();
Shinya Kitaoka 120a6e
  TPixelCM32 *pix, *pixBegin = (TPixelCM32 *)cm->getRawData();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  SelectionData *selData = data();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  unsigned int i, j;
Shinya Kitaoka 120a6e
  for (i = 0; i < ly; ++i) {
Shinya Kitaoka 120a6e
    pix = pixBegin + i * wrap;
Shinya Kitaoka 120a6e
    for (j = 0; j < lx; ++j, ++pix, ++selData) {
Shinya Kitaoka 120a6e
      selData->m_pureInk   = pix->getTone() == 0;
Shinya Kitaoka 120a6e
      selData->m_purePaint = pix->getTone() == 255;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  cm->unlock();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void SelectionRaster::updateSelection(TRasterCM32P cm,
Shinya Kitaoka 120a6e
                                      const BlendParam ¶m) {
Shinya Kitaoka 120a6e
  // Make a hard copy of color indexes. We do so since we absolutely prefer
Shinya Kitaoka 120a6e
  // having them SORTED!
Shinya Kitaoka 120a6e
  std::vector<int> cIndexes = param.colorsIndexes;</int>
Shinya Kitaoka 120a6e
  std::sort(cIndexes.begin(), cIndexes.end());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  unsigned int lx = cm->getLx(), ly = cm->getLy(), wrap = cm->getWrap();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Scan each cm pixel, looking if its ink or paint is in param's colorIndexes.
Shinya Kitaoka 120a6e
  cm->lock();
Shinya Kitaoka 120a6e
  TPixelCM32 *pix, *pixBegin = (TPixelCM32 *)cm->getRawData();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  SelectionData *selData = data();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const int *v =
Shinya Kitaoka 120a6e
      &cIndexes[0];  // NOTE: cIndexes.size() > 0 due to external check.
Shinya Kitaoka 120a6e
  unsigned int vSize = cIndexes.size();
Shinya Kitaoka 120a6e
  unsigned int i, j;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // NOTE: It seems that linear searches are definitely best for small color
Shinya Kitaoka 120a6e
  // indexes.
Shinya Kitaoka 120a6e
  if (vSize > 50) {
Shinya Kitaoka 120a6e
    for (i = 0; i < ly; ++i) {
Shinya Kitaoka 120a6e
      pix = pixBegin + i * wrap;
Shinya Kitaoka 120a6e
      for (j = 0; j < lx; ++j, ++pix, ++selData) {
Shinya Kitaoka 120a6e
        selData->m_selectedInk   = binarySearch(v, vSize, pix->getInk());
Shinya Kitaoka 120a6e
        selData->m_selectedPaint = binarySearch(v, vSize, pix->getPaint());
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    for (i = 0; i < ly; ++i) {
Shinya Kitaoka 120a6e
      pix = pixBegin + i * wrap;
Shinya Kitaoka 120a6e
      for (j = 0; j < lx; ++j, ++pix, ++selData) {
Shinya Kitaoka 120a6e
        selData->m_selectedInk   = linearSearch(v, vSize, pix->getInk());
Shinya Kitaoka 120a6e
        selData->m_selectedPaint = linearSearch(v, vSize, pix->getPaint());
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  cm->unlock();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=================================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//========================
Toshihiro Shimizu 890ddd
//    Blend functions
Toshihiro Shimizu 890ddd
//------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
// Pixel whose channels are doubles. Used to store intermediate values for pixel
Shinya Kitaoka 120a6e
// blending.
Toshihiro Shimizu 890ddd
struct DoubleRGBMPixel {
Shinya Kitaoka 120a6e
  double r;
Shinya Kitaoka 120a6e
  double g;
Shinya Kitaoka 120a6e
  double b;
Shinya Kitaoka 120a6e
  double m;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  DoubleRGBMPixel() : r(0.0), g(0.0), b(0.0), m(0.0) {}
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
const double maxTone = TPixelCM32::getMaxTone();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// Returns the ink & paint convex factors associated with passed tone.
Shinya Kitaoka 120a6e
inline void getFactors(int tone, double &inkFactor, double &paintFactor) {
Shinya Kitaoka 120a6e
  paintFactor = tone / maxTone;
Shinya Kitaoka 120a6e
  inkFactor   = (1.0 - paintFactor);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// Copies the cmIn paint and ink colors to the output rasters.
Campbell Barton b3bd84
static void buildLayers(const TRasterCM32P &cmIn,
shun-iwasawa 27b0cf
                        const std::vector<tpixel32> &palColors,</tpixel32>
shun-iwasawa 27b0cf
                        TRaster32P &inkRaster, TRaster32P &paintRaster) {
Shinya Kitaoka 120a6e
  // Separate cmIn by copying the ink & paint colors directly to the layer
Shinya Kitaoka 120a6e
  // rasters.
Shinya Kitaoka 120a6e
  TPixelCM32 *cmPix, *cmBegin = (TPixelCM32 *)cmIn->getRawData();
Shinya Kitaoka 120a6e
  TPixel32 *inkPix   = (TPixel32 *)inkRaster->getRawData();
Shinya Kitaoka 120a6e
  TPixel32 *paintPix = (TPixel32 *)paintRaster->getRawData();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  unsigned int i, j, lx = cmIn->getLx(), ly = cmIn->getLy(),
Shinya Kitaoka 120a6e
                     wrap = cmIn->getWrap();
Shinya Kitaoka 120a6e
  for (i = 0; i < ly; ++i) {
Shinya Kitaoka 120a6e
    cmPix = cmBegin + i * wrap;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (j = 0; j < lx; ++j, ++cmPix, ++inkPix, ++paintPix) {
Shinya Kitaoka 120a6e
      *inkPix   = palColors[cmPix->getInk()];
Shinya Kitaoka 120a6e
      *paintPix = palColors[cmPix->getPaint()];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Should pure colors be checked...?
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// Returns true or false whether the selectedColor is the only selectable color
Shinya Kitaoka 120a6e
// in the neighbourhood. If so, the blend copies it to the output layer pixel
Shinya Kitaoka 120a6e
// directly.
Shinya Kitaoka 120a6e
inline bool isFlatNeighbourhood(int selectedColor, const TRasterCM32P &cmIn,
Shinya Kitaoka 120a6e
                                const TPoint &pos,
Shinya Kitaoka 120a6e
                                const SelectionRaster &selRas,
Shinya Kitaoka 120a6e
                                const BlurPattern &blurPattern) {
Shinya Kitaoka 120a6e
  TPixelCM32 &pix = cmIn->pixels(pos.y)[pos.x];
Shinya Kitaoka 120a6e
  int lx = cmIn->getLx(), ly = cmIn->getLy();
Shinya Kitaoka 120a6e
  unsigned int xy;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPoint samplePix;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const TPoint *samplePoint =
Shinya Kitaoka 120a6e
      blurPattern.m_samples.empty() ? 0 : &blurPattern.m_samples[0];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Read the samples to determine if they only have posSelectedColor
Shinya Kitaoka 120a6e
  unsigned int i, samplesCount = blurPattern.m_samples.size();
Shinya Kitaoka 120a6e
  for (i = 0; i < samplesCount; ++i, ++samplePoint) {
Shinya Kitaoka 120a6e
    // Make sure the sample is inside the image
Shinya Kitaoka 120a6e
    samplePix.x = pos.x + samplePoint->x;
Shinya Kitaoka 120a6e
    samplePix.y = pos.y + samplePoint->y;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    xy = samplePix.x + lx * samplePix.y;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (samplePix.x < 0 || samplePix.y < 0 || samplePix.x >= lx ||
Shinya Kitaoka 120a6e
        samplePix.y >= ly)
Shinya Kitaoka 120a6e
      continue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (!selRas.isPurePaint(xy) && selRas.isSelectedInk(xy))
Shinya Kitaoka 120a6e
      if (cmIn->pixels(samplePix.y)[samplePix.x].getInk() != selectedColor)
Shinya Kitaoka 120a6e
        return false;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (!selRas.isPureInk(xy) && selRas.isSelectedPaint(xy))
Shinya Kitaoka 120a6e
      if (cmIn->pixels(samplePix.y)[samplePix.x].getPaint() != selectedColor)
Shinya Kitaoka 120a6e
        return false;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  return true;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// Calculates the estimate of blend selection in the neighbourhood specified by
Toshihiro Shimizu 890ddd
// blurPattern.
Shinya Kitaoka 120a6e
inline void addSamples(const TRasterCM32P &cmIn, const TPoint &pos,
Shinya Kitaoka 120a6e
                       const TRaster32P &inkRas, const TRaster32P &paintRas,
Shinya Kitaoka 120a6e
                       const SelectionRaster &selRas,
Shinya Kitaoka 120a6e
                       const BlurPattern &blurPattern, DoubleRGBMPixel &pixSum,
Shinya Kitaoka 120a6e
                       double &factorsSum) {
Shinya Kitaoka 120a6e
  double inkFactor, paintFactor;
Shinya Kitaoka 120a6e
  unsigned int xy, j, l;
Shinya Kitaoka 120a6e
  int lx = cmIn->getLx(), ly = cmIn->getLy();
Shinya Kitaoka 120a6e
  TPixel32 *color;
Shinya Kitaoka 120a6e
  TPoint samplePos, pathPos;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const TPoint *samplePoint =
Shinya Kitaoka 120a6e
      blurPattern.m_samples.empty() ? 0 : &blurPattern.m_samples[0];
Shinya Kitaoka 120a6e
  const TPoint *pathPoint;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  unsigned int i, blurSamplesCount = blurPattern.m_samples.size();
Shinya Kitaoka 120a6e
  for (i = 0; i < blurSamplesCount; ++i, ++samplePoint) {
Shinya Kitaoka 120a6e
    // Add each samples contribute to the sum
Shinya Kitaoka 120a6e
    samplePos.x = pos.x + samplePoint->x;
Shinya Kitaoka 120a6e
    samplePos.y = pos.y + samplePoint->y;
Shinya Kitaoka 120a6e
    if (samplePos.x < 0 || samplePos.y < 0 || samplePos.x >= lx ||
Shinya Kitaoka 120a6e
        samplePos.y >= ly)
Shinya Kitaoka 120a6e
      continue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Ensure that each pixel on the sample's path (if any) is selected
Shinya Kitaoka 120a6e
    l         = blurPattern.m_samplePaths[i].size();
Shinya Kitaoka 120a6e
    pathPoint = blurPattern.m_samplePaths[i].empty()
Shinya Kitaoka 120a6e
                    ? 0
Shinya Kitaoka 120a6e
                    : &blurPattern.m_samplePaths[i][0];
Shinya Kitaoka 120a6e
    for (j = 0; j < l; ++j, ++pathPoint) {
Shinya Kitaoka 120a6e
      pathPos.x = pos.x + pathPoint->x;
Shinya Kitaoka 120a6e
      pathPos.y = pos.y + pathPoint->y;
Shinya Kitaoka 120a6e
      xy        = pathPos.x + lx * pathPos.y;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (!(selRas.isPurePaint(xy) || selRas.isSelectedInk(xy))) break;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (!(selRas.isPureInk(xy) || selRas.isSelectedPaint(xy))) break;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (j < l) continue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    xy = samplePos.x + lx * samplePos.y;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (selRas.isSelectedInk(xy) && !selRas.isPurePaint(xy)) {
Shinya Kitaoka 120a6e
      getFactors(cmIn->pixels(samplePos.y)[samplePos.x].getTone(), inkFactor,
Shinya Kitaoka 120a6e
                 paintFactor);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      color = &inkRas->pixels(samplePos.y)[samplePos.x];
Shinya Kitaoka 120a6e
      pixSum.r += inkFactor * color->r;
Shinya Kitaoka 120a6e
      pixSum.g += inkFactor * color->g;
Shinya Kitaoka 120a6e
      pixSum.b += inkFactor * color->b;
Shinya Kitaoka 120a6e
      pixSum.m += inkFactor * color->m;
Shinya Kitaoka 120a6e
      factorsSum += inkFactor;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (selRas.isSelectedPaint(xy) && !selRas.isPureInk(xy)) {
Shinya Kitaoka 120a6e
      getFactors(cmIn->pixels(samplePos.y)[samplePos.x].getTone(), inkFactor,
Shinya Kitaoka 120a6e
                 paintFactor);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      color = &paintRas->pixels(samplePos.y)[samplePos.x];
Shinya Kitaoka 120a6e
      pixSum.r += paintFactor * color->r;
Shinya Kitaoka 120a6e
      pixSum.g += paintFactor * color->g;
Shinya Kitaoka 120a6e
      pixSum.b += paintFactor * color->b;
Shinya Kitaoka 120a6e
      pixSum.m += paintFactor * color->m;
Shinya Kitaoka 120a6e
      factorsSum += paintFactor;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
typedef std::pair<traster32p, traster32p=""> RGBMRasterPair;</traster32p,>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// Performs a single color blending. This function can be repeatedly invoked to
Toshihiro Shimizu 890ddd
// perform multiple color blending.
Shinya Kitaoka 120a6e
inline void doBlend(const TRasterCM32P &cmIn, RGBMRasterPair &inkLayer,
Shinya Kitaoka 120a6e
                    RGBMRasterPair &paintLayer, const SelectionRaster &selRas,
Shinya Kitaoka 120a6e
                    const std::vector<blurpattern> &blurPatterns) {</blurpattern>
Shinya Kitaoka 120a6e
  // Declare some vars
Shinya Kitaoka 120a6e
  unsigned int blurPatternsCount = blurPatterns.size();
Shinya Kitaoka 120a6e
  int lx = cmIn->getLx(), ly = cmIn->getLy();
Shinya Kitaoka 120a6e
  double totalFactor;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPixelCM32 *cmPix, *cmBegin = (TPixelCM32 *)cmIn->getRawData();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPixel32 *inkIn    = (TPixel32 *)inkLayer.first->getRawData(),
Shinya Kitaoka 120a6e
           *inkOut   = (TPixel32 *)inkLayer.second->getRawData(),
Shinya Kitaoka 120a6e
           *paintIn  = (TPixel32 *)paintLayer.first->getRawData(),
Shinya Kitaoka 120a6e
           *paintOut = (TPixel32 *)paintLayer.second->getRawData();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const BlurPattern *blurPattern, *blurPatternsBegin = &blurPatterns[0];
Shinya Kitaoka 120a6e
  bool builtSamples = false;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  DoubleRGBMPixel samplesSum;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // For every cmIn pixel
Shinya Kitaoka 120a6e
  TPoint pos;
Shinya Kitaoka 120a6e
  SelectionData *selData = selRas.data();
Shinya Kitaoka 120a6e
  cmPix                  = cmBegin;
Shinya Kitaoka 120a6e
  for (pos.y = 0; pos.y < ly;
Shinya Kitaoka 120a6e
       ++pos.y, cmPix = cmBegin + pos.y * cmIn->getWrap())
Shinya Kitaoka 120a6e
    for (pos.x = 0; pos.x < lx; ++pos.x, ++inkIn, ++inkOut, ++paintIn,
Shinya Kitaoka 120a6e
        ++paintOut, ++selData, ++cmPix) {
Shinya Kitaoka 120a6e
      blurPattern = blurPatternsBegin + (rand() % blurPatternsCount);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Build the ink blend color
Shinya Kitaoka 120a6e
      if (!selData->m_purePaint && selData->m_selectedInk) {
Shinya Kitaoka 120a6e
        if (!builtSamples) {
Shinya Kitaoka 120a6e
          // Build samples contributes
Shinya Kitaoka 120a6e
          totalFactor  = 1.0;
Shinya Kitaoka 120a6e
          samplesSum.r = samplesSum.g = samplesSum.b = samplesSum.m = 0.0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          if (!isFlatNeighbourhood(cmPix->getInk(), cmIn, pos, selRas,
Shinya Kitaoka 120a6e
                                   *blurPattern))
Shinya Kitaoka 120a6e
            addSamples(cmIn, pos, inkLayer.first, paintLayer.first, selRas,
Shinya Kitaoka 120a6e
                       *blurPattern, samplesSum, totalFactor);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          builtSamples = true;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        // Output the blended pixel
Shinya Kitaoka 120a6e
        inkOut->r = (samplesSum.r + inkIn->r) / totalFactor;
Shinya Kitaoka 120a6e
        inkOut->g = (samplesSum.g + inkIn->g) / totalFactor;
Shinya Kitaoka 120a6e
        inkOut->b = (samplesSum.b + inkIn->b) / totalFactor;
Shinya Kitaoka 120a6e
        inkOut->m = (samplesSum.m + inkIn->m) / totalFactor;
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        // If the color is not blended, then just copy the old layer pixel
Shinya Kitaoka 120a6e
        *inkOut = *inkIn;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Build the paint blend color
Shinya Kitaoka 120a6e
      if (!selData->m_pureInk && selData->m_selectedPaint) {
Shinya Kitaoka 120a6e
        if (!builtSamples) {
Shinya Kitaoka 120a6e
          // Build samples contributes
Shinya Kitaoka 120a6e
          totalFactor  = 1.0;
Shinya Kitaoka 120a6e
          samplesSum.r = samplesSum.g = samplesSum.b = samplesSum.m = 0.0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          if (!isFlatNeighbourhood(cmPix->getPaint(), cmIn, pos, selRas,
Shinya Kitaoka 120a6e
                                   *blurPattern))
Shinya Kitaoka 120a6e
            addSamples(cmIn, pos, inkLayer.first, paintLayer.first, selRas,
Shinya Kitaoka 120a6e
                       *blurPattern, samplesSum, totalFactor);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          builtSamples = true;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        // Output the blended pixel
Shinya Kitaoka 120a6e
        paintOut->r = (samplesSum.r + paintIn->r) / totalFactor;
Shinya Kitaoka 120a6e
        paintOut->g = (samplesSum.g + paintIn->g) / totalFactor;
Shinya Kitaoka 120a6e
        paintOut->b = (samplesSum.b + paintIn->b) / totalFactor;
Shinya Kitaoka 120a6e
        paintOut->m = (samplesSum.m + paintIn->m) / totalFactor;
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        // If the color is not blended, then just copy the old layer pixel
Shinya Kitaoka 120a6e
        *paintOut = *paintIn;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      builtSamples = false;
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
typedef std::vector<blurpattern> BlurPatternContainer;</blurpattern>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
/*! This function performs a group of  spatial color blending <\a> operations
Shinya Kitaoka 120a6e
   on Toonz Images.
Shinya Kitaoka 120a6e
    The BlendParam structure stores the blend options recognized by this
Shinya Kitaoka 120a6e
   function; it includes
Toshihiro Shimizu 890ddd
    a list of the palette indexes involved in the blend operation, plus:
Shinya Kitaoka 120a6e
    \li \b Intensity represents the \a radius of the blur operation between
Shinya Kitaoka 120a6e
   blend colors.
Shinya Kitaoka 120a6e
    \li \b Smoothness is the number of samples per pixel used to approximate the
Shinya Kitaoka 120a6e
   blur.
Shinya Kitaoka 120a6e
    
  • Stop at Contour <\b> specifies if lines from pixels to neighbouring
  • Shinya Kitaoka 120a6e
       samples
    Shinya Kitaoka 120a6e
             should not trespass color indexes not included in the blend operation
    Shinya Kitaoka 120a6e
       <\li>
    Toshihiro Shimizu 890ddd
        The succession of input blend parameters are applied in the order.
    Toshihiro Shimizu 890ddd
    */
    Toshihiro Shimizu 890ddd
    Toshihiro Shimizu 890ddd
    template <typename pixel=""></typename>
    Shinya Kitaoka 120a6e
    void blend(TToonzImageP ti, TRasterPT<pixel> rasOut,</pixel>
    Shinya Kitaoka 120a6e
               const std::vector<blendparam> ¶ms) {</blendparam>
    Shinya Kitaoka 120a6e
      assert(ti->getRaster()->getSize() == rasOut->getSize());
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Extract the interesting raster. It should be the savebox of passed cmap,
    Shinya Kitaoka 120a6e
      // plus - if
    Shinya Kitaoka 120a6e
      // some param has the 0 index as blending color - the intensity of that blend
    Shinya Kitaoka 120a6e
      // param.
    Shinya Kitaoka 120a6e
      unsigned int i, j;
    Shinya Kitaoka 120a6e
      TRect saveBox(ti->getSavebox());
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      int enlargement = 0;
    Shinya Kitaoka 120a6e
      for (i = 0; i < params.size(); ++i)
    Shinya Kitaoka 120a6e
        for (j = 0; j < params[i].colorsIndexes.size(); ++j)
    Shinya Kitaoka 120a6e
          if (params[i].colorsIndexes[j] == 0)
    Shinya Kitaoka 120a6e
            enlargement = std::max(enlargement, tceil(params[i].intensity));
    Shinya Kitaoka 120a6e
      saveBox           = saveBox.enlarge(enlargement);
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      TRasterCM32P cmIn(ti->getRaster()->extract(saveBox));
    Shinya Kitaoka 120a6e
      TRasterPT<pixel> rasOutExtract = rasOut->extract(saveBox);</pixel>
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Ensure that cmIn and rasOut have the same size
    Shinya Kitaoka 120a6e
      unsigned int lx = cmIn->getLx(), ly = cmIn->getLy();
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Build the pure colors infos
    Shinya Kitaoka 120a6e
      SelectionRaster selectionRaster(cmIn);
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Now, build a little group of BlurPatterns - and for each, one for passed
    Shinya Kitaoka 120a6e
      // param.
    Shinya Kitaoka 120a6e
      // A small number of patterns per param is needed to make the pattern look not
    Shinya Kitaoka 120a6e
      // ever the same.
    Shinya Kitaoka 120a6e
      const int blurPatternsPerParam = 10;
    Shinya Kitaoka 120a6e
      std::vector<blurpatterncontainer> blurGroup(params.size());</blurpatterncontainer>
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      for (i = 0; i < params.size(); ++i) {
    Shinya Kitaoka 120a6e
        BlurPatternContainer &blurContainer = blurGroup[i];
    Shinya Kitaoka 120a6e
        blurContainer.reserve(blurPatternsPerParam);
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
        for (j = 0; j < blurPatternsPerParam; ++j)
    Shinya Kitaoka 120a6e
          blurContainer.push_back(BlurPattern(
    Shinya Kitaoka 120a6e
              params[i].intensity, params[i].smoothness, params[i].stopAtCountour));
    Shinya Kitaoka 120a6e
      }
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Build the palette
    Shinya Kitaoka 120a6e
      TPalette *palette = ti->getPalette();
    Shinya Kitaoka 120a6e
      std::vector<tpixel32> paletteColors;</tpixel32>
    Shinya Kitaoka 120a6e
      paletteColors.resize(palette->getStyleCount());
    Shinya Kitaoka 120a6e
      for (i             = 0; i < paletteColors.size(); ++i)
    Shinya Kitaoka 120a6e
        paletteColors[i] = premultiply(palette->getStyle(i)->getAverageColor());
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Build the 4 auxiliary rasters for the blending procedure: they are ink /
    Shinya Kitaoka 120a6e
      // paint versus input / output in the blend.
    Shinya Kitaoka 120a6e
      // The output raster is reused to spare some memory - it should be, say, the
    Shinya Kitaoka 120a6e
      // inkLayer's second at the end of the overall
    Shinya Kitaoka 120a6e
      // blending procedure. It could be the first, without the necessity of
    Shinya Kitaoka 120a6e
      // clearing it before blending the layers, but things
    Shinya Kitaoka 120a6e
      // get more complicated when PIXEL is TPixel64...
    Shinya Kitaoka 120a6e
      RGBMRasterPair inkLayer, paintLayer;
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      TRaster32P rasOut32P_1(lx, ly, lx, (TPixel32 *)rasOut->getRawData(), false);
    Shinya Kitaoka 120a6e
      inkLayer.first  = (params.size() % 2) ? rasOut32P_1 : TRaster32P(lx, ly);
    Shinya Kitaoka 120a6e
      inkLayer.second = (params.size() % 2) ? TRaster32P(lx, ly) : rasOut32P_1;
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      if (PIXEL::maxChannelValue >= TPixel64::maxChannelValue) {
    Shinya Kitaoka 120a6e
        TRaster32P rasOut32P_2(lx, ly, lx,
    Shinya Kitaoka 120a6e
                               ((TPixel32 *)rasOut->getRawData()) + lx * ly, false);
    Shinya Kitaoka 120a6e
        paintLayer.first  = (params.size() % 2) ? rasOut32P_2 : TRaster32P(lx, ly);
    Shinya Kitaoka 120a6e
        paintLayer.second = (params.size() % 2) ? TRaster32P(lx, ly) : rasOut32P_2;
    Shinya Kitaoka 120a6e
      } else {
    Shinya Kitaoka 120a6e
        paintLayer.first  = TRaster32P(lx, ly);
    Shinya Kitaoka 120a6e
        paintLayer.second = TRaster32P(lx, ly);
    Shinya Kitaoka 120a6e
      }
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      inkLayer.first->clear();
    Shinya Kitaoka 120a6e
      inkLayer.second->clear();
    Shinya Kitaoka 120a6e
      paintLayer.first->clear();
    Shinya Kitaoka 120a6e
      paintLayer.second->clear();
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Now, we have to perform the blur of each of the cm's pixels.
    Shinya Kitaoka 120a6e
      cmIn->lock();
    Shinya Kitaoka 120a6e
      rasOut->lock();
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      inkLayer.first->lock();
    Shinya Kitaoka 120a6e
      inkLayer.second->lock();
    Shinya Kitaoka 120a6e
      paintLayer.first->lock();
    Shinya Kitaoka 120a6e
      paintLayer.second->lock();
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Convert the initial cmIn to fullcolor ink - paint layers
    Shinya Kitaoka 120a6e
      buildLayers(cmIn, paletteColors, inkLayer.first, paintLayer.first);
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Perform the blend on separated ink - paint layers
    Shinya Kitaoka 120a6e
      for (i = 0; i < params.size(); ++i) {
    Shinya Kitaoka 120a6e
        if (params[i].colorsIndexes.size() == 0) continue;
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
        selectionRaster.updateSelection(cmIn, params[i]);
    Shinya Kitaoka 120a6e
        doBlend(cmIn, inkLayer, paintLayer, selectionRaster, blurGroup[i]);
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
        tswap(inkLayer.first, inkLayer.second);
    Shinya Kitaoka 120a6e
        tswap(paintLayer.first, paintLayer.second);
    Shinya Kitaoka 120a6e
      }
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Release the unnecessary rasters
    Shinya Kitaoka 120a6e
      inkLayer.second->unlock();
    Shinya Kitaoka 120a6e
      paintLayer.second->unlock();
    Shinya Kitaoka 120a6e
      inkLayer.second   = TRaster32P();
    Shinya Kitaoka 120a6e
      paintLayer.second = TRaster32P();
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Clear rasOut - since it was reused to spare space...
    Shinya Kitaoka 120a6e
      rasOut->clear();
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Add the ink & paint layers on the output raster
    Shinya Kitaoka 120a6e
      double PIXELmaxChannelValue = PIXEL::maxChannelValue;
    Shinya Kitaoka 120a6e
      double toPIXELFactor =
    Shinya Kitaoka 120a6e
          PIXELmaxChannelValue / (double)TPixel32::maxChannelValue;
    Shinya Kitaoka 120a6e
      double inkFactor, paintFactor;
    Shinya Kitaoka 120a6e
      TPoint pos;
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      PIXEL *outPix, *outBegin    = (PIXEL *)rasOutExtract->getRawData();
    Shinya Kitaoka 120a6e
      TPixelCM32 *cmPix, *cmBegin = (TPixelCM32 *)cmIn->getRawData();
    Shinya Kitaoka 120a6e
      int wrap = rasOutExtract->getWrap();
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      TPixel32 *inkPix   = (TPixel32 *)inkLayer.first->getRawData();
    Shinya Kitaoka 120a6e
      TPixel32 *paintPix = (TPixel32 *)paintLayer.first->getRawData();
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      for (i = 0; i < ly; ++i) {
    Shinya Kitaoka 120a6e
        outPix = outBegin + wrap * i;
    Shinya Kitaoka 120a6e
        cmPix  = cmBegin + wrap * i;
    Shinya Kitaoka 120a6e
        for (j = 0; j < lx; ++j, ++outPix, ++cmPix, ++inkPix, ++paintPix) {
    Shinya Kitaoka 120a6e
          getFactors(cmPix->getTone(), inkFactor, paintFactor);
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
          outPix->r = tcrop(
    Shinya Kitaoka 120a6e
              toPIXELFactor * (inkFactor * inkPix->r + paintFactor * paintPix->r),
    Shinya Kitaoka 120a6e
              0.0, PIXELmaxChannelValue);
    Shinya Kitaoka 120a6e
          outPix->g = tcrop(
    Shinya Kitaoka 120a6e
              toPIXELFactor * (inkFactor * inkPix->g + paintFactor * paintPix->g),
    Shinya Kitaoka 120a6e
              0.0, PIXELmaxChannelValue);
    Shinya Kitaoka 120a6e
          outPix->b = tcrop(
    Shinya Kitaoka 120a6e
              toPIXELFactor * (inkFactor * inkPix->b + paintFactor * paintPix->b),
    Shinya Kitaoka 120a6e
              0.0, PIXELmaxChannelValue);
    Shinya Kitaoka 120a6e
          outPix->m = tcrop(
    Shinya Kitaoka 120a6e
              toPIXELFactor * (inkFactor * inkPix->m + paintFactor * paintPix->m),
    Shinya Kitaoka 120a6e
              0.0, PIXELmaxChannelValue);
    Shinya Kitaoka 120a6e
        }
    Shinya Kitaoka 120a6e
      }
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      inkLayer.first->unlock();
    Shinya Kitaoka 120a6e
      paintLayer.first->unlock();
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      cmIn->unlock();
    Shinya Kitaoka 120a6e
      rasOut->unlock();
    Shinya Kitaoka 120a6e
    Shinya Kitaoka 120a6e
      // Destroy the auxiliary bitmaps
    Shinya Kitaoka 120a6e
      selectionRaster.destroy();
    Toshihiro Shimizu 890ddd
    }
    Toshihiro Shimizu 890ddd
    Shinya Kitaoka 120a6e
    template void blend<tpixel32>(TToonzImageP cmIn, TRasterPT<tpixel32> rasOut,</tpixel32></tpixel32>
    Shinya Kitaoka 120a6e
                                  const std::vector<blendparam> ¶ms);</blendparam>
    Shinya Kitaoka 120a6e
    template void blend<tpixel64>(TToonzImageP cmIn, TRasterPT<tpixel64> rasOut,</tpixel64></tpixel64>
    Shinya Kitaoka 120a6e
                                  const std::vector<blendparam> ¶ms);</blendparam>