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(); }
//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 SelectionRaster::updateSelection(TRasterCM32P cm, const BlendParam ¶m) { // Make a hard copy of color indexes. We do so since we absolutely prefer // having them SORTED! std::vector<int> cIndexes = param.colorsIndexes; std::sort(cIndexes.begin(), cIndexes.end()); unsigned int lx = cm->getLx(), ly = cm->getLy(), wrap = cm->getWrap(); // Scan each cm pixel, looking if its ink or paint is in param's colorIndexes. cm->lock(); TPixelCM32 *pix, *pixBegin = (TPixelCM32 *)cm->getRawData(); SelectionData *selData = data(); const int *v = &cIndexes[0]; // NOTE: cIndexes.size() > 0 due to external check. unsigned int vSize = cIndexes.size(); unsigned int i, j; // NOTE: It seems that linear searches are definitely best for small color // indexes. if (vSize > 50) { for (i = 0; i < ly; ++i) { pix = pixBegin + i * wrap; for (j = 0; j < lx; ++j, ++pix, ++selData) { selData->m_selectedInk = binarySearch(v, vSize, pix->getInk()); selData->m_selectedPaint = binarySearch(v, vSize, pix->getPaint()); } } } else { for (i = 0; i < ly; ++i) { pix = pixBegin + i * wrap; for (j = 0; j < lx; ++j, ++pix, ++selData) { selData->m_selectedInk = linearSearch(v, vSize, pix->getInk()); selData->m_selectedPaint = linearSearch(v, vSize, pix->getPaint()); } } } cm->unlock(); }
SelectionRaster::SelectionRaster(TRasterCM32P cm) { unsigned int lx = cm->getLx(), ly = cm->getLy(), wrap = cm->getWrap(); unsigned int size = lx * ly; m_wrap = lx; m_selection.allocate(size); cm->lock(); TPixelCM32 *pix, *pixBegin = (TPixelCM32 *)cm->getRawData(); SelectionData *selData = data(); unsigned int i, j; for (i = 0; i < ly; ++i) { pix = pixBegin + i * wrap; for (j = 0; j < lx; ++j, ++pix, ++selData) { selData->m_pureInk = pix->getTone() == 0; selData->m_purePaint = pix->getTone() == 255; } } cm->unlock(); }
CleanupPreprocessedImage *TCleanupper::process( TRasterImageP &image, bool first_image, TRasterImageP &onlyResampledImage, bool isCameraTest, bool returnResampled, bool onlyForSwatch, TAffine *resampleAff) { TAffine aff; double blur; TDimension outDim(0, 0); TPointD outDpi; bool isSameDpi = false; bool autocentered = getResampleValues(image, aff, blur, outDim, outDpi, isCameraTest, isSameDpi); if (m_parameters->m_autocenterType != AUTOCENTER_NONE && !autocentered) DVGui::MsgBox(DVGui::WARNING, QObject::tr("The autocentering failed on the current drawing.")); bool fromGr8 = (bool)TRasterGR8P(image->getRaster()); bool toGr8 = (m_parameters->m_lineProcessingMode == lpGrey); // If necessary, perform auto-adjust if (!isCameraTest && m_parameters->m_lineProcessingMode != lpNone && toGr8 && m_parameters->m_autoAdjustMode != AUTO_ADJ_NONE && !onlyForSwatch) { static int ref_cum[256]; UCHAR lut[256]; int cum[256]; double x0_src_f, y0_src_f, x1_src_f, y1_src_f; int x0_src, y0_src, x1_src, y1_src; //cleanup_message("Autoadjusting... \n"); TAffine inv = aff.inv(); x0_src_f = affMV1(inv, 0, 0); y0_src_f = affMV2(inv, 0, 0); x1_src_f = affMV1(inv, outDim.lx - 1, outDim.ly - 1); y1_src_f = affMV2(inv, outDim.lx - 1, outDim.ly - 1); x0_src = tround(x0_src_f); y0_src = tround(y0_src_f); x1_src = tround(x1_src_f); y1_src = tround(y1_src_f); set_autoadjust_window(x0_src, y0_src, x1_src, y1_src); if (!TRasterGR8P(image->getRaster())) { //Auto-adjusting a 32-bit image. This means that a white background must be introduced first. TRaster32P ras32(image->getRaster()->clone()); TRop::addBackground(ras32, TPixel32::White); image = TRasterImageP(ras32); //old image is released here ras32 = TRaster32P(); TRasterGR8P rgr(image->getRaster()->getSize()); TRop::copy(rgr, image->getRaster()); //This is now legit. It was NOT before the clone, since the original could be cached. image->setRaster(rgr); } switch (m_parameters->m_autoAdjustMode) { case AUTO_ADJ_HISTOGRAM: { if (first_image) { build_gr_cum(image, ref_cum); } else { build_gr_cum(image, cum); build_gr_lut(ref_cum, cum, lut); apply_lut(image, lut); } } CASE AUTO_ADJ_HISTO_L : histo_l_algo(image, first_image); CASE AUTO_ADJ_BLACK_EQ : black_eq_algo(image); CASE AUTO_ADJ_NONE : DEFAULT : assert(false); } } fromGr8 = (bool)TRasterGR8P(image->getRaster()); //may have changed type due to auto-adjust assert(returnResampled || !onlyForSwatch); //if onlyForSwatch, then returnResampled // Allocate output colormap raster TRasterCM32P finalRas; if (!onlyForSwatch) { finalRas = TRasterCM32P(outDim); if (!finalRas) { TImageCache::instance()->outputMap(outDim.lx * outDim.ly * 4, "C:\\cachelog"); assert(!"failed finalRas allocation!"); return 0; } } // In case the input raster was a greymap, we cannot reutilize finalRas's buffer to transform the final // fullcolor pixels to colormap pixels directly (1 32-bit pixel would hold 4 8-bit pixels) - therefore, // a secondary greymap is allocated. //NOTE: This should be considered obsolete? By using TRop::resample( <TRaster32P& instance> , ...) we //should get the same effect!! TRasterP tmp_ras; if (returnResampled || (fromGr8 && toGr8)) { if (fromGr8 && toGr8) tmp_ras = TRasterGR8P(outDim); else tmp_ras = TRaster32P(outDim); if (!tmp_ras) { TImageCache::instance()->outputMap(outDim.lx * outDim.ly * 4, "C:\\cachelog"); assert(!"failed tmp_ras allocation!"); return 0; } } else //if finalRas is allocated, and the intermediate raster has to be 32-bit, we can perform pixel //conversion directly on the same output buffer tmp_ras = TRaster32P(outDim.lx, outDim.ly, outDim.lx, (TPixel32 *)finalRas->getRawData()); TRop::ResampleFilterType flt_type; if (isSameDpi) flt_type = TRop::ClosestPixel; //NearestNeighbor else if (isCameraTest) flt_type = TRop::Triangle; else flt_type = TRop::Hann2; TRop::resample(tmp_ras, image->getRaster(), aff, flt_type, blur); if ((TRaster32P)tmp_ras) //Add white background to deal with semitransparent pixels TRop::addBackground(tmp_ras, TPixel32::White); if (resampleAff) *resampleAff = aff; image->getRaster()->unlock(); image = TRasterImageP(); if (returnResampled) { onlyResampledImage = TRasterImageP(tmp_ras); onlyResampledImage->setDpi(outDpi.x, outDpi.y); } if (onlyForSwatch) return 0; assert(finalRas); // Copy current cleanup palette to parameters' colors m_parameters->m_colors.update(m_parameters->m_cleanupPalette.getPointer(), m_parameters->m_noAntialias); 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!! tmp_ras->lock(); finalRas->lock(); assert(tmp_ras->getSize() == finalRas->getSize()); assert(tmp_ras->getLx() == tmp_ras->getWrap()); assert(finalRas->getLx() == finalRas->getWrap()); int pixCount = outDim.lx * outDim.ly; if (fromGr8) { UCHAR *rowin = tmp_ras->getRawData(); TUINT32 *rowout = reinterpret_cast<TUINT32 *>(finalRas->getRawData()); for (int i = 0; i < pixCount; i++) *rowout++ = *rowin++; //Direct copy for now... :( } else { TPixel32 *rowin = reinterpret_cast<TPixel32 *>(tmp_ras->getRawData()); TUINT32 *rowout = reinterpret_cast<TUINT32 *>(finalRas->getRawData()); for (int i = 0; i < pixCount; i++) *rowout++ = TPixelGR8::from(*rowin++).value; } tmp_ras->unlock(); finalRas->unlock(); } else { //WARNING: finalRas and tmp_ras may share the SAME buffer! assert(TRaster32P(tmp_ras)); preprocessColors(finalRas, tmp_ras, m_parameters->m_colors); } TToonzImageP final; final = TToonzImageP(finalRas, finalRas->getBounds());
TPoint TFont::drawChar(TRasterCM32P &outImage, TPoint &glyphOrigin, int inkId, wchar_t charcode, wchar_t nextCharCode) const { TRasterGR8P grayAppImage; TPoint glyphOrig = appDrawChar(grayAppImage, m_pimpl->m_hdc, charcode); if (glyphOrig.x < 0) { glyphOrigin.x = glyphOrig.x; glyphOrig.x = 0; } else glyphOrigin.x = 0; if (glyphOrig.y < 0) { glyphOrigin.y = glyphOrig.y; glyphOrig.y = 0; } else glyphOrigin.y = 0; int srcLx = grayAppImage->getLx(); int srcLy = grayAppImage->getLy(); int dstLx = srcLx + glyphOrig.x; int dstLy = getMaxHeight(); outImage = TRasterCM32P(dstLx, dstLy); outImage->clear(); assert(TPixelCM32::getMaxTone() == 255); // TPixelCM32 bgColor(BackgroundStyle,BackgroundStyle,TPixelCM32::getMaxTone()); TPixelCM32 bgColor; int ty = m_pimpl->m_metrics.tmDescent - 1 + glyphOrig.y; assert(ty < dstLy); assert(ty >= srcLy - 1); grayAppImage->lock(); outImage->lock(); for (int sy = 0; sy < srcLy; ++sy, --ty) { TPixelGR8 *srcPix = grayAppImage->pixels(sy); TPixelCM32 *tarPix = outImage->pixels(ty) + glyphOrig.x; for (int x = 0; x < srcLx; ++x) { int tone = 256 - (srcPix->value << 2); // grayScale ToonzImage tone Meaning // 0 255 Bg = PurePaint // 1 252 // ... // 63 4 // 64 0 Fg = Pure Ink if (tone < 0) tone = 0; if (tone >= 255) *tarPix = bgColor; else *tarPix = TPixelCM32(inkId, 0, tone); // BackgroundStyle,tone); ++srcPix; ++tarPix; } } grayAppImage->unlock(); outImage->unlock(); return getDistance(charcode, nextCharCode); }