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(); }
//! Elimina i segmenti che non sono contenuti all'interno dello stroke!!! void checkSegments(std::vector<TAutocloser::Segment> &segments, TStroke *stroke, const TRasterCM32P &ras, const TPoint &delta) { TVectorImage vi; TStroke *app = new TStroke(); *app = *stroke; app->transform(TTranslation(convert(ras->getCenter()))); vi.addStroke(app); vi.findRegions(); std::vector<TAutocloser::Segment>::iterator it = segments.begin(); for (; it < segments.end(); it++) { if (it == segments.end()) break; int i; bool isContained = false; for (i = 0; i < (int)vi.getRegionCount(); i++) { TRegion *reg = vi.getRegion(i); if (reg->contains(convert(it->first + delta)) && reg->contains(convert(it->second + delta))) { isContained = true; break; } } if (!isContained) { it = segments.erase(it); if (it != segments.end() && it != segments.begin()) it--; else if (it == segments.end()) break; } } }
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; }
void InkSegmenter::drawSegment( const TPoint &p0, const TPoint &p1, int ink, /*vector<pair<TPixelCM32*, int> >& oldInks,*/ TTileSaverCM32 *saver) { int x, y, dx, dy, d, incr_1, incr_2; int x1 = p0.x; int y1 = p0.y; int x2 = p1.x; int y2 = p1.y; if (x1 > x2) { tswap(x1, x2); tswap(y1, y2); } TPixelCM32 *buf = m_r->pixels() + y1 * m_wrap + x1; /*if (buf->getInk()!=damInk) oldInks.push_back(pair<TPixelCM32*, int>(buf, buf->getInk())); if ((m_r->pixels() + y2*m_wrap + x2)->getInk()!=damInk) oldInks.push_back(pair<TPixelCM32*, int>(m_r->pixels() + y2*m_wrap + x2, (m_r->pixels() + y2*m_wrap + x2)->getInk()));*/ if (saver) { saver->save(p0); saver->save(p1); } buf->setInk(ink); (m_r->pixels() + y2 * m_wrap + x2)->setInk(ink); dx = x2 - x1; dy = y2 - y1; x = y = 0; if (dy >= 0) { if (dy <= dx) DRAW_SEGMENT(x, y, dx, dy, (buf++), (buf += m_wrap + 1), SET_INK) else DRAW_SEGMENT(y, x, dy, dx, (buf += m_wrap), (buf += m_wrap + 1), SET_INK) } else {
void Convert2Tlv::buildInksFromGrayTones(TRasterCM32P &rout, const TRasterP &rin) { int i, j; TRasterGR8P r8 = (TRasterGR8P)rin; TRaster32P r32 = (TRaster32P)rin; if (r8) for (i = 0; i < rin->getLy(); i++) { TPixelGR8 *pixin = r8->pixels(i); TPixelCM32 *pixout = rout->pixels(i); for (j = 0; j < rin->getLx(); j++, pixin++, pixout++) *pixout = TPixelCM32(1, 0, pixin->value); } else for (i = 0; i < rin->getLy(); i++) { TPixel *pixin = r32->pixels(i); TPixelCM32 *pixout = rout->pixels(i); for (j = 0; j < rin->getLx(); j++, pixin++, pixout++) *pixout = TPixelCM32(1, 0, TPixelGR8::from(*pixin).value); } }
// Copies the cmIn paint and ink colors to the output rasters. void buildLayers(const TRasterCM32P &cmIn, const std::vector<TPixel32> &palColors, TRaster32P &inkRaster, TRaster32P &paintRaster) { // Separate cmIn by copying the ink & paint colors directly to the layer // rasters. TPixelCM32 *cmPix, *cmBegin = (TPixelCM32 *)cmIn->getRawData(); TPixel32 *inkPix = (TPixel32 *)inkRaster->getRawData(); TPixel32 *paintPix = (TPixel32 *)paintRaster->getRawData(); unsigned int i, j, lx = cmIn->getLx(), ly = cmIn->getLy(), wrap = cmIn->getWrap(); for (i = 0; i < ly; ++i) { cmPix = cmBegin + i * wrap; for (j = 0; j < lx; ++j, ++cmPix, ++inkPix, ++paintPix) { *inkPix = palColors[cmPix->getInk()]; *paintPix = palColors[cmPix->getPaint()]; // Should pure colors be checked...? } } }
// Returns true or false whether the selectedColor is the only selectable color // in the neighbourhood. If so, the blend copies it to the output layer pixel // directly. inline bool isFlatNeighbourhood(int selectedColor, const TRasterCM32P &cmIn, const TPoint &pos, const SelectionRaster &selRas, const BlurPattern &blurPattern) { TPixelCM32 &pix = cmIn->pixels(pos.y)[pos.x]; int lx = cmIn->getLx(), ly = cmIn->getLy(); unsigned int xy; TPoint samplePix; const TPoint *samplePoint = blurPattern.m_samples.empty() ? 0 : &blurPattern.m_samples[0]; // Read the samples to determine if they only have posSelectedColor unsigned int i, samplesCount = blurPattern.m_samples.size(); for (i = 0; i < samplesCount; ++i, ++samplePoint) { // Make sure the sample is inside the image samplePix.x = pos.x + samplePoint->x; samplePix.y = pos.y + samplePoint->y; xy = samplePix.x + lx * samplePix.y; if (samplePix.x < 0 || samplePix.y < 0 || samplePix.x >= lx || samplePix.y >= ly) continue; if (!selRas.isPurePaint(xy) && selRas.isSelectedInk(xy)) if (cmIn->pixels(samplePix.y)[samplePix.x].getInk() != selectedColor) return false; if (!selRas.isPureInk(xy) && selRas.isSelectedPaint(xy)) if (cmIn->pixels(samplePix.y)[samplePix.x].getPaint() != selectedColor) return false; } return true; }
//----------------------------------------------------------------------------- // questa funzione viene chiamata dopo il fill rect delle aree, e colora gli // inchiostri di tipo "autoink" // che confinano con le aree appena fillate con il rect. rbefore e' il rect del // raster prima del rectfill. void fillautoInks(TRasterCM32P &rin, TRect &rect, const TRasterCM32P &rbefore, TPalette *plt) { assert(plt); TRasterCM32P r = rin->extract(rect); assert(r->getSize() == rbefore->getSize()); int i, j; for (i = 0; i < r->getLy(); i++) { TPixelCM32 *pix = r->pixels(i); TPixelCM32 *pixb = rbefore->pixels(i); for (j = 0; j < r->getLx(); j++, pix++, pixb++) { int paint = pix->getPaint(); int tone = pix->getTone(); int ink = pix->getInk(); if (paint != pixb->getPaint() && tone > 0 && tone < 255 && ink != paint && plt->getStyle(ink)->getFlags() != 0) inkFill(rin, TPoint(j, i) + rect.getP00(), paint, 0, NULL, &rect); } } }
void Convert2Tlv::buildInksForNAAImage(TRasterCM32P &rout, const TRaster32P &rin) { std::map<TPixel, int>::iterator it; TPixel curColor = TPixel::Transparent; int i, j; int curIndex; //prima passata: identifico i colori di inchiostro e metto in rout i pixel di inchiostro puro for (i = 0; i < rin->getLy(); i++) { TPixel *pixin = rin->pixels(i); TPixelCM32 *pixout = rout->pixels(i); for (j = 0; j < rin->getLx(); j++, pixin++, pixout++) { TPixel colorIn; /*- treat white/transparent pixels as transparent -*/ if (*pixin == TPixel(255, 255, 255) || *pixin == TPixel::Transparent) { *pixout = TPixelCM32(0, 0, 255); continue; } if (curColor != *pixin) { curColor = *pixin; if ((it = m_colorMap.find(curColor)) == m_colorMap.end()) { if (m_lastIndex < 4095) m_colorMap[curColor] = ++m_lastIndex; curIndex = m_lastIndex; } else curIndex = it->second; } *pixout = TPixelCM32(curIndex, 0, 0); } } if (m_colorMap.empty()) m_colorMap[TPixel::Black] = ++m_lastIndex; }
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(); }
void OutlineVectorizer::makeDataRaster(const TRasterP &src) { m_vimage = new TVectorImage(); if (!src) return; m_src = src; clearNodes(); clearJunctions(); int x, y, ii = 0; TRaster32P srcRGBM = (TRaster32P)m_src; TRasterCM32P srcCM = (TRasterCM32P)m_src; TRasterGR8P srcGR = (TRasterGR8P)m_src; // Inizializzo DataRasterP per i casi in cui si ha un TRaster32P, un TRasterGR8P o un TRasterCM32P molto grande DataRasterP dataRaster(m_src->getSize().lx + 2, m_src->getSize().ly + 2); if (srcRGBM || srcGR || (srcCM && srcCM->getLx() * srcCM->getLy() > 5000000)) { int ly = dataRaster->getLy(); int lx = dataRaster->getLx(); int wrap = dataRaster->getWrap(); DataPixel *dataPix0 = dataRaster->pixels(0); DataPixel *dataPix1 = dataRaster->pixels(0) + m_src->getLx() + 1; for (y = 0; y < ly; y++, dataPix0 += wrap, dataPix1 += wrap) { dataPix0->m_pos.x = 0; dataPix1->m_pos.x = lx - 1; dataPix0->m_pos.y = dataPix1->m_pos.y = y; dataPix0->m_value = dataPix1->m_value = 0; dataPix0->m_ink = dataPix1->m_ink = false; dataPix0->m_node = dataPix1->m_node = 0; } dataPix0 = dataRaster->pixels(0); dataPix1 = dataRaster->pixels(ly - 1); for (x = 0; x < lx; x++, dataPix0++, dataPix1++) { dataPix0->m_pos.x = dataPix1->m_pos.x = x; dataPix0->m_pos.y = 0; dataPix1->m_pos.y = ly - 1; dataPix0->m_value = dataPix1->m_value = 0; dataPix0->m_ink = dataPix1->m_ink = false; dataPix0->m_node = dataPix1->m_node = 0; } } if (srcRGBM) { assert(m_palette); int inkId = m_palette->getClosestStyle(m_configuration.m_inkColor); if (!inkId || m_configuration.m_inkColor != m_palette->getStyle(inkId)->getMainColor()) { inkId = m_palette->getStyleCount(); m_palette->getStylePage(1)->insertStyle(1, m_configuration.m_inkColor); m_palette->setStyle(inkId, m_configuration.m_inkColor); } assert(inkId); m_dataRasterArray.push_back(std::pair<int, DataRasterP>(inkId, dataRaster)); int maxDistance2 = m_configuration.m_threshold * m_configuration.m_threshold; for (y = 0; y < m_src->getLy(); y++) { TPixel32 *inPix = srcRGBM->pixels(y); TPixel32 *inEndPix = inPix + srcRGBM->getLx(); DataPixel *dataPix = dataRaster->pixels(y + 1) + 1; x = 0; while (inPix < inEndPix) { *dataPix = DataPixel(); int distance2 = colorDistance2(m_configuration.m_inkColor, *inPix); if (y == 0 || y == m_src->getLy() - 1 || x == 0 || x == m_src->getLx() - 1 || inPix->m == 0) { dataPix->m_value = 255; dataPix->m_ink = false; } else { dataPix->m_value = (inPix->r + 2 * inPix->g + inPix->b) >> 2; dataPix->m_ink = (distance2 < maxDistance2); } dataPix->m_pos.x = x++; dataPix->m_pos.y = y; dataPix->m_node = 0; inPix++; dataPix++; } } } else if (srcGR) {
// Performs a single color blending. This function can be repeatedly invoked to // perform multiple color blending. inline void doBlend(const TRasterCM32P &cmIn, RGBMRasterPair &inkLayer, RGBMRasterPair &paintLayer, const SelectionRaster &selRas, const std::vector<BlurPattern> &blurPatterns) { // Declare some vars unsigned int blurPatternsCount = blurPatterns.size(); int lx = cmIn->getLx(), ly = cmIn->getLy(); double totalFactor; TPixelCM32 *cmPix, *cmBegin = (TPixelCM32 *)cmIn->getRawData(); TPixel32 *inkIn = (TPixel32 *)inkLayer.first->getRawData(), *inkOut = (TPixel32 *)inkLayer.second->getRawData(), *paintIn = (TPixel32 *)paintLayer.first->getRawData(), *paintOut = (TPixel32 *)paintLayer.second->getRawData(); const BlurPattern *blurPattern, *blurPatternsBegin = &blurPatterns[0]; bool builtSamples = false; DoubleRGBMPixel samplesSum; // For every cmIn pixel TPoint pos; SelectionData *selData = selRas.data(); cmPix = cmBegin; for (pos.y = 0; pos.y < ly; ++pos.y, cmPix = cmBegin + pos.y * cmIn->getWrap()) for (pos.x = 0; pos.x < lx; ++pos.x, ++inkIn, ++inkOut, ++paintIn, ++paintOut, ++selData, ++cmPix) { blurPattern = blurPatternsBegin + (rand() % blurPatternsCount); // Build the ink blend color if (!selData->m_purePaint && selData->m_selectedInk) { if (!builtSamples) { // Build samples contributes totalFactor = 1.0; samplesSum.r = samplesSum.g = samplesSum.b = samplesSum.m = 0.0; if (!isFlatNeighbourhood(cmPix->getInk(), cmIn, pos, selRas, *blurPattern)) addSamples(cmIn, pos, inkLayer.first, paintLayer.first, selRas, *blurPattern, samplesSum, totalFactor); builtSamples = true; } // Output the blended pixel inkOut->r = (samplesSum.r + inkIn->r) / totalFactor; inkOut->g = (samplesSum.g + inkIn->g) / totalFactor; inkOut->b = (samplesSum.b + inkIn->b) / totalFactor; inkOut->m = (samplesSum.m + inkIn->m) / totalFactor; } else { // If the color is not blended, then just copy the old layer pixel *inkOut = *inkIn; } // Build the paint blend color if (!selData->m_pureInk && selData->m_selectedPaint) { if (!builtSamples) { // Build samples contributes totalFactor = 1.0; samplesSum.r = samplesSum.g = samplesSum.b = samplesSum.m = 0.0; if (!isFlatNeighbourhood(cmPix->getPaint(), cmIn, pos, selRas, *blurPattern)) addSamples(cmIn, pos, inkLayer.first, paintLayer.first, selRas, *blurPattern, samplesSum, totalFactor); builtSamples = true; } // Output the blended pixel paintOut->r = (samplesSum.r + paintIn->r) / totalFactor; paintOut->g = (samplesSum.g + paintIn->g) / totalFactor; paintOut->b = (samplesSum.b + paintIn->b) / totalFactor; paintOut->m = (samplesSum.m + paintIn->m) / totalFactor; } else { // If the color is not blended, then just copy the old layer pixel *paintOut = *paintIn; } builtSamples = false; } }
// Calculates the estimate of blend selection in the neighbourhood specified by // blurPattern. inline void addSamples(const TRasterCM32P &cmIn, const TPoint &pos, const TRaster32P &inkRas, const TRaster32P &paintRas, const SelectionRaster &selRas, const BlurPattern &blurPattern, DoubleRGBMPixel &pixSum, double &factorsSum) { double inkFactor, paintFactor; unsigned int xy, j, l; int lx = cmIn->getLx(), ly = cmIn->getLy(); TPixel32 *color; TPoint samplePos, pathPos; const TPoint *samplePoint = blurPattern.m_samples.empty() ? 0 : &blurPattern.m_samples[0]; const TPoint *pathPoint; unsigned int i, blurSamplesCount = blurPattern.m_samples.size(); for (i = 0; i < blurSamplesCount; ++i, ++samplePoint) { // Add each samples contribute to the sum samplePos.x = pos.x + samplePoint->x; samplePos.y = pos.y + samplePoint->y; if (samplePos.x < 0 || samplePos.y < 0 || samplePos.x >= lx || samplePos.y >= ly) continue; // Ensure that each pixel on the sample's path (if any) is selected l = blurPattern.m_samplePaths[i].size(); pathPoint = blurPattern.m_samplePaths[i].empty() ? 0 : &blurPattern.m_samplePaths[i][0]; for (j = 0; j < l; ++j, ++pathPoint) { pathPos.x = pos.x + pathPoint->x; pathPos.y = pos.y + pathPoint->y; xy = pathPos.x + lx * pathPos.y; if (!(selRas.isPurePaint(xy) || selRas.isSelectedInk(xy))) break; if (!(selRas.isPureInk(xy) || selRas.isSelectedPaint(xy))) break; } if (j < l) continue; xy = samplePos.x + lx * samplePos.y; if (selRas.isSelectedInk(xy) && !selRas.isPurePaint(xy)) { getFactors(cmIn->pixels(samplePos.y)[samplePos.x].getTone(), inkFactor, paintFactor); color = &inkRas->pixels(samplePos.y)[samplePos.x]; pixSum.r += inkFactor * color->r; pixSum.g += inkFactor * color->g; pixSum.b += inkFactor * color->b; pixSum.m += inkFactor * color->m; factorsSum += inkFactor; } if (selRas.isSelectedPaint(xy) && !selRas.isPureInk(xy)) { getFactors(cmIn->pixels(samplePos.y)[samplePos.x].getTone(), inkFactor, paintFactor); color = &paintRas->pixels(samplePos.y)[samplePos.x]; pixSum.r += paintFactor * color->r; pixSum.g += paintFactor * color->g; pixSum.b += paintFactor * color->b; pixSum.m += paintFactor * color->m; factorsSum += paintFactor; } } }
void TRop::over(TRasterP rout, const TRasterCM32P &rup, TPalette *palette, const TPoint &point, const TAffine &aff) { TRaster32P app(rup->getSize()); TRop::convert(app, rup, palette); TRop::over(rout, app, point, aff); }
void Convert2Tlv::buildToonzRaster(TRasterCM32P &rout, const TRasterP &rin1, const TRasterP &rin2) { if (rin2) assert(rin1->getSize() == rin2->getSize()); rout->clear(); std::cout << " computing inks...\n"; TRaster32P r1 = (TRaster32P)rin1; TRasterGR8P r1gr = (TRasterGR8P)rin1; TRaster32P r2 = (TRaster32P)rin2; TRasterGR8P r2gr = (TRasterGR8P)rin2; TRasterP rU, rP; if (r1gr) { rU = r1gr; rP = r2; } else if (r2gr) { rU = r2gr; rP = r1; } else if (!r1) rU = r2; else if (!r2) rU = r1; else if (firstIsUnpainted(r1, r2)) { rU = r1; rP = r2; } else { rU = r2; rP = r1; } TRasterCM32P r; if (rout->getSize() != rU->getSize()) { int dx = rout->getLx() - rU->getLx(); int dy = rout->getLy() - rU->getLy(); assert(dx >= 0 && dy >= 0); r = rout->extract(dx / 2, dy / 2, dx / 2 + rU->getLx() - 1, dy / 2 + rU->getLy() - 1); } else r = rout; if ((TRasterGR8P)rU) buildInksFromGrayTones(r, rU); else if (m_isUnpaintedFromNAA) buildInksForNAAImage(r, (TRaster32P)rU); else { int maxMatte = getMaxMatte((TRaster32P)rU); if (maxMatte == -1) buildInksFromGrayTones(r, rU); else { if (maxMatte < 255) normalize(rU, maxMatte); buildInks(r, (TRaster32P)rU /*rP,*/); } } if (m_autoclose) TAutocloser(r, AutocloseDistance, AutocloseAngle, 1, AutocloseOpacity).exec(); if (rP) { std::cout << " computing paints...\n"; doFill(r, rP); } if (m_antialiasType == 2) //remove antialias removeAntialias(r); else if (m_antialiasType == 1) //add antialias { TRasterCM32P raux(r->getSize()); TRop::antialias(r, raux, 10, m_antialiasValue); rout = raux; } }
void Convert2Tlv::doFill(TRasterCM32P &rout, const TRaster32P &rin) { //prima passata: si filla solo partendo da pixel senza inchiostro, senza antialiasing(tone==255) for (int i = 0; i < rin->getLy(); i++) { TPixel *pixin = rin->pixels(i); TPixelCM32 *pixout = rout->pixels(i); for (int j = 0; j < rin->getLx(); j++, pixin++, pixout++) { if (!(pixout->getTone() == 255 && pixout->getPaint() == 0 && pixin->m == 255)) continue; std::map<TPixel, int>::const_iterator it; int paintIndex; if ((it = m_colorMap.find(*pixin)) == m_colorMap.end()) { if (m_colorTolerance > 0) it = findNearestColor(*pixin); // if (it==colorMap.end() && (int)colorMap.size()>origColorCount) //se non l'ho trovato tra i colori origari, lo cerco in quelli nuovi, ma in questo caso deve essere esattamente uguale(tolerance = 0) // it = findNearestColor(*pixin, colorMap, colorTolerance, origColorCount, colorMap.size()-1); if (it == m_colorMap.end() && m_lastIndex < 4096) { m_colorMap[*pixin] = ++m_lastIndex; paintIndex = m_lastIndex; } else if (it != m_colorMap.end()) { m_colorMap[*pixin] = it->second; paintIndex = it->second; } } else paintIndex = it->second; FillParameters params; params.m_p = TPoint(j, i); params.m_styleId = paintIndex; params.m_emptyOnly = true; fill(rout, params); //if (*((ULONG *)rout->getRawData())!=0xff) // { // int cavolo=0; // } } } //seconda passata: se son rimasti pixel antialiasati non fillati, si fillano, cercando nelle vicinanze un pixel di paint puro per capire il colore da usare for (int i = 0; i < rin->getLy(); i++) { TPixel *pixin = rin->pixels(i); TPixelCM32 *pixout = rout->pixels(i); for (int j = 0; j < rin->getLx(); j++, pixin++, pixout++) { if (!(pixout->getTone() > 0 && pixout->getTone() < 255 && pixout->getPaint() == 0 && pixin->m == 255)) continue; TPoint p = getClosestPurePaint(rout, i, j); if (p.x == -1) continue; //pixout->setPaint( paintIndex); FillParameters params; params.m_p = TPoint(j, i); params.m_styleId = (rout->pixels(p.y) + p.x)->getPaint(); params.m_emptyOnly = true; fill(rout, params); } } //infine, si filla di trasparente lo sfondo, percorrendo il bordo, nel caso di trasbordamenti di colore TPixelCM32 *pixCm; TPixel *pix; pixCm = rout->pixels(0); pix = rin->pixels(0); FillParameters params; params.m_styleId = 0; for (int i = 0; i < rout->getLx(); i++, pixCm++, pix++) if (pixCm->getTone() == 255 && pixCm->getPaint() != 0 && pix->m == 0) { params.m_p = TPoint(i, 0); fill(rout, params); } pixCm = rout->pixels(rout->getLy() - 1); pix = rin->pixels(rout->getLy() - 1); for (int i = 0; i < rout->getLx(); i++, pixCm++, pix++) if (pixCm->getTone() == 255 && pixCm->getPaint() != 0 && pix->m == 0) { params.m_p = TPoint(i, rout->getLy() - 1); fill(rout, params); } int wrapCM = rout->getWrap(); int wrap = rin->getWrap(); pixCm = rout->pixels(0); pix = rin->pixels(0); for (int i = 0; i < rin->getLy(); i++, pixCm += wrapCM, pix += wrap) if (pixCm->getTone() == 255 && pixCm->getPaint() != 0 && pix->m == 0) { params.m_p = TPoint(0, i); fill(rout, params); } pixCm = rout->pixels(0) + rout->getLx() - 1; pix = rin->pixels(0) + rin->getLx() - 1; for (int i = 0; i < rin->getLy(); i++, pixCm += wrapCM, pix += wrap) if (pixCm->getTone() == 255 && pixCm->getPaint() != 0 && pix->m == 0) { params.m_p = TPoint(rout->getLx() - 1, i); fill(rout, params); } }
void Convert2Tlv::buildInks(TRasterCM32P &rout, const TRaster32P &rin) { std::map<TPixel, int>::const_iterator it; TPixel curColor = TPixel::Transparent; int i, j; int curIndex; //prima passata: identifico i colori di inchiostro e metto in rout i pixel di inchiostro puro for (i = 0; i < rin->getLy(); i++) { TPixel *pixin = rin->pixels(i); TPixelCM32 *pixout = rout->pixels(i); for (j = 0; j < rin->getLx(); j++, pixin++, pixout++) { TPixel colorIn; if (pixin->m != 255) continue; if (curColor != *pixin) { curColor = *pixin; if ((it = m_colorMap.find(curColor)) == m_colorMap.end()) { if (m_colorTolerance > 0) it = findNearestColor(curColor); //if (it==colorMap.end() && (int)colorMap.size()>origColorCount) // it = findNearestColor(curColor, colorMap, colorTolerance, origColorCount, colorMap.size()-1); if (it == m_colorMap.end() && m_lastIndex < 4095) { m_colorMap[curColor] = ++m_lastIndex; curIndex = m_lastIndex; } else if (it != m_colorMap.end()) { m_colorMap[curColor] = it->second; curIndex = it->second; } } else curIndex = it->second; } *pixout = TPixelCM32(curIndex, 0, 0); } } //seconda passata: metto gli inchiostri di antialiasing curColor = TPixel::Transparent; for (i = 0; i < rin->getLy(); i++) { TPixel *pixin = rin->pixels(i); TPixelCM32 *pixout = rout->pixels(i); for (j = 0; j < rin->getLx(); j++, pixin++, pixout++) { TPixel colorIn; if (pixin->m == 255) //gia' messo nel ciclo precedente continue; if (pixin->m == 0) continue; colorIn = unmultiply(*pixin); //findClosestOpaque(rin, i, j); if (curColor != colorIn) { curColor = colorIn; if ((it = m_colorMap.find(curColor)) != m_colorMap.end()) curIndex = it->second; else curIndex = findClosest(m_colorMap, curColor); } *pixout = TPixelCM32(curIndex, 0, 255 - pixin->m); } } }
/*-- 戻り値は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; }
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); }