Exemple #1
0
bool LayerCamera::addNewKeyFrameAt( int frameNumber )
{
    QMatrix viewMatrix = getViewAtFrame( frameNumber );
    Camera* pCamera = new Camera( viewMatrix );
    pCamera->setPos( frameNumber );
    return addKeyFrame( frameNumber, pCamera );
}
Exemple #2
0
bool LayerCamera::addNewKeyAt( int frameNumber )
{
    QTransform view = getViewAtFrame( frameNumber );
    Camera* pCamera = new Camera( view );
    pCamera->setPos( frameNumber );
    return addKeyFrame( frameNumber, pCamera );
}
Exemple #3
0
Status MovieExporter::generateImageSequence(
    const Object* obj,
    std::function<void(float)>  progress )
{
    int frameStart        = mDesc.startFrame;
    int frameEnd          = mDesc.endFrame;
    QSize exportSize      = mDesc.exportSize;
    bool transparency     = false;
    QString strCameraName = mDesc.strCameraName;

    auto cameraLayer = (LayerCamera*)obj->findLayerByName( strCameraName, Layer::CAMERA );
    if ( cameraLayer == nullptr )
    {
        cameraLayer = obj->getLayersByType< LayerCamera >().front();
    }

    for ( int currentFrame = frameStart; currentFrame <= frameEnd; currentFrame++ )
    {
        if ( mCanceled )
        {
            return Status::CANCELED;
        }

        QImage imageToExport( exportSize, QImage::Format_ARGB32_Premultiplied );
        QColor bgColor = Qt::white;
        if ( transparency )
        {
            bgColor.setAlpha( 0 );
        }
        imageToExport.fill( bgColor );

        QPainter painter( &imageToExport );

        QTransform view = cameraLayer->getViewAtFrame( currentFrame );

        QSize camSize = cameraLayer->getViewSize();
        QTransform centralizeCamera;
        centralizeCamera.translate( camSize.width() / 2, camSize.height() / 2 );

        painter.setWorldTransform( view * centralizeCamera );
        painter.setWindow( QRect( 0, 0, camSize.width(), camSize.height() ) );

        obj->paintImage( painter, currentFrame, false, true );

        QString imageFileWithFrameNumber = QString().sprintf( IMAGE_FILENAME,  currentFrame );

        QString strImgPath = mTempWorkDir + imageFileWithFrameNumber;
        bool bSave = imageToExport.save( strImgPath );
        Q_ASSERT( bSave );

        qDebug() << "Save img to: " << strImgPath;

        float fProgressValue = ( currentFrame / (float)( frameEnd - frameStart ) );
        progress( 0.1f + ( fProgressValue * 0.99f ) );
    }

    return Status::OK;
}
Exemple #4
0
bool LayerCamera::addImageAtFrame(int frameNumber) {
	int index = getIndexAtFrame(frameNumber);
	if(index == -1) {
		//framesImage.append(new QImage(imageSize, QImage::Format_ARGB32_Premultiplied));
		Camera* camera = new Camera();
		camera->view = getViewAtFrame(frameNumber);
		framesCamera.append(camera);
		framesPosition.append(frameNumber);
		framesSelected.append(false);
		framesFilename.append("");
		framesModified.append(false);
		bubbleSort();
		int frameNumber1 = frameNumber;
		int frameNumber2 = frameNumber;
		if(index>0) frameNumber1 = framesPosition.at(index-1);
		if(index<framesPosition.size()-1) frameNumber1 = framesPosition.at(index+1);
		emit imageAdded(frameNumber1, frameNumber2);
		return true;
	} else {
		return false;
	}
}
Exemple #5
0
/** Exports obj to a gif image at strOut using FFmpeg.
 *
 *  @param[in]  obj An Object containing the animation to export.
 *  @param[in]  ffmpegPath The path to the FFmpeg binary.
 *  @param[in]  strOut The output path. Should end with .gif.
 *  @param[out] progress A function that takes one float argument
 *              (the percentage of the gif generation complete) and
 *              may display the output to the user in any way it
 *              sees fit.
 *
 *  @return Returns the final status of the operation (ok or canceled)
 */
Status MovieExporter::generateGif(
        const Object* obj,
        QString ffmpegPath,
        QString strOut,
        std::function<void(float)> progress)
{

    if (mCanceled)
    {
        return Status::CANCELED;
    }

    // Frame generation setup

    int frameStart = mDesc.startFrame;
    int frameEnd = mDesc.endFrame;
    const QSize exportSize = mDesc.exportSize;
    bool transparency = false;
    QString strCameraName = mDesc.strCameraName;
    bool loop = mDesc.loop;
    int bytesWritten;

    auto cameraLayer = static_cast<LayerCamera*>(obj->findLayerByName(strCameraName, Layer::CAMERA));
    if (cameraLayer == nullptr)
    {
        cameraLayer = obj->getLayersByType< LayerCamera >().front();
    }
    int currentFrame = frameStart;

    /* We create an image with the correct dimensions and background
     * color here and then copy this and draw over top of it to
     * generate each frame. This is faster than having to generate
     * a new background image for each frame.
     */
    QImage imageToExportBase(exportSize, QImage::Format_ARGB32_Premultiplied);
    QColor bgColor = Qt::white;
    if (transparency)
    {
        bgColor.setAlpha(0);
    }
    imageToExportBase.fill(bgColor);

    QSize camSize = cameraLayer->getViewSize();
    QTransform centralizeCamera;
    centralizeCamera.translate(camSize.width() / 2, camSize.height() / 2);

    // Build FFmpeg command

    QString strCmd = QString("\"%1\"").arg(ffmpegPath);
    strCmd += QString(" -f rawvideo -pixel_format bgra");
    strCmd += QString(" -video_size %1x%2").arg(exportSize.width()).arg(exportSize.height());
    strCmd += QString(" -framerate %1").arg(mDesc.fps);

    strCmd += " -i -";

    strCmd += " -y";

    strCmd += " -filter_complex \"[0:v]palettegen [p]; [0:v][p] paletteuse\"";

    strCmd += QString(" -loop %1").arg(loop ? "0" : "-1");
    strCmd += QString(" \"%1\"").arg(strOut);

    // Run FFmpeg command

    STATUS_CHECK(executeFFMpegPipe(strCmd, progress, [&](QProcess& ffmpeg, int framesProcessed)
    {
        /* The GIF FFmpeg command requires the entires stream to be
         * written before FFmpeg can encode the GIF. This is because
         * the generated pallete is based off of the colors in all
         * frames. The only way to avoid this would be to generate
         * all the frames twice and run two separate commands, which
         * would likely have unacceptable speed costs.
         */

        Q_UNUSED(framesProcessed);
        if(currentFrame > frameEnd)
        {
            ffmpeg.closeWriteChannel();
            return false;
        }

        QImage imageToExport = imageToExportBase.copy();
        QPainter painter(&imageToExport);

        QTransform view = cameraLayer->getViewAtFrame(currentFrame);
        painter.setWorldTransform(view * centralizeCamera);
        painter.setWindow(QRect(0, 0, camSize.width(), camSize.height()));

        obj->paintImage(painter, currentFrame, false, true);

        bytesWritten = ffmpeg.write(reinterpret_cast<const char*>(imageToExport.constBits()), imageToExport.byteCount());
        Q_ASSERT(bytesWritten == imageToExport.byteCount());

        currentFrame++;

        return true;
    }));

    return Status::OK;
}
Exemple #6
0
/** Exports obj to a movie image at strOut using FFmpeg.
 *
 *  @param[in]  obj An Object containing the animation to export.
 *  @param[in]  ffmpegPath The path to the FFmpeg binary.
 *  @param[in]  strOutputFile The output path. Should end with .gif.
 *  @param[out] progress A function that takes one float argument
 *              (the percentage of the gif generation complete) and
 *              may display the output to the user in any way it
 *              sees fit.
 *
 *  The movie formats supported by this operation are any file
 *  formats that the referenced FFmpeg binary supports and that have
 *  the required features (ex. video and audio support)
 *
 *  @return Returns the final status of the operation (ok or canceled)
 */
Status MovieExporter::generateMovie(
        const Object* obj,
        QString ffmpegPath,
        QString strOutputFile,
        std::function<void(float)> progress)
{
    if (mCanceled)
    {
        return Status::CANCELED;
    }

    // Frame generation setup

    int frameStart = mDesc.startFrame;
    int frameEnd = mDesc.endFrame;
    const QSize exportSize = mDesc.exportSize;
    bool transparency = mDesc.alpha;
    QString strCameraName = mDesc.strCameraName;
    bool loop = mDesc.loop;

    auto cameraLayer = static_cast<LayerCamera*>(obj->findLayerByName(strCameraName, Layer::CAMERA));
    if (cameraLayer == nullptr)
    {
        cameraLayer = obj->getLayersByType< LayerCamera >().front();
    }
    int currentFrame = frameStart;

    /* We create an image with the correct dimensions and background
     * color here and then copy this and draw over top of it to
     * generate each frame. This is faster than having to generate
     * a new background image for each frame.
     */
    QImage imageToExportBase(exportSize, QImage::Format_ARGB32_Premultiplied);
    QColor bgColor = Qt::white;
    if (transparency)
    {
        bgColor.setAlpha(0);
    }
    imageToExportBase.fill(bgColor);

    QSize camSize = cameraLayer->getViewSize();
    QTransform centralizeCamera;
    centralizeCamera.translate(camSize.width() / 2, camSize.height() / 2);

    int failCounter = 0;
    /* Movie export uses a "sliding window" to reduce memory usage
     * while having a relatively small impact on speed. This basically
     * means that there is a maximum number of frames that can be waiting
     * to be encoded by ffmpeg at any one time. The limit is set by the
     * frameWindow variable which is designed to take up a maximum of
     * about 1GB of memory
     */
    int frameWindow = static_cast<int>(1e9 / (camSize.width() * camSize.height() * 4.0));

    // Build FFmpeg command

    //int exportFps = mDesc.videoFps;
    const QString tempAudioPath = mTempWorkDir + "/tmpaudio.wav";

    QString strCmd = QString("\"%1\"").arg(ffmpegPath);
    strCmd += QString(" -f rawvideo -pixel_format bgra");
    strCmd += QString(" -video_size %1x%2").arg(exportSize.width()).arg(exportSize.height());
    strCmd += QString(" -framerate %1").arg(mDesc.fps);

    //strCmd += QString( " -r %1").arg( exportFps );
    strCmd += QString(" -i -");
    strCmd += QString(" -threads %1").arg(QThread::idealThreadCount() == 1 ? 0 : QThread::idealThreadCount());

    if (QFile::exists(tempAudioPath))
    {
        strCmd += QString(" -i \"%1\" ").arg(tempAudioPath);
    }

    if (strOutputFile.endsWith(".apng", Qt::CaseInsensitive))
    {
        strCmd += QString(" -plays %1").arg(loop ? "0" : "1");
    }

    if (strOutputFile.endsWith("mp4", Qt::CaseInsensitive))
    {
        strCmd += QString(" -pix_fmt yuv420p");
    }

    if (strOutputFile.endsWith(".avi", Qt::CaseInsensitive))
    {
        strCmd += " -q:v 5";
    }

    strCmd += " -y";
    strCmd += QString(" \"%1\"").arg(strOutputFile);

    // Run FFmpeg command

    STATUS_CHECK(executeFFMpegPipe(strCmd, progress, [&](QProcess& ffmpeg, int framesProcessed)
    {
        if(framesProcessed < 0)
        {
            failCounter++;
        }

        if(currentFrame > frameEnd)
        {
            ffmpeg.closeWriteChannel();
            return false;
        }

        if((currentFrame - frameStart <= framesProcessed + frameWindow || failCounter > 10) && currentFrame <= frameEnd)
        {
            QImage imageToExport = imageToExportBase.copy();
            QPainter painter(&imageToExport);

            QTransform view = cameraLayer->getViewAtFrame(currentFrame);
            painter.setWorldTransform(view * centralizeCamera);
            painter.setWindow(QRect(0, 0, camSize.width(), camSize.height()));

            obj->paintImage(painter, currentFrame, false, true);
            painter.end();

            // Should use sizeInBytes instead of byteCount to support large images,
            // but this is only supported in QT 5.10+
            int bytesWritten = ffmpeg.write(reinterpret_cast<const char*>(imageToExport.constBits()), imageToExport.byteCount());
            Q_ASSERT(bytesWritten == imageToExport.byteCount());

            currentFrame++;
            failCounter = 0;
            return true;
        }

        return false;
    }));

    return Status::OK;
}