//this one incorporate the preprocessColors and the finalize function; used for swatch.(tipically on very small rasters) TRasterP TCleanupper::processColors(const TRasterP &rin) { if (m_parameters->m_lineProcessingMode == lpNone) return rin; TRasterCM32P rcm = TRasterCM32P(rin->getSize()); if (!rcm) { assert(!"failed finalRas allocation!"); return TRasterCM32P(); } // Copy current cleanup palette to parameters' colors m_parameters->m_colors.update(m_parameters->m_cleanupPalette.getPointer(), m_parameters->m_noAntialias); bool toGr8 = (m_parameters->m_lineProcessingMode == lpGrey); if (toGr8) { //No (color) processing. Not even thresholding. This just means that all the important //stuff here is made in the brightness/contrast stage... //NOTE: Most of the color processing should be DISABLED in this case!! //finalRas->clear(); rin->lock(); rcm->lock(); if (TRasterGR8P(rin)) { UCHAR *rowin = rin->getRawData(); TUINT32 *rowout = reinterpret_cast<TUINT32 *>(rcm->getRawData()); for (int i = 0; i < rin->getLy(); i++) { for (int j = 0; j < rin->getLx(); j++) *rowout++ = *rowin++; //Direct copy for now... :( rowin += rin->getWrap() - rin->getLx(); rowout += rcm->getWrap() - rcm->getLx(); } } else { TPixel32 *rowin = reinterpret_cast<TPixel32 *>(rin->getRawData()); TUINT32 *rowout = reinterpret_cast<TUINT32 *>(rcm->getRawData()); for (int i = 0; i < rin->getLy(); i++) { for (int j = 0; j < rin->getLx(); j++) *rowout++ = TPixelGR8::from(*rowin++).value; rowin += rin->getWrap() - rin->getLx(); rowout += rcm->getWrap() - rcm->getLx(); } } rin->unlock(); rcm->unlock(); } else { assert(TRaster32P(rin)); preprocessColors(rcm, rin, m_parameters->m_colors); } //outImg->setDpi(outDpi.x, outDpi.y); CleanupPreprocessedImage cpi(m_parameters, TToonzImageP(rcm, rcm->getBounds()), toGr8); cpi.m_autocentered = true; TRaster32P rout = TRaster32P(rin->getSize()); finalize(rout, &cpi); return rout; }
void ToonzImageData::getData(TRasterP &copiedRaster, double &dpiX, double &dpiY, std::vector<TRectD> &rects, std::vector<TStroke> &strokes, std::vector<TStroke> &originalStrokes, TAffine &transformation, TPalette *targetPalette) const { if (!m_copiedRaster || (m_rects.empty() && m_strokes.empty())) return; copiedRaster = m_copiedRaster->clone(); dpiX = m_dpiX; dpiY = m_dpiY; assert(m_palette); int i; for (i = 0; i < (int)m_rects.size(); i++) rects.push_back(m_rects[i]); for (i = 0; i < (int)m_strokes.size(); i++) strokes.push_back(m_strokes[i]); for (i = 0; i < (int)m_originalStrokes.size(); i++) originalStrokes.push_back(m_originalStrokes[i]); transformation = m_transformation; TRasterCM32P cmRas = copiedRaster; if (!targetPalette) targetPalette = new TPalette(); if (!cmRas) return; std::set<int> usedStyles(m_usedStyles); TToonzImageP ti(cmRas, cmRas->getBounds()); if (usedStyles.size() == 0) ToonzImageUtils::getUsedStyles(usedStyles, ti); std::map<int, int> indexTable; mergePalette(targetPalette, indexTable, m_palette, usedStyles); ToonzImageUtils::scrambleStyles(ti, indexTable); ti->setPalette(m_palette.getPointer()); }
/*-- (StylePickerTool内で)LineとAreaを切り替えてPickできる。mode: 0=Area, 1=Line, 2=Line&Areas(default) --*/ int StylePicker::pickStyleId(const TPointD &pos, double radius2, int mode) const { int styleId = 0; if (TToonzImageP ti = m_image) { TRasterCM32P ras = ti->getRaster(); TPoint point = getRasterPoint(pos); if (!ras->getBounds().contains(point)) return -1; TPixelCM32 col = ras->pixels(point.y)[point.x]; switch (mode) { case 0: //AREAS styleId = col.getPaint(); break; case 1: //LINES styleId = col.getInk(); break; case 2: //ALL (Line & Area) default: styleId = col.isPurePaint() ? col.getPaint() : col.getInk(); break; } } else if (TRasterImageP ri = m_image) { const TPalette *palette = m_palette.getPointer(); if (!palette) return -1; TRaster32P ras = ri->getRaster(); if (!ras) return -1; TPoint point = getRasterPoint(pos); if (!ras->getBounds().contains(point)) return -1; TPixel32 col = ras->pixels(point.y)[point.x]; styleId = palette->getClosestStyle(col); } else if (TVectorImageP vi = m_image) { // prima cerca lo stile della regione piu' vicina TRegion *r = vi->getRegion(pos); if (r) styleId = r->getStyle(); // poi cerca quello della stroke, ma se prima aveva trovato una regione, richiede che // il click sia proprio sopra la stroke, altrimenti cerca la stroke piu' vicina (max circa 10 pixel) const double maxDist2 = (styleId == 0) ? 100.0 * radius2 : 0; bool strokeFound; double dist2, w, thick; UINT index; //!funzionerebbe ancora meglio con un getNearestStroke che considera //la thickness, cioe' la min distance dalla outline e non dalla centerLine strokeFound = vi->getNearestStroke(pos, w, index, dist2); if (strokeFound) { TStroke *stroke = vi->getStroke(index); thick = stroke->getThickPoint(w).thick; if (dist2 - thick * thick < maxDist2) { assert(stroke); styleId = stroke->getStyle(); } } } return styleId; }
AreaFiller::AreaFiller(const TRasterCM32P &ras) : m_ras(ras) , m_bounds(ras->getBounds()) , m_pixels(ras->pixels()) , m_wrap(ras->getWrap()) , m_color(0) { m_ras->lock(); }
void inkFill(const TRasterCM32P &r, const TPoint &pin, int ink, int searchRay, TTileSaverCM32 *saver, TRect *insideRect) { r->lock(); TPixelCM32 *pixels = (TPixelCM32 *)r->getRawData(); int oldInk; TPoint p = pin; if ((pixels + p.y * r->getWrap() + p.x)->isPurePaint() && (searchRay == 0 || (p = nearestInk(r, p, searchRay)) == TPoint(-1, -1))) { r->unlock(); return; } TPixelCM32 *pix = pixels + (p.y * r->getWrap() + p.x); if (pix->getInk() == ink) { r->unlock(); return; } oldInk = pix->getInk(); std::stack<TPoint> seeds; seeds.push(p); while (!seeds.empty()) { p = seeds.top(); seeds.pop(); if (!r->getBounds().contains(p)) continue; if (insideRect && !insideRect->contains(p)) continue; TPixelCM32 *pix = pixels + (p.y * r->getWrap() + p.x); if (pix->isPurePaint() || pix->getInk() != oldInk) continue; if (saver) saver->save(p); pix->setInk(ink); seeds.push(TPoint(p.x - 1, p.y - 1)); seeds.push(TPoint(p.x - 1, p.y)); seeds.push(TPoint(p.x - 1, p.y + 1)); seeds.push(TPoint(p.x, p.y - 1)); seeds.push(TPoint(p.x, p.y + 1)); seeds.push(TPoint(p.x + 1, p.y - 1)); seeds.push(TPoint(p.x + 1, p.y)); seeds.push(TPoint(p.x + 1, p.y + 1)); } r->unlock(); }
/*--- Toonz Raster LevelのToneを拾う。 ---*/ int StylePicker::pickTone(const TPointD &pos) { if (TToonzImageP ti = m_image) { TRasterCM32P ras = ti->getRaster(); if (!ras) return -1; TPoint point = getRasterPoint(pos); if (!ras->getBounds().contains(point)) return -1; TPixelCM32 col = ras->pixels(point.y)[point.x]; return col.getTone(); } else return -1; }
InkSegmenter(const TRasterCM32P &r, float growFactor, TTileSaverCM32 *saver) : m_r(r) , m_lx(r->getLx()) , m_ly(r->getLy()) , m_wrap(r->getWrap()) , m_buf((TPixelCM32 *)r->getRawData()) , m_bBox(r->getBounds()) , m_saver(saver) , m_growFactor(growFactor) { m_displaceVector[0] = -m_wrap - 1; m_displaceVector[1] = -m_wrap; m_displaceVector[2] = -m_wrap + 1; m_displaceVector[3] = -1; m_displaceVector[4] = +1; m_displaceVector[5] = m_wrap - 1; m_displaceVector[6] = m_wrap; m_displaceVector[7] = m_wrap + 1; }
/*-- 戻り値はsaveBoxが更新されたかどうか --*/ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, TTileSaverCM32 *saver) { TPixelCM32 *pix, *limit, *pix0, *oldpix; int oldy, xa, xb, xc, xd, dy; int oldxc, oldxd; int tone, oldtone; TPoint p = params.m_p; int x = p.x, y = p.y; int paint = params.m_styleId; int fillDepth = params.m_shiftFill ? params.m_maxFillDepth : params.m_minFillDepth; /*-- getBoundsは画像全面 --*/ TRect bbbox = r->getBounds(); /*- 画面外のクリックの場合はreturn -*/ if (!bbbox.contains(p)) return false; /*- 既に同じ色が塗られている場合はreturn -*/ if ((r->pixels(p.y) + p.x)->getPaint() == paint) return false; /*- 「透明部分だけを塗る」オプションが有効で、既に色が付いている場合はreturn * -*/ if (params.m_emptyOnly && (r->pixels(p.y) + p.x)->getPaint() != 0) return false; assert(fillDepth >= 0 && fillDepth < 16); switch (TPixelCM32::getMaxTone()) { case 15: fillDepth = (15 - fillDepth); break; case 255: fillDepth = ((15 - fillDepth) << 4) | (15 - fillDepth); break; default: assert(false); } /*-- 四隅の色を見て、一つでも変わったらsaveBoxを更新する --*/ TPixelCM32 borderIndex[4]; TPixelCM32 *borderPix[4]; pix = r->pixels(0); borderPix[0] = pix; borderIndex[0] = *pix; pix += r->getLx() - 1; borderPix[1] = pix; borderIndex[1] = *pix; pix = r->pixels(r->getLy() - 1); borderPix[2] = pix; borderIndex[2] = *pix; pix += r->getLx() - 1; borderPix[3] = pix; borderIndex[3] = *pix; std::stack<FillSeed> seeds; fillRow(r, p, xa, xb, paint, params.m_palette, saver); seeds.push(FillSeed(xa, xb, y, 1)); seeds.push(FillSeed(xa, xb, y, -1)); while (!seeds.empty()) { FillSeed fs = seeds.top(); seeds.pop(); xa = fs.m_xa; xb = fs.m_xb; oldy = fs.m_y; dy = fs.m_dy; y = oldy + dy; if (y > bbbox.y1 || y < bbbox.y0) continue; pix = pix0 = r->pixels(y) + xa; limit = r->pixels(y) + xb; oldpix = r->pixels(oldy) + xa; x = xa; oldxd = (std::numeric_limits<int>::min)(); oldxc = (std::numeric_limits<int>::max)(); while (pix <= limit) { oldtone = threshTone(*oldpix, fillDepth); tone = threshTone(*pix, fillDepth); if (pix->getPaint() != paint && tone <= oldtone && tone != 0) { fillRow(r, TPoint(x, y), xc, xd, paint, params.m_palette, saver); if (xc < xa) seeds.push(FillSeed(xc, xa - 1, y, -dy)); if (xd > xb) seeds.push(FillSeed(xb + 1, xd, y, -dy)); if (oldxd >= xc - 1) oldxd = xd; else { if (oldxd >= 0) seeds.push(FillSeed(oldxc, oldxd, y, dy)); oldxc = xc; oldxd = xd; } pix += xd - x + 1; oldpix += xd - x + 1; x += xd - x + 1; } else { pix++; oldpix++, x++; } } if (oldxd > 0) seeds.push(FillSeed(oldxc, oldxd, y, dy)); } bool saveBoxChanged = false; for (int i = 0; i < 4; i++) { if (!((*borderPix[i]) == borderIndex[i])) { saveBoxChanged = true; break; } } return saveBoxChanged; }