コード例 #1
0
ファイル: unaryFx.cpp プロジェクト: Makoto-Sasahara/opentoonz
bool TGeometryFx::doGetBBox(double frame, TRectD &bBox,
                            const TRenderSettings &info) {
  TRasterFxPort *input = dynamic_cast<TRasterFxPort *>(getInputPort(0));
  assert(input);

  if (input->isConnected()) {
    TRasterFxP fx = input->getFx();
    assert(fx);
    bool ret = fx->doGetBBox(frame, bBox, info);
    if (getActiveTimeRegion().contains(frame))
      bBox = getPlacement(frame) * bBox;
    return ret;
  } else {
    bBox = TRectD();
    return false;
  }
  return true;
};
コード例 #2
0
void FreeDistortBaseFx::doDryCompute(TRectD &rect, double frame, const TRenderSettings &info)
{
	if (!m_input.isConnected())
		return;

	if (m_deactivate->getValue()) {
		m_input->dryCompute(rect, frame, info);
		return;
	}

	TRectD rectOnInput;
	TRenderSettings infoOnInput;
	TRectD inBBox;

	safeTransform(frame, 0, rect, info, rectOnInput, infoOnInput, inBBox);

	rectOnInput *= inBBox;

	if (!myIsEmpty(rectOnInput))
		m_input->dryCompute(rectOnInput, frame, infoOnInput);
}
コード例 #3
0
int FreeDistortBaseFx::getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info)
{
	if (!m_input.isConnected())
		return 0;

	TRectD inRect;
	TRenderSettings riNew;
	TRectD inBBox;
	safeTransform(frame, 0, rect, info, inRect, riNew, inBBox);
	inRect *= inBBox;

	return TRasterFx::memorySize(inRect, riNew.m_bpp);
}
コード例 #4
0
ファイル: unaryFx.cpp プロジェクト: Makoto-Sasahara/opentoonz
void TGeometryFx::doCompute(TTile &tile, double frame,
                            const TRenderSettings &ri) {
  TRasterFxPort *input = dynamic_cast<TRasterFxPort *>(getInputPort(0));
  assert(input);

  if (!input->isConnected()) return;

  if (!getActiveTimeRegion().contains(frame)) {
    TRasterFxP(input->getFx())->compute(tile, frame, ri);
    return;
  }

  if (!TRaster32P(tile.getRaster()) && !TRaster64P(tile.getRaster()))
    throw TException("AffineFx unsupported pixel type");

  TAffine aff1 = getPlacement(frame);
  TRenderSettings ri2(ri);
  ri2.m_affine = ri2.m_affine * aff1;

  TRasterFxP src = getInputPort("source")->getFx();
  src->compute(tile, frame, ri2);
  return;
}
コード例 #5
0
bool TExternalProgramFx::doGetBBox(double frame, TRectD &bBox,
                                   const TRenderSettings &info) {
  // bBox = TRectD(-30,-30,30,30);
  //  return true;

  std::map<std::string, Port>::const_iterator portIt;
  for (portIt = m_ports.begin(); portIt != m_ports.end(); ++portIt) {
    if (portIt->second.m_port != 0) {
      TRasterFxPort *tmp;
      tmp = portIt->second.m_port;
      if (tmp->isConnected()) {
        TRectD tmpbBox;
        (*tmp)->doGetBBox(frame, tmpbBox, info);
        bBox += tmpbBox;
      }
    }
  }

  if (bBox.isEmpty()) {
    bBox = TRectD();
    return false;
  } else
    return true;
  /*
if(m_input1.isConnected() || m_input2.isConnected())
{
bool ret = m_input1->doGetBBox(frame, bBox) || m_input1->doGetBBox(frame, bBox);
return ret;
}
else
{
bBox = TRectD();
return false;
}
*/
}
コード例 #6
0
ファイル: iwa_particlesfx.cpp プロジェクト: CroW-CZ/opentoonz
void Iwa_TiledParticlesFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri)
{
	std::vector<int> lastframe;
	std::vector<TLevelP> partLevel;

	TPointD p_offset;
	TDimension p_size(0, 0);

	/*- 参照画像ポートの取得 -*/
	std::vector<TRasterFxPort *> part_ports;   /*- テクスチャ素材画像のポート -*/
	std::map<int, TRasterFxPort *> ctrl_ports; /*- コントロール画像のポート番号/ポート -*/
	int portsCount = this->getInputPortCount();

	for (int i = 0; i < portsCount; ++i) {
		std::string tmpName = this->getInputPortName(i);
		QString portName = QString::fromStdString(tmpName);

		if (portName.startsWith("T")) {
			TRasterFxPort *tmpPart = (TRasterFxPort *)this->getInputPort(tmpName);
			if (tmpPart->isConnected())
				part_ports.push_back((TRasterFxPort *)this->getInputPort(tmpName));
		} else {
			portName.replace(QString("Control"), QString(""));
			TRasterFxPort *tmpCtrl = (TRasterFxPort *)this->getInputPort(tmpName);
			if (tmpCtrl->isConnected())
				ctrl_ports[portName.toInt()] = (TRasterFxPort *)this->getInputPort(tmpName);
		}
	}

	/*- テクスチャ素材のバウンディングボックスを足し合わせる ←この工程、いらないかも?-*/
	if (!part_ports.empty()) {
		TRectD outTileBBox(tile.m_pos, TDimensionD(tile.getRaster()->getLx(), tile.getRaster()->getLy()));
		TRectD bbox;

		for (unsigned int i = 0; i < (int)part_ports.size(); ++i) {
			const TFxTimeRegion &tr = (*part_ports[i])->getTimeRegion();

			lastframe.push_back(tr.getLastFrame() + 1);
			partLevel.push_back(new TLevel());
			partLevel[i]->setName((*part_ports[i])->getAlias(0, ri));

			// The particles offset must be calculated without considering the affine's translational
			// component
			TRenderSettings riZero(ri);
			riZero.m_affine.a13 = riZero.m_affine.a23 = 0;

			// Calculate the bboxes union
			for (int t = 0; t <= tr.getLastFrame(); ++t) {
				TRectD inputBox;
				(*part_ports[i])->getBBox(t, inputBox, riZero);
				bbox += inputBox;
			}
		}

		if (bbox == TConsts::infiniteRectD)
			bbox *= outTileBBox;

		p_size.lx = (int)bbox.getLx() + 1;
		p_size.ly = (int)bbox.getLy() + 1;
		p_offset = TPointD(0.5 * (bbox.x0 + bbox.x1), 0.5 * (bbox.y0 + bbox.y1));
	} else {
		partLevel.push_back(new TLevel());
		partLevel[0]->setName("particles");
		TDimension vecsize(10, 10);
		TOfflineGL *offlineGlContext = new TOfflineGL(vecsize);
		offlineGlContext->clear(TPixel32(0, 0, 0, 0));

		TStroke *stroke;
		stroke = makeEllipticStroke(0.07, TPointD((vecsize.lx - 1) * .5, (vecsize.ly - 1) * .5), 2.0, 2.0);
		TVectorImageP vectmp = new TVectorImage();

		TPalette *plt = new TPalette();
		vectmp->setPalette(plt);
		vectmp->addStroke(stroke);
		TVectorRenderData rd(AffI, TRect(vecsize), plt, 0, true, true);
		offlineGlContext->makeCurrent();
		offlineGlContext->draw(vectmp, rd);

		partLevel[0]->setFrame(0, TRasterImageP(offlineGlContext->getRaster()->clone()));
		p_size.lx = vecsize.lx + 1;
		p_size.ly = vecsize.ly + 1;
		lastframe.push_back(1);

		delete offlineGlContext;
	}

	Iwa_Particles_Engine myEngine(this, frame);

	// Retrieving the dpi multiplier from the accumulated affine (which is isotropic). That is,
	// the affine will be applied *before* this effect - and we'll multiply geometrical parameters
	// by this dpi mult. in order to compensate.
	float dpi = sqrt(fabs(ri.m_affine.det())) * 100;

	TTile tileIn;
	if (TRaster32P raster32 = tile.getRaster()) {
		TFlash *flash = 0;
		myEngine.render_particles(flash, &tile, part_ports, ri, p_size, p_offset, ctrl_ports, partLevel,
								  1, (int)frame, 1, 0, 0, 0, 0, lastframe, getIdentifier());
	} else if (TRaster64P raster64 = tile.getRaster()) {
		TFlash *flash = 0;
		myEngine.render_particles(flash, &tile, part_ports, ri, p_size, p_offset, ctrl_ports, partLevel,
								  1, (int)frame, 1, 0, 0, 0, 0, lastframe, getIdentifier());
	} else
		throw TException("ParticlesFx: unsupported Pixel Type");
}
コード例 #7
0
ファイル: pins.cpp プロジェクト: walkerka/opentoonz
void subCompute(TRasterFxPort &m_input, TTile &tile, double frame,
                const TRenderSettings &ri, TPointD p00, TPointD p01,
                TPointD p11, TPointD p10, int details, bool wireframe,
                TDimension m_offScreenSize, bool isCast) {
  TPixel32 bgColor;
  TRectD outBBox, inBBox;
  outBBox = inBBox = TRectD(tile.m_pos, TDimensionD(tile.getRaster()->getLx(),
                                                    tile.getRaster()->getLy()));
  m_input->getBBox(frame, inBBox, ri);
  if (inBBox == TConsts::infiniteRectD)  // e' uno zerario
    inBBox = outBBox;

  int inBBoxLx = (int)inBBox.getLx() / ri.m_shrinkX;
  int inBBoxLy = (int)inBBox.getLy() / ri.m_shrinkY;

  if (inBBox.isEmpty()) return;

  if (p00 == p01 && p00 == p10 && p00 == p11 &&
      !isCast)  // significa che non c'e' deformazione
  {
    m_input->compute(tile, frame, ri);
    return;
  }

  TRaster32P rasIn;
  TPointD rasInPos;

  if (!wireframe) {
    if (ri.m_bpp == 64 || ri.m_bpp == 48) {
      TRaster64P aux = TRaster64P(inBBoxLx, inBBoxLy);
      rasInPos = TPointD(inBBox.x0 / ri.m_shrinkX, inBBox.y0 / ri.m_shrinkY);
      TTile tmp(aux, rasInPos);
      m_input->compute(tmp, frame, ri);
      rasIn = TRaster32P(inBBoxLx, inBBoxLy);
      TRop::convert(rasIn, aux);
    } else {
      rasInPos = TPointD(inBBox.x0 / ri.m_shrinkX, inBBox.y0 / ri.m_shrinkY);
      TTile tmp(TRaster32P(inBBoxLx, inBBoxLy), rasInPos);
      m_input->allocateAndCompute(tmp, rasInPos, TDimension(inBBoxLx, inBBoxLy),
                                  TRaster32P(), frame, ri);
      rasIn = tmp.getRaster();
    }
  }

  unsigned int texWidth  = 2;
  unsigned int texHeight = 2;

  while (texWidth < (unsigned int)inBBoxLx) texWidth = texWidth << 1;

  while (texHeight < (unsigned int)inBBoxLy) texHeight = texHeight << 1;

  while (texWidth > 1024 || texHeight > 1024)  // avevo usato la costante
                                               // GL_MAX_TEXTURE_SIZE invece di
                                               // 1024, ma non funzionava!
  {
    inBBoxLx  = inBBoxLx >> 1;
    inBBoxLy  = inBBoxLy >> 1;
    texWidth  = texWidth >> 1;
    texHeight = texHeight >> 1;
  }

  if (rasIn->getLx() != inBBoxLx || rasIn->getLy() != inBBoxLy) {
    TRaster32P rasOut = TRaster32P(inBBoxLx, inBBoxLy);
    TRop::resample(rasOut, rasIn,
                   TScale((double)rasOut->getLx() / rasIn->getLx(),
                          (double)rasOut->getLy() / rasIn->getLy()));
    rasIn = rasOut;
  }

  int rasterWidth  = tile.getRaster()->getLx() + 2;
  int rasterHeight = tile.getRaster()->getLy() + 2;
  assert(rasterWidth > 0);
  assert(rasterHeight > 0);

  TRectD clippingRect =
      TRectD(tile.m_pos,
             TDimensionD(tile.getRaster()->getLx(), tile.getRaster()->getLy()));
#if CREATE_GL_CONTEXT_ONE_TIME
  int ret = wglMakeCurrent(m_offScreenGL.m_offDC, m_offScreenGL.m_hglRC);
  assert(ret == TRUE);
#else
  TOfflineGL offScreenRendering(TDimension(rasterWidth, rasterHeight));
  //#ifdef _WIN32
  offScreenRendering.makeCurrent();
//#else
//#if defined(LINUX) || defined(MACOSX)
// offScreenRendering.m_offlineGL->makeCurrent();
//#endif
#endif

  checkErrorsByGL
      // disabilito quello che non mi serve per le texture
      glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
  glDisable(GL_DITHER);
  glDisable(GL_DEPTH_TEST);
  glCullFace(GL_FRONT);
  glDisable(GL_STENCIL_TEST);
  glDisable(GL_LOGIC_OP);

  // creo la texture in base all'immagine originale
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  checkErrorsByGL
#if !CREATE_GL_CONTEXT_ONE_TIME
      TRaster32P rasaux;
  if (!wireframe) {
    TRaster32P texture(texWidth, texHeight);
    texture->clear();
    rasaux = texture;
    rasaux->lock();
    texture->copy(rasIn);

    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    glTexImage2D(GL_TEXTURE_2D, 0, 4, texWidth, texHeight, 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, texture->getRawData());
  }
#else

      unsigned int texWidth = 1024;
  unsigned int texHeight    = 1024;
  rasaux                    = rasIn;
  rasaux->lock();

  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rasIn->getLx(), rasIn->getLy(),
                  GL_RGBA, GL_UNSIGNED_BYTE, rasIn->getRawData());

#endif
  checkErrorsByGL

      glEnable(GL_TEXTURE_2D);

  // cfr. help: OpenGL/Programming tip/OpenGL Correctness Tips
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-rasterWidth * 0.5, rasterWidth * 0.5, -rasterHeight * 0.5,
          rasterHeight * 0.5, -1, 1);
  glViewport(0, 0, rasterWidth, rasterHeight);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT);

  // do OpenGL draw

  double lwTex = (double)(inBBoxLx - 1) / (double)(texWidth - 1);
  double lhTex = (double)(inBBoxLy - 1) / (double)(texHeight - 1);

  TPointD tex00 = TPointD(0.0, 0.0);
  TPointD tex10 = TPointD(lwTex, 0.0);
  TPointD tex11 = TPointD(lwTex, lhTex);
  TPointD tex01 = TPointD(0.0, lhTex);

  GLenum polygonStyle;
  if (wireframe) {
    polygonStyle = GL_LINE;
    glDisable(GL_TEXTURE_2D);
  } else
    polygonStyle = GL_FILL;
  checkErrorsByGL p00.x /= ri.m_shrinkX;
  p00.y /= ri.m_shrinkY;

  p10.x /= ri.m_shrinkX;
  p10.y /= ri.m_shrinkY;

  p11.x /= ri.m_shrinkX;
  p11.y /= ri.m_shrinkY;

  p01.x /= ri.m_shrinkX;
  p01.y /= ri.m_shrinkY;

  TPointD translate = TPointD(tile.m_pos.x + tile.getRaster()->getLx() * 0.5,
                              tile.m_pos.y + tile.getRaster()->getLy() * 0.5);
  glTranslated(-translate.x, -translate.y, 0.0);

  // disegno il poligono
  double dist_p00_p01                 = tdistance2(p00, p01);
  double dist_p10_p11                 = tdistance2(p10, p11);
  double dist_p01_p11                 = tdistance2(p01, p11);
  double dist_p00_p10                 = tdistance2(p00, p10);
  bool vertical                       = (dist_p00_p01 == dist_p10_p11);
  bool horizontal                     = (dist_p00_p10 == dist_p01_p11);
  if (vertical && horizontal) details = 1;
  glPolygonMode(GL_FRONT_AND_BACK, polygonStyle);
  subdivision(p00, p10, p11, p01, tex00, tex10, tex11, tex01, clippingRect,
              details);

  if (!wireframe) {
    // abilito l'antialiasing delle linee
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

    // disegno il bordo del poligono
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glBegin(GL_QUADS);
    glTexCoord2d(tex00.x, tex00.y);
    tglVertex(p00);
    glTexCoord2d(tex10.x, tex10.y);
    tglVertex(p10);
    glTexCoord2d(tex11.x, tex11.y);
    tglVertex(p11);
    glTexCoord2d(tex01.x, tex01.y);
    tglVertex(p01);
    glEnd();

    // disabilito l'antialiasing per le linee
    glDisable(GL_LINE_SMOOTH);
    glDisable(GL_BLEND);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisable(GL_TEXTURE_2D);
  }

  // force to finish
  glFlush();

  // rimetto il disegno dei poligoni a GL_FILL
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

  // metto il frame buffer nel raster del tile
  glPixelStorei(GL_UNPACK_ROW_LENGTH, rasterWidth);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

  TRaster32P newRas(tile.getRaster()->getLx(), tile.getRaster()->getLy());
  newRas->lock();
  glReadPixels(1, 1, newRas->getLx(), newRas->getLy(), GL_RGBA,
               GL_UNSIGNED_BYTE, (void *)newRas->getRawData());
  newRas->unlock();
  checkErrorsByGL

      rasaux->unlock();

  tile.getRaster()->copy(newRas);
}
コード例 #8
0
void TExternalProgramFx::doCompute(TTile &tile, double frame,
                                   const TRenderSettings &ri) {
  TRaster32P ras = tile.getRaster();
  if (!ras) return;
  std::string args           = m_args;
  std::string executablePath = ::to_string(m_executablePath);
  std::map<std::string, TFilePath> tmpFiles;  // portname --> file
  TFilePath outputTmpFile;

  std::map<std::string, Port>::const_iterator portIt;
  for (portIt = m_ports.begin(); portIt != m_ports.end(); ++portIt) {
    TFilePath fp            = TSystem::getUniqueFile("externfx");
    fp                      = fp.withType(portIt->second.m_ext);
    tmpFiles[portIt->first] = fp;
    if (portIt->second.m_port == 0)  // solo una porta e' di output
      outputTmpFile = fp;
    else {
      TRasterFxPort *tmp;
      tmp = portIt->second.m_port;
      if (tmp->isConnected()) {
        (*tmp)->compute(tile, frame, ri);
        TImageWriter::save(fp, ras);
      }
    }
  }

  // args e' della forma "$src $ctrl -o $out -v $value"
  // sostituisco le variabili
  int i = 0;
  for (;;) {
    i = args.find('$', i);
    if (i == (int)std::string::npos) break;
    int j   = i + 1;
    int len = args.length();
    while (j < len && isalnum(args[j])) j++;
    // un '$' non seguito da caratteri alfanumerici va ignorato
    if (j == i + 1) {
      // la sequenza '$$' diventa '$'
      if (j < len && args[j] == '$') args.replace(i, 2, "$");
      i++;
      continue;
    }
    // ho trovato una variabile
    int m            = j - i - 1;
    std::string name = args.substr(i + 1, m);
    // calcolo il valore.
    std::string value;

    std::map<std::string, TFilePath>::const_iterator it;
    it = tmpFiles.find(name);
    if (it != tmpFiles.end()) {
      // e' una porta. il valore e' il nome del
      // file temporaneo
      value = "\"" + ::to_string(it->second.getWideString()) + "\"";
    } else {
      // e' un parametro
      // se il nome non viene riconosciuto sostituisco la stringa nulla
      TDoubleParamP param = TParamP(getParams()->getParam(name));
      if (param) value    = std::to_string(param->getValue(frame));
    }

    args.replace(i, m + 1, value);
  }
  args = " " + args;  // aggiungo uno spazio per sicurezza
  // ofstream os("C:\\temp\\butta.txt");
  // os << args << endl;

  // bisognerebbe calcolare le immagini dalla/e porta/e di input
  // scrivere il/i valore/i nei files temporanei/o
  // chiamare "m_executablePath args"
  // e leggere l'immagine scritta in outputTmpFile
  // poi cancellare tutto
  std::string expandedargs;
  char buffer[1024];
#ifdef _WIN32
  ExpandEnvironmentStrings(args.c_str(), buffer, 1024);

  STARTUPINFO si;
  PROCESS_INFORMATION pinfo;

  GetStartupInfo(&si);

  BOOL ret = CreateProcess(
      (char *)executablePath.c_str(),           // name of executable module
      buffer,                                   // command line string
      NULL,                                     // SD
      NULL,                                     // SD
      TRUE,                                     // handle inheritance option
      CREATE_NO_WINDOW, /*CREATE_NEW_CONSOLE*/  // creation flags
      NULL,                                     // new environment block
      NULL,                                     // current directory name
      &si,                                      // startup information
      &pinfo                                    // process information
      );

  if (!ret) DWORD err = GetLastError();

  // aspetta che il processo termini
  WaitForSingleObject(pinfo.hProcess, INFINITE);

  DWORD exitCode;
  ret = GetExitCodeProcess(pinfo.hProcess,  // handle to the process
                           &exitCode);      // termination status

#else
  std::string cmdline = executablePath + buffer;
  //    int exitCode =
  system(cmdline.c_str());
#endif
  /*
string name = m_executablePath.getName();
TPixel32 color;
if(name == "saturate") color = TPixel32::Magenta;
else if(name == "over") color = TPixel32::Green;
else color = TPixel32::Red;
for(int iy=0;iy<ras->getLy();iy++)
{
TPixel32 *pix = ras->pixels(iy);
TPixel32 *endPix = pix + ras->getLx();
double x = tile.m_pos.x;
double y = tile.m_pos.y + iy;
while(pix<endPix)
 {
  if(x*x+y*y<900) *pix = color;
  else *pix = TPixel32(0,0,0,0);
  ++pix;
  x+=1.0;
 }
}
*/

  try {
    TRasterP ras = tile.getRaster();
    TImageReader::load(outputTmpFile, ras);
  } catch (...) {
  }

  // butto i file temporanei creati
  std::map<std::string, TFilePath>::const_iterator fileIt;
  for (fileIt = tmpFiles.begin(); fileIt != tmpFiles.end(); ++fileIt) {
    if (TFileStatus(fileIt->second).doesExist() == true) try {
        TSystem::deleteFile(fileIt->second);
      } catch (...) {
      }
  }
  if (TFileStatus(outputTmpFile).doesExist() == true) try {
      TSystem::deleteFile(outputTmpFile);
    } catch (...) {
    }
}
コード例 #9
0
void FreeDistortBaseFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri)
{
	if (!m_input.isConnected())
		return;

	//Upon deactivation, this fx does nothing.
	if (m_deactivate->getValue()) {
		m_input->compute(tile, frame, ri);
		return;
	}

	//Get the source quad
	TPointD p00_b = m_p00_b->getValue(frame);
	TPointD p10_b = m_p10_b->getValue(frame);
	TPointD p01_b = m_p01_b->getValue(frame);
	TPointD p11_b = m_p11_b->getValue(frame);

	//Get destination quad
	TPointD p00_a = m_p00_a->getValue(frame);
	TPointD p10_a = m_p10_a->getValue(frame);
	TPointD p01_a = m_p01_a->getValue(frame);
	TPointD p11_a = m_p11_a->getValue(frame);

	if (m_isCastShadow) {
		//Shadows are mirrored
		tswap(p00_a, p01_a);
		tswap(p10_a, p11_a);
	}

	//Get requested tile's geometry
	TRasterP tileRas(tile.getRaster());
	TRectD tileRect(convert(tileRas->getBounds()) + tile.m_pos);

	//Call transform to get the minimal rectOnInput
	TRectD inRect;
	TRenderSettings riNew;
	TRectD inBBox;

	safeTransform(frame, 0, tileRect, ri, inRect, riNew, inBBox);

	//Intersect with the bbox
	inRect *= inBBox;

	if (myIsEmpty(inRect))
		return;

	double scale = ri.m_affine.a11;

	double downBlur = m_downBlur->getValue(frame) * scale;
	double upBlur = m_upBlur->getValue(frame) * scale;
	int brad = tceil(tmax(downBlur, upBlur));

	inRect = inRect.enlarge(brad);

	TDimension inRectSize(tceil(inRect.getLx()), tceil(inRect.getLy()));

	TTile inTile;
	m_input->allocateAndCompute(inTile, inRect.getP00(), inRectSize, tileRas, frame, riNew);

	TPointD inTilePosRi = inTile.m_pos;

	//Update quads by the scale factors
	p00_b = riNew.m_affine * p00_b;
	p10_b = riNew.m_affine * p10_b;
	p01_b = riNew.m_affine * p01_b;
	p11_b = riNew.m_affine * p11_b;

	p00_a = ri.m_affine * p00_a;
	p10_a = ri.m_affine * p10_a;
	p01_a = ri.m_affine * p01_a;
	p11_a = ri.m_affine * p11_a;

	PerspectiveDistorter perpDistorter(
		p00_b - inTile.m_pos, p10_b - inTile.m_pos, p01_b - inTile.m_pos, p11_b - inTile.m_pos,
		p00_a, p10_a, p01_a, p11_a);

	BilinearDistorter bilDistorter(
		p00_b - inTile.m_pos, p10_b - inTile.m_pos, p01_b - inTile.m_pos, p11_b - inTile.m_pos,
		p00_a, p10_a, p01_a, p11_a);

	TQuadDistorter *distorter;
	if (m_distortType->getValue() == PERSPECTIVE)
		distorter = &perpDistorter;
	else if (m_distortType->getValue() == BILINEAR)
		distorter = &bilDistorter;
	else
		assert(0);

	if (m_isCastShadow) {
		TRaster32P ras32 = inTile.getRaster();
		TRaster64P ras64 = inTile.getRaster();

		if (ras32) {
			if (m_fade->getValue(frame) > 0)
				doFade(ras32, m_color->getValue(frame), m_fade->getValue(frame) / 100.0);
			if (brad > 0)
				doBlur(ras32, upBlur, downBlur,
					   m_upTransp->getValue(frame) / 100.0, m_downTransp->getValue(frame) / 100.0,
					   inBBox.y0 - inTile.m_pos.y, inBBox.y1 - inTile.m_pos.y);
			else if (m_upTransp->getValue(frame) > 0 || m_downTransp->getValue(frame) > 0)
				doTransparency(ras32, m_upTransp->getValue(frame) / 100.0, m_downTransp->getValue(frame) / 100.0,
							   inBBox.y0 - inTile.m_pos.y, inBBox.y1 - inTile.m_pos.y);
		} else if (ras64) {
			if (m_fade->getValue(frame) > 0)
				doFade(ras64, toPixel64(m_color->getValue(frame)), m_fade->getValue(frame) / 100.0);
			if (brad > 0)
				doBlur(ras64, upBlur, downBlur,
					   m_upTransp->getValue(frame) / 100.0, m_downTransp->getValue(frame) / 100.0,
					   inBBox.y0 - inTile.m_pos.y, inBBox.y1 - inTile.m_pos.y);
			else if (m_upTransp->getValue(frame) > 0 || m_downTransp->getValue(frame) > 0)
				doTransparency(ras64, m_upTransp->getValue(frame) / 100.0, m_downTransp->getValue(frame) / 100.0,
							   inBBox.y0 - inTile.m_pos.y, inBBox.y1 - inTile.m_pos.y);
		} else
			assert(false);
	}

	distort(tileRas, inTile.getRaster(), *distorter, convert(tile.m_pos), TRop::Bilinear);
}