//------------------------------------------------ inline void setMatteAndYMirror(const TRaster32P &ras) { ras->lock(); TPixel32 *upRow = ras->pixels(); TPixel32 *dwRow = ras->pixels(ras->getLy() - 1); int hLy = (int)(ras->getLy() / 2. + 0.5); //piccola pessimizzazione... int wrap = ras->getWrap(); int lx = ras->getLx(); TPixel32 *upPix = 0; TPixel32 *lastPix = ras->pixels(hLy); while (upPix < lastPix) { upPix = upRow; TPixel32 *dwPix = dwRow; TPixel32 *endPix = upPix + lx; while (upPix < endPix) { TPixel32 tmpPix(upPix->r, upPix->g, upPix->b, 0xff); *upPix = *dwPix; upPix->m = 0xff; *dwPix = tmpPix; ++upPix; ++dwPix; } upRow += wrap; dwRow -= wrap; } ras->unlock(); }
void FullColorAreaFiller::rectFill(const TRect &rect, const FillParameters ¶ms, bool onlyUnfilled) { TRect bbox = m_ras->getBounds(); TRect r = rect * bbox; if (r.isEmpty()) return; TRaster32P workRas = m_ras->extract(r); TRaster32P copy = workRas->clone(); TPixel32 color = params.m_palette->getStyle(params.m_styleId)->getMainColor(); // Fillo tutto il quadaratino con color int x, y; for (y = 0; y < workRas->getLy(); y++) { TPixel32 *line = workRas->pixels(y); for (x = 0; x < workRas->getLx(); x++) *(line + x) = overPix(color, workRas->pixels(y)[x]); } FillParameters paramsApp = params; TPixel32 refColor; for (y = 0; y < workRas->getLy(); y++) { paramsApp.m_p = TPoint(0, y); if (y == 0 || refColor != workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]) { fill(workRas, copy, paramsApp); refColor = workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]; } } for (y = 0; y < workRas->getLy(); y++) { paramsApp.m_p = TPoint(workRas->getLx() - 1, y); if (y == 0 || refColor != workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]) { fill(workRas, copy, paramsApp); refColor = workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]; } } for (x = 0; x < workRas->getLx(); x++) { paramsApp.m_p = TPoint(x, 0); if (x == 0 || refColor != workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]) { fill(workRas, copy, paramsApp); refColor = workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]; } } for (x = 0; x < workRas->getLx(); x++) { paramsApp.m_p = TPoint(x, workRas->getLy() - 1); if (x == 0 || refColor != workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]) { fill(workRas, copy, paramsApp); refColor = workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]; } } }
/*-- (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; }
FullColorAreaFiller::FullColorAreaFiller(const TRaster32P &ras) : m_ras(ras) , m_bounds(ras->getBounds()) , m_pixels(ras->pixels()) , m_wrap(ras->getWrap()) , m_color(0) { m_ras->lock(); }
static void clearMatte(const TRaster32P &ras, int xBegin, int yBegin, int xEnd, int yEnd) { for (int y = yBegin; y != yEnd; ++y) { TPixel32 *line = ras->pixels(y), *pixEnd = line + xEnd; for (TPixel32 *pix = line + xBegin; pix != pixEnd; ++pix) pix->m = 0; } }
void TRop::swapRBChannels(const TRaster32P &r) { int lx = r->getLx(); int y = r->getLy(); r->lock(); while (--y >= 0) { TPixel32 *pix = r->pixels(y); TPixel32 *endPix = pix + lx; while (pix < endPix) { tswap(pix->r, pix->b); ++pix; } } r->unlock(); }
void Naa2TlvConverter::setSourceImage(const TRaster32P &srcRas) { int lx = srcRas->getSize().lx; int ly = srcRas->getSize().ly; m_colors.clear(); m_regions.clear(); delete m_regionRas; m_regionRas = new WorkRaster<unsigned short>(lx, ly); delete m_borderRas; m_borderRas = 0; delete m_dotRas; m_dotRas = 0; delete m_syntheticInkRas; m_syntheticInkRas = 0; QMap<TPixel32, int> colorTable; for (int y = 0; y < ly; y++) { TPixel32 *srcScanLine = srcRas->pixels(y); unsigned short *regionScanLine = m_regionRas->pixels(y); for (int x = 0; x < lx; x++) { TPixel32 srcPix = overPixOnWhite(srcScanLine[x]); QMap<TPixel32, int>::ConstIterator it = colorTable.find(srcPix); if (it == colorTable.end()) { // found new color (and therefore new region) RegionInfo r; // add new color r.colorIndex = m_colors.count(); m_colors.append(srcPix); r.pixelCount = 1; // add new region int regionIndex = m_regions.count(); m_regions.append(r); // update raster and colorTable regionScanLine[x] = regionIndex; colorTable.insert(srcPix, regionIndex); if (m_colors.count() > MaxColorCount) { return; } } else { // already defined color int regionIndex = it.value(); regionScanLine[x] = regionIndex; m_regions[regionIndex].pixelCount++; } } } m_valid = true; }
static void addBackground32(TRaster32P ras, const TPixel32 &col) { ras->lock(); int nrows = ras->getLy(); while (nrows-- > 0) { TPixel32 *pix = ras->pixels(nrows); TPixel32 *endPix = pix + ras->getLx(); while (pix < endPix) { *pix = overPix(col, *pix); pix++; } } ras->unlock(); }
void do_over(TRaster32P rout, const TRasterGR8P &rup, const TPixel32 &color) { assert(rout->getSize() == rup->getSize()); for (int y = rout->getLy(); --y >= 0;) { TPixel32 *out_pix = rout->pixels(y); TPixel32 *const out_end = out_pix + rout->getLx(); const TPixelGR8 *up_pix = rup->pixels(y); for (; out_pix < out_end; ++out_pix, ++up_pix) { double v = up_pix->value / 255.0; TPixel32 up(troundp(v * color.r), troundp(v * color.g), troundp(v * color.b), troundp(v * color.m)); *out_pix = overPix(*out_pix, up); } } }
//Usata tinylinetest static void my_do_over(TRaster32P rout, const TRasterGR8P &rup) { assert(rout->getSize() == rup->getSize()); for (int y = rout->getLy(); --y >= 0;) { TPixel32 *out_pix = rout->pixels(y); TPixel32 *const out_end = out_pix + rout->getLx(); const TPixelGR8 *up_pix = rup->pixels(y); for (; out_pix < out_end; ++out_pix, ++up_pix) { int v = up_pix->value; out_pix->r = out_pix->r * v / 255; out_pix->g = out_pix->r; out_pix->b = out_pix->r; } } }
TImageP TImageReaderBmp::load() { TImageP image; void *buff; int retCode = readbmp(getFilePath().getWideString().c_str(), &m_lx, &m_ly, &buff); if (retCode != OK) { throw TImageException(getFilePath(), buildBMPExceptionString(retCode)); } TRaster32P raster; raster.create(m_lx, m_ly); raster->lock(); memcpy(raster->pixels(), buff, m_lx * m_ly * sizeof(TPixel32)); raster->unlock(); TRasterImageP rasImage(raster); // image->setRaster(raster); delete[] buff; return TImageP(rasImage); }
void DummyProcessor::process(TRaster32P raster) { if (isActive()) { int x, y, lx = raster->getLx(), ly = raster->getLy(); m_dummyData.clear(); m_dummyData.resize(ly, 0); std::vector<int> hues(lx); for (y = 0; y < ly; y++) { TPixel32 *pix = raster->pixels(y); for (x = 0; x < lx; x++) { int hsv[3]; rgb2hsv(hsv, *pix); hues[x] = hsv[0]; pix++; } std::pair<int, int> range; for (x = 0; x + 1 < lx; x++) if (abs(hues[x] - hues[x + 1]) > 5) break; m_dummyData[y] = x; } } }
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; }
TRaster32P TRop::copyAndSwapRBChannels(const TRaster32P &srcRaster) { TRaster32P newRaster(srcRaster->getSize()); int lx = srcRaster->getLx(); int y = srcRaster->getLy(); srcRaster->lock(); newRaster->lock(); while (--y >= 0) { TPixel32 *pix = srcRaster->pixels(y); TPixel32 *newpix = newRaster->pixels(y); TPixel32 *endPix = pix + lx; while (pix < endPix) { newpix->r = pix->b; newpix->g = pix->g; newpix->b = pix->r; newpix->m = pix->m; ++pix; ++newpix; } } srcRaster->unlock(); newRaster->unlock(); return newRaster; }
// 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 fill(const TRaster32P &ras, const TRaster32P &ref, const FillParameters ¶ms, TTileSaverFullColor *saver) { TPixel32 *pix, *limit, *pix0, *oldpix; int oldy, xa, xb, xc, xd, dy; int oldxc, oldxd; int matte, oldMatte; int x = params.m_p.x, y = params.m_p.y; TRaster32P workRas = ref ? ref : ras; TRect bbbox = workRas->getBounds(); if (!bbbox.contains(params.m_p)) return; TPaletteP plt = params.m_palette; TPixel32 color = plt->getStyle(params.m_styleId)->getMainColor(); int fillDepth = params.m_shiftFill ? params.m_maxFillDepth : params.m_minFillDepth; assert(fillDepth >= 0 && fillDepth < 16); fillDepth = ((15 - fillDepth) << 4) | (15 - fillDepth); // looking for any pure transparent pixel along the border; if after filling // that pixel will be changed, // it means that I filled the bg and the savebox needs to be recomputed! TPixel32 borderIndex; TPixel32 *borderPix = 0; pix = workRas->pixels(0); int i; for (i = 0; i < workRas->getLx(); i++, pix++) // border down if (pix->m == 0) { borderIndex = *pix; borderPix = pix; break; } if (borderPix == 0) // not found in border down...try border up (avoid left // and right borders...so unlikely) { pix = workRas->pixels(workRas->getLy() - 1); for (i = 0; i < workRas->getLx(); i++, pix++) // border up if (pix->m == 0) { borderIndex = *pix; borderPix = pix; break; } } std::stack<FillSeed> seeds; std::map<int, std::vector<std::pair<int, int>>> segments; // fillRow(r, params.m_p, xa, xb, color ,saver); findSegment(workRas, params.m_p, xa, xb, color); segments[y].push_back(std::pair<int, int>(xa, xb)); 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 = workRas->pixels(y) + xa; limit = workRas->pixels(y) + xb; oldpix = workRas->pixels(oldy) + xa; x = xa; oldxd = (std::numeric_limits<int>::min)(); oldxc = (std::numeric_limits<int>::max)(); while (pix <= limit) { oldMatte = threshMatte(oldpix->m, fillDepth); matte = threshMatte(pix->m, fillDepth); bool test = false; if (segments.find(y) != segments.end()) test = isPixelInSegment(segments[y], x); if (*pix != color && !test && matte >= oldMatte && matte != 255) { findSegment(workRas, TPoint(x, y), xc, xd, color); // segments[y].push_back(std::pair<int,int>(xc, xd)); insertSegment(segments[y], std::pair<int, int>(xc, xd)); 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)); } std::map<int, std::vector<std::pair<int, int>>>::iterator it; for (it = segments.begin(); it != segments.end(); it++) { TPixel32 *line = ras->pixels(it->first); TPixel32 *refLine = 0; TPixel32 *refPix; if (ref) refLine = ref->pixels(it->first); std::vector<std::pair<int, int>> segmentVector = it->second; for (int i = 0; i < (int)segmentVector.size(); i++) { std::pair<int, int> segment = segmentVector[i]; if (segment.second >= segment.first) { pix = line + segment.first; if (ref) refPix = refLine + segment.first; int n; for (n = 0; n < segment.second - segment.first + 1; n++, pix++) { if (ref) { *pix = *refPix; refPix++; } else *pix = pix->m == 0 ? color : overPix(color, *pix); } } } } }
void TRop::brush( TRaster32P ras, const TPoint &aa, const TPoint &bb, int radius, const TPixel32 &col) { TPoint a = aa; TPoint b = bb; if (a.y > b.y) tswap(a, b); // a e' piu' in basso di b int lx = ras->getLx(); int ly = ras->getLy(); ras->lock(); // ----- radius = 0 if (radius == 0) { // k = +1/-1 se il rettangolo e' inclinato positivamente (0<=m)/negativamente (m<0) // (se k<0 viene fatta una riflessione sulle ascisse prima di tornare alle // coordinate "di schermo") int k = 1; int dy = b.y - a.y; int dx = b.x - a.x; if (dx < 0) { dx = -dx; k = -1; } assert(dx >= 0); assert(dy >= 0); double m; // m sara' definita solo per dx!=0) if (dx > 0) { m = dy / (double)dx; } //double length = sqrt(dx*dx + dy*dy); const int alpha = dy, beta = -dx; const int incE = alpha; const int incNE = alpha + beta; const int incN = beta; // N.B. le coordinate sono relative ad un sist. di rif. con l'origine in a // l'eq. della retta e' alpha * x + beta * y = 0 int yMin = tmax(a.y, 0) - a.y; // clipping y + cambio riferimento int yMax = tmin(b.y, ly - 1) - a.y; // (trasporto dell'origine in a) if (dx > 0 && m <= 1) { // midpoint algorithm TPoint segm; if (dy == 0) // segmento orizzontale: inizializza segm { segm.x = 0; segm.y = yMin; } else // 0<m<=1 : inizializza segm { segm.x = tceil((yMin - 0.5) / m); segm.y = yMin; } int dSegm = tfloor(alpha * (segm.x + 1) + beta * (segm.y + 0.5)); while (segm.y <= yMax) { int count = 0; // i trati orizzontali di segm vengono disegnati in "blocco" while (dSegm < 0 && segm.x <= dx) // Est: segm.x<=dx evita il ciclo { // infinito quando m=0 (incE=0) dSegm = dSegm + incE; segm.x++; count++; } // NordEst int xMin, xMax; if (k > 0) { xMin = tmax(a.x + segm.x - count, a.x, 0); // clipping x + ritorno alle xMax = tmin(a.x + segm.x, b.x, lx - 1); // coordinate "di schermo" } else { xMin = tmax(a.x - segm.x, a.x - dx, 0); // clipping x + riflessione + ritorno xMax = tmin(a.x - segm.x + count, a.x, lx - 1); // alle coordinate "di schermo" } TPixel32 *p = ras->pixels(segm.y + a.y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; dSegm = dSegm + incNE; segm.x++; segm.y++; } } else // m>1 oppure segmento verticale { // midpoint algorithm TPoint segm; if (dx == 0) // segmento verticale: inizializza segm { segm.x = 0; segm.y = yMin; } else // m>1 : inizializza segm { segm.x = tround(yMin / m); segm.y = yMin; } int dSegm = tfloor(alpha * (segm.x + 0.5) + beta * (segm.y + 1)); while (segm.y <= yMax) { int xMin, xMax; if (k > 0) { xMin = tmax(a.x + segm.x, 0); // clipping x + ritorno alle xMax = tmin(a.x + segm.x, lx - 1); // coordinate "di schermo" } else { xMin = tmax(a.x - segm.x, 0); // clipping x + riflessione + ritorno xMax = tmin(a.x - segm.x, lx - 1); // alle coordinate "di schermo" } TPixel32 *p = ras->pixels(segm.y + a.y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; if (dSegm <= 0) // NordEst { dSegm = dSegm + incNE; segm.x++; } else // Nord { dSegm = dSegm + incN; } segm.y++; } } ras->unlock(); return; } HalfCord halfCord(radius); int x, y; // ----- punti iniziali coincidenti: disegna un cerchio if (a == b) { int yMin = tmax(a.y - radius, 0); // clipping y int yMax = tmin(a.y + radius, ly - 1); // clipping y for (y = yMin; y <= yMax; y++) { int deltay = abs(y - a.y); int xMin = tmax(a.x - halfCord.getCord(deltay), 0); // clipping x int xMax = tmin(a.x + halfCord.getCord(deltay), lx - 1); // clipping x TPixel32 *p = ras->pixels(y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; } ras->unlock(); return; } // ----- rettangolo orizzontale (a.y = b.y, a.x != b.x) if (a.y == b.y) { int yMin = tmax((a.y - radius), 0); // clipping y int yMax = tmin((a.y + radius), ly - 1); // clipping y int xLeft = tmin(a.x, b.x); int xRight = tmax(a.x, b.x); for (y = yMin; y <= yMax; y++) { int deltay = abs(y - a.y); int xMin = tmax(xLeft - halfCord.getCord(deltay), 0); // clipping x int xMax = tmin(xRight + halfCord.getCord(deltay), lx - 1); // clipping x TPixel32 *p = ras->pixels(y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; } ras->unlock(); return; } // ----- rettangolo verticale (a.x = b.x, a.y != b.y) if (a.x == b.x) { int xMin = tmax(a.x - radius, 0); // clipping x int xMax = tmin(a.x + radius, lx - 1); // clipping x for (x = xMin; x <= xMax; x++) { int deltax = abs(x - a.x); int yMin = tmax(a.y - halfCord.getCord(deltax), 0); // clipping y int yMax = tmin(b.y + halfCord.getCord(deltax), ly - 1); // clipping y if (yMin <= yMax) { TPixel32 *p = ras->pixels(yMin) + x; TPixel32 *q = ras->pixels(yMax) + x; int wrap = ras->getWrap(); while (p <= q) { *p = col; p += wrap; } } } ras->unlock(); return; } // ----- rettangolo inclinato // k = +1/-1 se il rettangolo e' inclinato positivamente/negativamente int k = 1; int dx = b.x - a.x; if (dx < 0) { dx = -dx; k = -1; } int dy = b.y - a.y; assert(dx > 0); assert(dy > 0); double length = sqrt((double)(dx * dx + dy * dy)); const double m = dy / (double)dx; //punto di tangenza superiore nel sistema di riferimento del cerchio TPointD up(-radius * dy / length, radius * dx / length); //semi-ampiezza orizzontale delle "calotte" circolari int halfAmplCap = tfloor(-up.x); // A meno di intersezioni relative tra le diverse zone: // le scanline della "calotta" circolare superiore sono (b.y+cutExt,b.y+radius] // le scanline del trapezoide circolare superiore sono [b.y-cutIn,b.y+cutExt] // le scanline del parallelogramma sono (a.y+cutIn,b.y-cutIn) // le scanline del trapezoide circolare inferiore sono [a.y-cutExt,a.y+cutIn] // le scanline della "calotta" circolare inferiore sono [a.y-radius,a.y-cutExt) int cutExt, cutIn; // vertici del parallelogramma TPointD rightUp; TPointD rightDown; TPointD leftUp; TPointD leftDown; double mParall; //coeff. angolare parallelogramma // NOTA BENE: halfAmplCap=0 <=> (radius=0 (caso a parte) , 1) if (radius > 1) { for (cutExt = radius; cutExt >= 0 && halfCord.getCord(cutExt) <= halfAmplCap; cutExt--) ; cutIn = cutExt; // vedi else successivo rightUp.x = dx + halfCord.getCord(cutIn); rightUp.y = dy - cutIn; rightDown.x = halfCord.getCord(cutIn); rightDown.y = -cutIn; leftUp.x = dx - halfCord.getCord(cutIn); leftUp.y = dy + cutIn; leftDown.x = -halfCord.getCord(cutIn); leftDown.y = cutIn; mParall = dy / (double)dx; } else // N.B. cutExt != cutIn solo quando radius=1 { cutExt = radius; // radius=1 => halfAmplCap=0 (non ci sono mai le "calotte" circolari) cutIn = 0; // anche per radius=1 il limite "interno" dei trapezoidi circolari e' < radius rightUp.x = dx - up.x; rightUp.y = dy - up.y; rightDown.x = -up.x; rightDown.y = -up.y; leftUp.x = dx + up.x; leftUp.y = dy + up.y; leftDown.x = up.x; leftDown.y = up.y; mParall = m; } // ----- riempie "calotte" circolari // ----- riempie "calotta" circolare inferiore int yMin = tmax(a.y - radius, 0); // clipping y int yMax = tmin(a.y - cutExt - 1, ly - 1); // clipping y for (y = yMin; y <= yMax; y++) { int r = halfCord.getCord(a.y - y); int xMin = tmax(a.x - r, 0); // clipping x int xMax = tmin(a.x + r, lx - 1); // clipping x TPixel32 *p = ras->pixels(y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; } // ----- riempie "calotta" circolare superiore yMin = tmax(b.y + cutExt + 1, 0); // clipping y yMax = tmin(b.y + radius, ly - 1); // clipping y for (y = yMin; y <= yMax; y++) { int r = halfCord.getCord(y - b.y); int xMin = tmax(b.x - r, 0); // clipping x int xMax = tmin(b.x + r, lx - 1); // clipping x TPixel32 *p = ras->pixels(y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; } // ----- riempie trapezoidi // (se k<0 viene fatta una riflessione sulle ascisse prima di tornare alle // coordinate "di schermo") // limite destro assoluto delle scanline trapezoide: int xSegmMax = tround(dx - up.x); // coordinata x del punto di tangenza inferiore sul cerchio superiore // limite sinistro assoluto delle scanline: int xSegmMin = tround(up.x); // coordinata x del punto di tangenza superiore sul cerchio inferiore // ----- riempie trapezoide inferiore // N.B. le coordinate sono relative ad un sist. di rif. con l'origine sul centro // del cerchio inferiore yMin = tmax(a.y - cutExt, 0) - a.y; // clipping y yMax = tmin(a.y + cutIn, b.y - cutIn - 1, ly - 1) - a.y; // clipping y // l'eq. della retta e' alpha * x + beta * y + gammaRight = 0 const int alpha = dy, beta = -dx; const double gammaRight = rightDown.y * dx - rightDown.x * dy; const int incE = alpha; const int incNE = alpha + beta; const int incN = beta; if (m <= 1) { // midpoint algorithm; le scanline vengono disegnate solo // sul NordEst. L'ultima scanline non viene disegnata TPoint segmRight(tceil((yMin + 0.5 - rightDown.y) / mParall + rightDown.x) - 1, yMin); int dSegmRight = tfloor(alpha * (segmRight.x + 1) + beta * (segmRight.y + 0.5) + gammaRight); while (segmRight.y <= yMax) { if (dSegmRight < 0) // Est { dSegmRight = dSegmRight + incE; segmRight.x++; } else // NordEst { int xMin, xMax; if (k > 0) { xMin = tmax(a.x - halfCord.getCord(abs(segmRight.y)), 0); // clipping x xMax = tmin(a.x + tmin(segmRight.x, xSegmMax), lx - 1); // clipping x } else { xMin = tmax(a.x - tmin(segmRight.x, xSegmMax), 0); // clipping x + ritorno alle xMax = tmin(a.x + halfCord.getCord(abs(segmRight.y)), lx - 1); // coordinate "di schermo" } TPixel32 *p = ras->pixels(segmRight.y + a.y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; dSegmRight = dSegmRight + incNE; segmRight.x++; segmRight.y++; } } } else // m>1 { // midpoint algorithm; le scanline vengono disegnate sempre TPoint segmRight(tround((yMin - rightDown.y) / mParall + rightDown.x), yMin); int dSegmRight = tfloor(alpha * (segmRight.x + 0.5) + beta * (segmRight.y + 1) + gammaRight); while (segmRight.y <= yMax) { int xMin, xMax; if (k > 0) { xMin = tmax(a.x - halfCord.getCord(abs(segmRight.y)), 0); // clipping x xMax = tmin(a.x + segmRight.x, lx - 1); // clipping x } else { xMin = tmax(a.x - segmRight.x, 0); // clipping x + ritorno alle coordinate xMax = tmin(a.x + halfCord.getCord(abs(segmRight.y)), lx - 1); // "di schermo" } TPixel32 *p = ras->pixels(segmRight.y + a.y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; if (dSegmRight <= 0) // NordEst { dSegmRight = dSegmRight + incNE; segmRight.x++; } else // Nord { dSegmRight = dSegmRight + incN; } segmRight.y++; } } // ----- riempie trapezoide superiore // N.B. le coordinate sono relative ad un sist. di rif. con l'origine sul centro // del cerchio superiore yMin = tmax(b.y - cutIn, a.y + cutIn + 1, 0) - b.y; // clipping y yMax = tmin(b.y + cutExt, ly - 1) - b.y; // clipping y // l'eq. della retta e' alpha * x + beta * y + gammaLeft = 0 const double gammaLeft = leftDown.y * dx - leftDown.x * dy; if (m <= 1) { // midpoint algorithm; le scanline vengono disegnate solo // sul NordEst. L'ultima scanline non viene disegnata TPoint segmLeft(tceil((yMin - 0.5 - leftDown.y) / mParall + leftDown.x), yMin); int dSegmLeft = tfloor(alpha * (segmLeft.x + 1) + beta * (segmLeft.y + 0.5) + gammaLeft); while (segmLeft.y <= yMax) { int xMin, xMax; if (k > 0) { xMin = tmax(b.x + tmax(segmLeft.x, xSegmMin - dx), 0); // clipping x xMax = tmin(b.x + halfCord.getCord(abs(segmLeft.y)), lx - 1); // clipping x } else { xMin = tmax(b.x - halfCord.getCord(abs(segmLeft.y)), 0); // clipping x + ritorno alle xMax = tmin(b.x - tmax(segmLeft.x, xSegmMin - dx), lx - 1); // coordinate "di schermo" } TPixel32 *p = ras->pixels(segmLeft.y + b.y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; while (dSegmLeft < 0) { dSegmLeft = dSegmLeft + incE; segmLeft.x++; } dSegmLeft = dSegmLeft + incNE; segmLeft.x++; segmLeft.y++; } } else // m>1 { // midpoint algorithm; le scanline vengono disegnate sempre TPoint segmLeft(tround((yMin - leftDown.y) / mParall + leftDown.x), yMin); int dSegmLeft = tfloor(alpha * (segmLeft.x + 0.5) + beta * (segmLeft.y + 1) + gammaLeft); while (segmLeft.y <= yMax) { int xMin, xMax; if (k > 0) { xMin = tmax(b.x + segmLeft.x, 0); // clipping x xMax = tmin(b.x + halfCord.getCord(abs(segmLeft.y)), lx - 1); // clipping x } else { xMin = tmax(b.x - halfCord.getCord(abs(segmLeft.y)), 0); // clipping x + ritorno alle xMax = tmin(b.x - segmLeft.x, lx - 1); // coordinate "di schermo" } TPixel32 *p = ras->pixels(segmLeft.y + b.y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; if (dSegmLeft <= 0) // NordEst { dSegmLeft = dSegmLeft + incNE; segmLeft.x++; } else // Nord { dSegmLeft = dSegmLeft + incN; } segmLeft.y++; } } // ----- parallelogramma (in alternativa a "parallelogrammoide circolare") // N.B. le coordinate sono relative ad un sist. di rif. con l'origine sul centro // del cerchio inferiore // retta destra di equaz. alpha * x + beta * y + gammaRight = 0 // retta sinistra di equaz. alpha * x + beta * y + gammaLeft = 0 yMin = tmax(a.y + cutIn + 1, 0) - a.y; //clipping y yMax = tmin(b.y - cutIn - 1, ly - 1) - a.y; //clipping y if (m <= 1) { // midpoint algorithm; le scanline vengono disegnate solo // sul NordEst. L'ultima scanline non viene disegnata TPoint segmRight(tceil((yMin + 0.5 - rightDown.y) / mParall + rightDown.x) - 1, yMin); TPoint segmLeft = TPoint(tceil((yMin - 0.5 - leftDown.y) / mParall + leftDown.x), yMin); int dSegmRight = tfloor(alpha * (segmRight.x + 1) + beta * (segmRight.y + 0.5) + gammaRight); int dSegmLeft = tfloor(alpha * (segmLeft.x + 1) + beta * (segmLeft.y + 0.5) + gammaLeft); while (segmRight.y <= yMax) { if (dSegmRight < 0) // segmRight a Est { dSegmRight = dSegmRight + incE; segmRight.x++; } else // segmRight a NordEst { int xMin, xMax; if (k > 0) { xMin = tmax(a.x + tmax(segmLeft.x, xSegmMin), 0); // clipping x xMax = tmin(a.x + tmin(segmRight.x, xSegmMax), lx - 1); // clipping x } else { xMin = tmax(a.x - tmin(segmRight.x, xSegmMax), 0); // clipping x + ritorno alle xMax = tmin(a.x - tmax(segmLeft.x, xSegmMin), lx - 1); // coordinate "di schermo" } TPixel32 *p = ras->pixels(segmRight.y + a.y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; dSegmRight = dSegmRight + incNE; segmRight.x++; segmRight.y++; while (dSegmLeft < 0) // segmLeft a Est { dSegmLeft = dSegmLeft + incE; segmLeft.x++; } // segmLeft a NordEst dSegmLeft = dSegmLeft + incNE; segmLeft.x++; segmLeft.y++; } } } else // m>1 { // midpoint algorithm; le scanline vengono disegnate sempre TPoint segmRight(tround((yMin - rightDown.y) / mParall + rightDown.x), yMin); TPoint segmLeft(tround((yMin - leftDown.y) / mParall + leftDown.x), yMin); int dSegmRight = tfloor(alpha * (segmRight.x + 0.5) + beta * (segmRight.y + 1) + gammaRight); int dSegmLeft = tfloor(alpha * (segmLeft.x + 0.5) + beta * (segmLeft.y + 1) + gammaLeft); while (segmRight.y <= yMax) { int xMin, xMax; if (k > 0) { xMin = tmax(a.x + segmLeft.x, 0); // clipping x xMax = tmin(a.x + segmRight.x, lx - 1); // clipping x } else { xMin = tmax(a.x - segmRight.x, 0); // clipping x + ritorno alle xMax = tmin(a.x - segmLeft.x, lx - 1); // coordinate "di schermo" } TPixel32 *p = ras->pixels(segmRight.y + a.y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; if (dSegmRight <= 0) // segmRight a NordEst { dSegmRight = dSegmRight + incNE; segmRight.x++; } else // segmRight a Nord { dSegmRight = dSegmRight + incN; } segmRight.y++; if (dSegmLeft <= 0) // segmLeft a NordEst { dSegmLeft = dSegmLeft + incNE; segmLeft.x++; } else // segmLeft a Nord { dSegmLeft = dSegmLeft + incN; } } } // ---- parallelogrammoide circolare (in alternativa a parallelogramma) // N.B. coordinate di schermo (riflessione per k<0 ) yMin = tmax(b.y - cutIn, 0); yMax = tmin(a.y + cutIn, ly - 1); for (y = yMin; y <= yMax; y++) { int xMin, xMax; if (k > 0) { xMin = tmax(a.x - halfCord.getCord(abs(y - a.y)), 0); // clipping x xMax = tmin(b.x + halfCord.getCord(abs(b.y - y)), lx - 1); // clipping x } else { xMin = tmax(b.x - halfCord.getCord(abs(b.y - y)), 0); // clipping x + ritorno alle xMax = tmin(a.x + halfCord.getCord(abs(y - a.y)), lx - 1); // coordinate "di schermo" } TPixel32 *p = ras->pixels(y) + xMin; TPixel32 *q = p + (xMax - xMin); while (p <= q) *p++ = col; } ras->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) {
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); } } }