//Returns the lattice cell containing the relative position pos. inline TPoint TCacheResource::getCellPos(const TPointD &pos) const { TPoint cellIndex( tfloor(pos.x / (double)latticeStep), tfloor(pos.y / (double)latticeStep)); return TPoint(cellIndex.x * latticeStep, cellIndex.y * latticeStep); }
TColor TPatternParquet::evaluate (const TVector& rktPOINT) const { TScalar tValue; TColor tRet; TScalar tX = rktPOINT.x()/3; TScalar tY = rktPOINT.y()/3; TScalar tZ = rktPOINT.z()/3; int iX = ((int) tfloor (rktPOINT.x() / 8.0)) & 7; int iZ = ((int) tfloor (rktPOINT.z() / 8.0)) & 7; int iT = akiLookup [ iX ][ iZ ]; TScalar tT = (TScalar) iT; if (iT & 1) { tValue = tWood.wood (tZ, tY + tT / 3, tX, 15 + tT/2, 1); } else { tValue = tWood.wood (tX, tY + tT / 3, tZ, 15 + tT/2, 1); } tValue = clamp (tValue, 0, 1); tRet = lerp (tColor, tBaseColor, tValue); srand (iT << 2); tRet *= 1.0 - frand() * 0.4; return tRet; } /* evaluate() */
void PlaneViewer::draw(TVectorImageP vi) { TRectD bbox(vi->getBBox()); TRect bboxI(tfloor(bbox.x0), tfloor(bbox.y0), tceil(bbox.x1) - 1, tceil(bbox.y1) - 1); TVectorRenderData rd(TAffine(), bboxI, vi->getPalette(), 0, true, true); tglDraw(rd, vi.getPointer()); }
bool TCacheResource::checkTile(const TTile &tile) const { //Ensure that tile has integer geoometry. TPointD tileFracPos(tile.m_pos.x - tfloor(tile.m_pos.x), tile.m_pos.y - tfloor(tile.m_pos.y)); if (tileFracPos.x != 0.0 || tileFracPos.y != 0.0) { assert(!"The passed tile must have integer geometry!"); return false; } return true; }
void TRop::over(const TRasterP &out, const TRasterP &up, const TPoint &pos, const TAffine &aff, ResampleFilterType filterType) { if (aff.isIdentity()) //simple over with offset TRop::over(out, up, pos); else { TRect rasterBounds = up->getBounds(); TRectD dbounds(rasterBounds.x0, rasterBounds.y0, rasterBounds.x1, rasterBounds.y1); dbounds = aff * dbounds; TRect bounds(tfloor(dbounds.x0), tfloor(dbounds.y0), tceil(dbounds.x1), tceil(dbounds.y1)); TRasterP tmp = up->create(bounds.getLx(), bounds.getLy()); resample(tmp, up, TTranslation(-dbounds.getP00()) * aff, filterType); TRop::over(out, tmp, pos); } }
TScalar TWood::wood (const TScalar ktX, const TScalar ktY, const TScalar ktZ, const TScalar ktRINGSCALE, const TScalar ktGRAINFACTOR) const { TVector tQ; TScalar tT; TScalar tR; TScalar tS; tT = ktZ / ktRINGSCALE; tQ = TVector (ktX * 8.0, ktY * 8.0, ktZ); tT += tNoise.noise (tQ) / 16.0; tQ = TVector (ktX, tT, ktY + 12.93); tR = ktRINGSCALE * tNoise.noise (tQ); tR -= tfloor (tR); tR = 0.2 + 0.8 * smoothstep (0.2, 0.55, tR) * (1.0 - smoothstep (0.75, 0.8, tR)); tQ = TVector (ktX * 128.0 + 5.0, ktZ * 8.0 - 3.0, ktY * 128.0 + 1); tS = ktGRAINFACTOR * (1.3 - tNoise.noise (tQ)) + (1.0 - ktGRAINFACTOR); return tR * tS * tS; } /* wood */
//! Adapts image viewer's affine to display the passed viewer rect at maximized //! ratio void ImageViewer::adaptView(const QRect &geomRect) { if (!m_image) return; // Retrieve the rect in image reference and call the associated adaptView TRect imgBounds(getImageBounds(m_image)); TRectD imgBoundsD(imgBounds.x0, imgBounds.y0, imgBounds.x1 + 1, imgBounds.y1 + 1); TRectD geomRectD(geomRect.left(), geomRect.top(), geomRect.right() + 1, geomRect.bottom() + 1); TRectD viewRectD(getImgToWidgetAffine().inv() * geomRectD); TRect viewRect(tfloor(viewRectD.x0), tfloor(viewRectD.y0), tceil(viewRectD.x1) - 1, tceil(viewRectD.y1) - 1); adaptView(imgBounds, viewRect); }
static D jtremdd(J jt,D a,D b){D q,x,y; if(!a)R b; ASSERT(!INF(b),EVNAN); if(a==inf )R 0<=b?b:a; if(a==infm)R 0>=b?b:a; q=b/a; x=tfloor(q); y=tceil(q); R teq(x,y)?0:b-a*x; }
void onDeliver() { if (m_error) { m_error = false; MsgBox(DVGui::CRITICAL, QObject::tr("There was an error saving frames for the %1 level.").arg(QString::fromStdWString(m_fp.withoutParentDir().getWideString()))); } bool isPreview = (m_fp.getType() == "noext"); TImageCache::instance()->remove(toString(m_fp.getWideString() + L".0")); TNotifier::instance()->notify(TSceneNameChange()); if (Preferences::instance()->isGeneratedMovieViewEnabled()) { if (!isPreview && (Preferences::instance()->isDefaultViewerEnabled()) && (m_fp.getType() == "mov" || m_fp.getType() == "avi" || m_fp.getType() == "3gp")) { QString name = QString::fromStdString(m_fp.getName()); int index; if ((index = name.indexOf("#RENDERID")) != -1) //!quite ugly I know.... m_fp = m_fp.withName(name.left(index).toStdWString()); if (!TSystem::showDocument(m_fp)) { QString msg(QObject::tr("It is not possible to display the file %1: no player associated with its format").arg(QString::fromStdWString(m_fp.withoutParentDir().getWideString()))); MsgBox(WARNING, msg); } } else { int r0, r1, step; TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); TOutputProperties &outputSettings = isPreview ? *scene->getProperties()->getPreviewProperties() : *scene->getProperties()->getOutputProperties(); outputSettings.getRange(r0, r1, step); const TRenderSettings rs = outputSettings.getRenderSettings(); if (r0 == 0 && r1 == -1) r0 = 0, r1 = scene->getFrameCount() - 1; double timeStretchFactor = isPreview ? 1.0 : (double)outputSettings.getRenderSettings().m_timeStretchTo / outputSettings.getRenderSettings().m_timeStretchFrom; r0 = tfloor(r0 * timeStretchFactor); r1 = tceil((r1 + 1) * timeStretchFactor) - 1; TXsheet::SoundProperties *prop = new TXsheet::SoundProperties(); prop->m_frameRate = outputSettings.getFrameRate(); TSoundTrack *snd = app->getCurrentXsheet()->getXsheet()->makeSound(prop); if (outputSettings.getRenderSettings().m_stereoscopic) { assert(!isPreview); ::viewFile(m_fp.withName(m_fp.getName() + "_l"), r0 + 1, r1 + 1, step, isPreview ? rs.m_shrinkX : 1, snd, 0, false, true); ::viewFile(m_fp.withName(m_fp.getName() + "_r"), r0 + 1, r1 + 1, step, isPreview ? rs.m_shrinkX : 1, snd, 0, false, true); } else ::viewFile(m_fp, r0 + 1, r1 + 1, step, isPreview ? rs.m_shrinkX : 1, snd, 0, false, true); } } }
void TRop::fracmove(TRasterP rout, TRasterCM32P rin, const TPaletteP &palette, double dx, double dy) { double w[4] = {1, 0, 0, 0}; double sum = 0; int idx = tfloor(dx); int idy = tfloor(dy); double fracX = dx - idx; double fracY = dy - idy; int i, j; for (i = 0; i < 2; ++i) for (j = 0; j < 2; ++j) sum += w[j + 2 * i] = fabs(fracX - j) * fabs(fracY - i); for (i = 0; i < 4; ++i) w[i] /= sum; TRop::convolve_i(rout, rin, idx, idy, w, 2); }
void TRop::fracmove(TRasterP rout, TRasterP rin, double dx, double dy) { //Using a bilinear filter is best - consider that only 4 pixels should contribute //to a fractionarily shifted one, with weights proportional to the intersection areas. double w[4] = {1, 0, 0, 0}; double sum = 0; int idx = tfloor(dx); int idy = tfloor(dy); double fracX = dx - idx; double fracY = dy - idy; int i, j; for (i = 0; i < 2; ++i) for (j = 0; j < 2; ++j) sum += w[j + 2 * i] = fabs(fracX - j) * fabs(fracY - i); for (i = 0; i < 4; ++i) w[i] /= sum; TRop::convolve_i(rout, rin, idx, idy, w, 2); }
void TRop::over(const TRasterP &out, const TRasterP &up, const TAffine &aff, ResampleFilterType filterType) { out->lock(); up->lock(); if (filterType == ClosestPixel || filterType == Bilinear) ::quickPut(out, up, aff, filterType); else { TRect rasterBounds = up->getBounds(); TRectD dbounds(rasterBounds.x0, rasterBounds.y0, rasterBounds.x1 + 1, rasterBounds.y1 + 1); dbounds = aff * dbounds; TRect bounds(tfloor(dbounds.x0), tfloor(dbounds.y0), tceil(dbounds.x1) - 1, tceil(dbounds.y1) - 1); TRasterP tmp = up->create(bounds.getLx(), bounds.getLy()); resample(tmp, up, TTranslation(-bounds.x0, -bounds.y0) * aff, filterType); over(out, tmp, bounds.getP00()); } out->unlock(); up->unlock(); }
void PlaneViewer::drawBackground() { glClearColor(m_bgColorF[0], m_bgColorF[1], m_bgColorF[2], 1.0); glClear(GL_COLOR_BUFFER_BIT); if (m_bgColorF[0] != m_bgColorF[3] || m_bgColorF[1] != m_bgColorF[4] || m_bgColorF[2] != m_bgColorF[5]) { // Cast the widget rect to world rect TRectD rect(winToWorld(0, 0), winToWorld(width(), height())); // Deduce chess geometry TRect chessRect(tfloor(rect.x0 / m_chessSize), tfloor(rect.y0 / m_chessSize), tceil(rect.x1 / m_chessSize), tceil(rect.y1 / m_chessSize)); // Draw chess squares glColor3f(m_bgColorF[3], m_bgColorF[4], m_bgColorF[5]); glBegin(GL_QUADS); int x, y; TPointD pos; double chessSize2 = 2.0 * m_chessSize; for (y = chessRect.y0; y < chessRect.y1; ++y) { pos.y = y * m_chessSize; for (x = chessRect.x0 + ((chessRect.x0 + y) % 2), pos.x = x * m_chessSize; x < chessRect.x1; x += 2, pos.x += chessSize2) { glVertex2d(pos.x, pos.y); glVertex2d(pos.x + m_chessSize, pos.y); glVertex2d(pos.x + m_chessSize, pos.y + m_chessSize); glVertex2d(pos.x, pos.y + m_chessSize); } } glEnd(); } }
TRect TRasterImageUtils::convertWorldToRaster(const TRectD &area, const TRasterImageP ri) { if (area.isEmpty()) return TRect(); if (!ri || !ri->getRaster()) return TRect(tfloor(area.x0), tfloor(area.y0), tfloor(area.x1) - 1, tfloor(area.y1) - 1); TRasterP ras = ri->getRaster(); TRectD rect(area + ras->getCenterD()); return TRect(tfloor(rect.x0), tfloor(rect.y0), tceil(rect.x1) - 1, tceil(rect.y1) - 1); }
inline std::string TCacheResource::getCellCacheId(const TPoint &cellPos) const { return getCellCacheId(tfloor(cellPos.x / (double)latticeStep), tfloor(cellPos.y / (double)latticeStep)); }
void doCompute(TTile &tile, double frame, const TRenderSettings &info) { bool isWarped = m_warped.isConnected(); if (!isWarped) return; if (fabs(m_intensity->getValue(frame)) < 0.01) { m_warped->compute(tile, frame, info); return; } int shrink = (info.m_shrinkX + info.m_shrinkY) / 2; double scale = sqrt(fabs(info.m_affine.det())); double gridStep = 1.5 * m_gridStep->getValue(frame); WarpParams params; params.m_intensity = m_intensity->getValue(frame) / gridStep; params.m_warperScale = scale * gridStep; params.m_sharpen = m_sharpen->getValue(); params.m_shrink = shrink; double evolution = m_evol->getValue(frame); double size = 100.0 / info.m_shrinkX; TPointD pos(m_posx->getValue(frame), m_posy->getValue(frame)); //The warper is calculated on a standard reference, with fixed dpi. This makes sure //that the lattice created for the warp does not depend on camera transforms and resolution. TRenderSettings warperInfo(info); double warperScaleFactor = 1.0 / params.m_warperScale; warperInfo.m_affine = TScale(warperScaleFactor) * info.m_affine; //Retrieve tile's geometry TRectD tileRect; { TRasterP tileRas = tile.getRaster(); tileRect = TRectD(tile.m_pos, TDimensionD(tileRas->getLx(), tileRas->getLy())); } //Build the compute rect TRectD warpedBox, warpedComputeRect, tileComputeRect; m_warped->getBBox(frame, warpedBox, info); getWarpComputeRects(tileComputeRect, warpedComputeRect, warpedBox, tileRect, params); if (tileComputeRect.getLx() <= 0 || tileComputeRect.getLy() <= 0) return; if (warpedComputeRect.getLx() <= 0 || warpedComputeRect.getLy() <= 0) return; TRectD warperComputeRect(TScale(warperScaleFactor) * tileComputeRect); double warperEnlargement = getWarperEnlargement(params); warperComputeRect = warperComputeRect.enlarge(warperEnlargement); warperComputeRect.x0 = tfloor(warperComputeRect.x0); warperComputeRect.y0 = tfloor(warperComputeRect.y0); warperComputeRect.x1 = tceil(warperComputeRect.x1); warperComputeRect.y1 = tceil(warperComputeRect.y1); //Compute the warped tile TTile tileIn; m_warped->allocateAndCompute(tileIn, warpedComputeRect.getP00(), TDimension(warpedComputeRect.getLx(), warpedComputeRect.getLy()), tile.getRaster(), frame, info); TRasterP rasIn = tileIn.getRaster(); //Compute the warper tile TSpectrum::ColorKey colors[] = { TSpectrum::ColorKey(0, TPixel32::White), TSpectrum::ColorKey(1, TPixel32::Black)}; TSpectrumParamP cloudscolors = TSpectrumParamP(tArrayCount(colors), colors); //Build the warper warperInfo.m_affine = warperInfo.m_affine; TAffine aff = warperInfo.m_affine.inv(); TTile warperTile; TRasterP rasWarper = rasIn->create(warperComputeRect.getLx(), warperComputeRect.getLy()); warperTile.m_pos = warperComputeRect.getP00(); warperTile.setRaster(rasWarper); { TRenderSettings info2(warperInfo); //Now, separate the part of the affine the Fx can handle from the rest. TAffine fxHandledAffine = handledAffine(warperInfo, frame); info2.m_affine = fxHandledAffine; TAffine aff = warperInfo.m_affine * fxHandledAffine.inv(); aff.a13 /= warperInfo.m_shrinkX; aff.a23 /= warperInfo.m_shrinkY; TRectD rectIn = aff.inv() * warperComputeRect; //rectIn = rectIn.enlarge(getResampleFilterRadius(info)); //Needed to counter the resample filter TRect rectInI(tfloor(rectIn.x0), tfloor(rectIn.y0), tceil(rectIn.x1) - 1, tceil(rectIn.y1) - 1); // rasIn e' un raster dello stesso tipo di tile.getRaster() TTile auxtile(warperTile.getRaster()->create(rectInI.getLx(), rectInI.getLy()), convert(rectInI.getP00())); TPointD mypos(auxtile.m_pos - pos); double scale2 = sqrt(fabs(info2.m_affine.det())); doClouds(auxtile.getRaster(), cloudscolors, mypos, evolution, size, 0.0, 1.0, PNOISE_CLOUDS, scale2, frame); info2.m_affine = aff; TRasterFx::applyAffine(warperTile, auxtile, info2); } //Warp TPointD db; TRect rasComputeRectI(convert(tileComputeRect - tileRect.getP00(), db)); TRasterP tileRas = tile.getRaster()->extract(rasComputeRectI); TPointD rasInPos(warpedComputeRect.getP00() - tileComputeRect.getP00()); TPointD warperPos((TScale(params.m_warperScale) * warperComputeRect.getP00()) - tileComputeRect.getP00()); warp(tileRas, rasIn, rasWarper, rasInPos, warperPos, params); }
void doCompute(TTile &tile, double frame, const TRenderSettings &info) override { bool isWarped = m_warped.isConnected(); if (!isWarped) return; if (fabs(m_intensity->getValue(frame)) < 0.01) { m_warped->compute(tile, frame, info); return; } int shrink = (info.m_shrinkX + info.m_shrinkY) / 2; double scale = sqrt(fabs(info.m_affine.det())); double gridStep = 1.5 * m_gridStep->getValue(frame); WarpParams params; params.m_intensity = m_intensity->getValue(frame) / gridStep; params.m_warperScale = scale * gridStep; params.m_sharpen = m_sharpen->getValue(); params.m_shrink = shrink; double period = m_period->getValue(frame) / info.m_shrinkX; double count = m_count->getValue(frame); double cycle = m_cycle->getValue(frame) / info.m_shrinkX; double scaleX = m_scaleX->getValue(frame) / 100.0; double scaleY = m_scaleY->getValue(frame) / 100.0; double angle = -m_angle->getValue(frame); TPointD center = m_center->getValue(frame) * (1.0 / info.m_shrinkX); // The warper is calculated on a standard reference, with fixed dpi. This // makes sure // that the lattice created for the warp does not depend on camera // transforms and resolution. TRenderSettings warperInfo(info); double warperScaleFactor = 1.0 / params.m_warperScale; warperInfo.m_affine = TScale(warperScaleFactor) * info.m_affine; // Retrieve tile's geometry TRectD tileRect; { TRasterP tileRas = tile.getRaster(); tileRect = TRectD(tile.m_pos, TDimensionD(tileRas->getLx(), tileRas->getLy())); } // Build the compute rect TRectD warpedBox, warpedComputeRect, tileComputeRect; m_warped->getBBox(frame, warpedBox, info); getWarpComputeRects(tileComputeRect, warpedComputeRect, warpedBox, tileRect, params); if (tileComputeRect.getLx() <= 0 || tileComputeRect.getLy() <= 0) return; if (warpedComputeRect.getLx() <= 0 || warpedComputeRect.getLy() <= 0) return; TRectD warperComputeRect(TScale(warperScaleFactor) * tileComputeRect); double warperEnlargement = getWarperEnlargement(params); warperComputeRect = warperComputeRect.enlarge(warperEnlargement); warperComputeRect.x0 = tfloor(warperComputeRect.x0); warperComputeRect.y0 = tfloor(warperComputeRect.y0); warperComputeRect.x1 = tceil(warperComputeRect.x1); warperComputeRect.y1 = tceil(warperComputeRect.y1); // Compute the warped tile TTile tileIn; m_warped->allocateAndCompute( tileIn, warpedComputeRect.getP00(), TDimension(warpedComputeRect.getLx(), warpedComputeRect.getLy()), tile.getRaster(), frame, info); TRasterP rasIn = tileIn.getRaster(); // Compute the warper tile TSpectrum::ColorKey colors[] = {TSpectrum::ColorKey(0, TPixel32::White), TSpectrum::ColorKey(0.5, TPixel32::Black), TSpectrum::ColorKey(1, TPixel32::White)}; TSpectrumParamP ripplecolors = TSpectrumParamP(tArrayCount(colors), colors); // Build the multiradial warperInfo.m_affine = warperInfo.m_affine * TTranslation(center) * TRotation(angle) * TScale(scaleX, scaleY); TAffine aff = warperInfo.m_affine.inv(); TPointD posTrasf = aff * (warperComputeRect.getP00()); TRasterP rasWarper = rasIn->create(warperComputeRect.getLx(), warperComputeRect.getLy()); multiRadial(rasWarper, posTrasf, ripplecolors, period, count, cycle, aff, frame); // TImageWriter::save(TFilePath("C:\\ripple.tif"), rasWarper); // Warp TPointD db; TRect rasComputeRectI(convert(tileComputeRect - tileRect.getP00(), db)); TRasterP tileRas = tile.getRaster()->extract(rasComputeRectI); TPointD rasInPos(warpedComputeRect.getP00() - tileComputeRect.getP00()); TPointD warperPos( (TScale(params.m_warperScale) * warperComputeRect.getP00()) - tileComputeRect.getP00()); warp(tileRas, rasIn, rasWarper, rasInPos, warperPos, params); }
inline TCacheResource::PointLess TCacheResource::getCellIndex(const TPoint &pos) const { return PointLess( tfloor(pos.x / (double)latticeStep), tfloor(pos.y / (double)latticeStep)); }
bool RenderCommand::init(bool isPreview) { ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); TSceneProperties *sprop = scene->getProperties(); /*-- Preview/Renderに応じてそれぞれのSettingを取得 --*/ TOutputProperties &outputSettings = isPreview ? *sprop->getPreviewProperties() : *sprop->getOutputProperties(); outputSettings.getRange(m_r0, m_r1, m_step); /*-- シーン全体のレンダリングの場合、m_r1をScene長に設定 --*/ if (m_r0 == 0 && m_r1 == -1) { m_r0 = 0; m_r1 = scene->getFrameCount() - 1; } if (m_r0 < 0) m_r0 = 0; if (m_r1 >= scene->getFrameCount()) m_r1 = scene->getFrameCount() - 1; if (m_r1 < m_r0) { MsgBox(WARNING, QObject::tr("The command cannot be executed because the scene is empty.")); return false; // throw TException("empty scene"); // non so perche', ma termina il programma // nonostante il try all'inizio } // Initialize the preview case /*TRenderSettings rs = sprop->getPreviewProperties()->getRenderSettings(); TRenderSettings rso = sprop->getOutputProperties()->getRenderSettings(); rs.m_stereoscopic=true; rs.m_stereoscopicShift=0.05; rso.m_stereoscopic=true; rso.m_stereoscopicShift=0.05; sprop->getPreviewProperties()->setRenderSettings(rs); sprop->getOutputProperties()->setRenderSettings(rso);*/ if (isPreview) { /*-- PreviewではTimeStretchを考慮しないので、そのままフレーム値を格納してゆく --*/ m_numFrames = (int)(m_r1 - m_r0 + 1); m_r = m_r0; m_stepd = m_step; m_multimediaRender = 0; return true; } // Full render case // Read the output filepath TFilePath fp = outputSettings.getPath(); /*-- ファイル名が指定されていない場合は、シーン名を出力ファイル名にする --*/ if (fp.getWideName() == L"") fp = fp.withName(scene->getScenePath().getName()); /*-- ラスタ画像の場合、ファイル名にフレーム番号を追加 --*/ if (TFileType::getInfo(fp) == TFileType::RASTER_IMAGE || fp.getType() == "pct" || fp.getType() == "pic" || fp.getType() == "pict") //pct e' un formato"livello" (ha i settings di quicktime) ma fatto di diversi frames fp = fp.withFrame(TFrameId::EMPTY_FRAME); fp = scene->decodeFilePath(fp); if (!TFileStatus(fp.getParentDir()).doesExist()) { try { TFilePath parent = fp.getParentDir(); TSystem::mkDir(parent); DvDirModel::instance()->refreshFolder(parent.getParentDir()); } catch (TException &e) { MsgBox(WARNING, QObject::tr("It is not possible to create folder : %1").arg(QString::fromStdString(toString(e.getMessage())))); return false; } catch (...) { MsgBox(WARNING, QObject::tr("It is not possible to create a folder.")); return false; } } m_fp = fp; // Retrieve camera infos const TCamera *camera = isPreview ? scene->getCurrentPreviewCamera() : scene->getCurrentCamera(); TDimension cameraSize = camera->getRes(); TPointD cameraDpi = camera->getDpi(); // Retrieve render interval/step/times double stretchTo = (double)outputSettings.getRenderSettings().m_timeStretchTo; double stretchFrom = (double)outputSettings.getRenderSettings().m_timeStretchFrom; m_timeStretchFactor = stretchTo / stretchFrom; m_stepd = m_step / m_timeStretchFactor; int stretchedR0 = tfloor(m_r0 * m_timeStretchFactor); int stretchedR1 = tceil((m_r1 + 1) * m_timeStretchFactor) - 1; m_r = stretchedR0 / m_timeStretchFactor; m_numFrames = (stretchedR1 - stretchedR0) / m_step + 1; // Update the multimedia render switch m_multimediaRender = outputSettings.getMultimediaRendering(); return true; }
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(); }