[graphics/krita/krita/6.0] libs/ui/animation: Split off common libav context functionality

Carsten Hartenfels <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 36380b153c91b03c0dd2e5d43d0506ba4b4b9dbb by Carsten Hartenfels.
Committed on 27/07/2026 at 17:19.
Pushed by hartenfels into branch 'krita/6.0'.

Split off common libav context functionality

It will be used in a libav media encoder implementation coming in
subsequent commits, this pulls some common parts out of the Android
encoder.

M  +8    -66   libs/ui/animation/KisAndroidMediaEncoderRunnable.cpp
A  +98   -0    libs/ui/animation/KisLibavEncoderContext.h     [License: GPL(v3.0+)]

https://invent.kde.org/graphics/krita/-/commit/36380b153c91b03c0dd2e5d43d0506ba4b4b9dbb

diff --git a/libs/ui/animation/KisAndroidMediaEncoderRunnable.cpp b/libs/ui/animation/KisAndroidMediaEncoderRunnable.cpp
index b4390f684f9..a0dc30f2b1e 100644
--- a/libs/ui/animation/KisAndroidMediaEncoderRunnable.cpp
+++ b/libs/ui/animation/KisAndroidMediaEncoderRunnable.cpp
@@ -31,6 +31,8 @@ extern "C" {
 #include <libswscale/swscale.h>
 }
 
+#include "KisLibavEncoderContext.h"
+
 struct KisAndroidMediaEncoderRunnable::EncoderImage {
     uint8_t *bufferY;
     uint8_t *bufferU;
@@ -210,20 +212,16 @@ private:
     int m_formatId;
 };
 
-class KisAndroidMediaEncoderRunnable::Context
+class KisAndroidMediaEncoderRunnable::Context : public KisLibavEncoderContext
 {
 public:
     explicit Context(QString *outErrorMessage = nullptr)
-        : m_outErrorMessage(outErrorMessage)
+        : KisLibavEncoderContext(outErrorMessage)
     {
     }
 
-    ~Context()
+    ~Context() override
     {
-        if (m_imageFormat != AV_PIX_FMT_NONE) {
-            av_freep(&m_imageBuffers[0]);
-        }
-        sws_freeContext(m_swsContext);
         clearEncoder();
     }
 
@@ -273,15 +271,11 @@ public:
             return true;
         } else if (result == STATUS_ERROR_START_ENCODER) {
             warnFile << "Start encoder error" << result;
-            if (m_outErrorMessage) {
-                *m_outErrorMessage = i18n("Unsupported video parameters, try lowering the video FPS or size");
-            }
+            setErrorMessage(i18n("Unsupported video parameters, try lowering the video FPS or size"));
             return true;
         } else if (result == STATUS_ERROR_DRAIN_MUXER_ADD_TRACK) {
             warnFile << "Muxer track error" << result;
-            if (m_outErrorMessage) {
-                *m_outErrorMessage = i18n("Unsupported format");
-            }
+            setErrorMessage(i18n("Unsupported format"));
             return true;
         } else if (isErrorResult(result)) {
             setInternalErrorMessage(QStringLiteral("%1 failed with code %2").arg(title).arg(result));
@@ -303,27 +297,6 @@ public:
         }
     }
 
-    SwsContext *getSwsContextFor(int inputWidth,
-                                 int inputHeight,
-                                 AVPixelFormat inputFormat,
-                                 int outputWidth,
-                                 int outputHeight,
-                                 AVPixelFormat outputFormat,
-                                 int flags)
-    {
-        return sws_getCachedContext(m_swsContext,
-                                    inputWidth,
-                                    inputHeight,
-                                    inputFormat,
-                                    outputWidth,
-                                    outputHeight,
-                                    outputFormat,
-                                    flags,
-                                    nullptr,
-                                    nullptr,
-                                    nullptr);
-    }
-
     int imageFormat() const
     {
         return m_imageFormat;
@@ -358,16 +331,6 @@ public:
         }
     }
 
-    void setInternalErrorMessage(const QString &detail)
-    {
-        warnFile << "Media encoder error:" << detail;
-        if (m_outErrorMessage) {
-            // Internal encoder errors are only really useful for developers,
-            // so there's no point in translating them.
-            *m_outErrorMessage = i18n("Internal error (%1)", detail);
-        }
-    }
-
 private:
     static bool isErrorResult(int result)
     {
@@ -376,8 +339,6 @@ private:
 
     QJniEnvironment m_env;
     QJniObject m_encoder;
-    QString *m_outErrorMessage;
-    SwsContext *m_swsContext = nullptr;
     uint8_t *m_imageBuffers[4] = {nullptr, nullptr, nullptr, nullptr};
     int m_imageLinesizes[4] = {0, 0, 0, 0};
     AVPixelFormat m_imageFormat = AV_PIX_FMT_NONE;
@@ -486,26 +447,7 @@ KisMediaEncoderRunnable::EncodeResult KisAndroidMediaEncoderRunnable::encode(QSt
         // Grab the next frame from disk.
         QImage inputImage;
         AVPixelFormat inputPixelFormat;
-        if (frame.readImage(inputImage)) {
-            switch (inputImage.format()) {
-            case QImage::Format_RGB32:
-                inputPixelFormat = AV_PIX_FMT_BGR0;
-                break;
-            case QImage::Format_ARGB32:
-                inputPixelFormat = AV_PIX_FMT_BGRA;
-                break;
-            default:
-                // The above are the only formats I can get the the recorder to
-                // produce, so I'm not gonna get experimental with this.
-                inputPixelFormat = AV_PIX_FMT_BGRA;
-                inputImage = inputImage.convertToFormat(QImage::Format_ARGB32);
-                if (inputImage.isNull()) {
-                    warnFile << "Frame conversion from" << inputImage.format() << "failed";
-                    continue;
-                }
-                break;
-            }
-        } else {
+        if (!frame.readImage(inputImage) || !ctx.convertFrame(inputImage, inputPixelFormat)) {
             continue; // Keep going, some frames may be corrupted.
         }
 
diff --git a/libs/ui/animation/KisLibavEncoderContext.h b/libs/ui/animation/KisLibavEncoderContext.h
new file mode 100644
index 00000000000..01536d4c7c8
--- /dev/null
+++ b/libs/ui/animation/KisLibavEncoderContext.h
@@ -0,0 +1,98 @@
+/*
+ *  SPDX-License-Identifier: GPL-3.0-or-later
+ */
+#ifndef KISLIBAVENCODERCONTEXT
+#define KISLIBAVENCODERCONTEXT
+
+#include <QImage>
+#include <QString>
+
+#include <klocalizedstring.h>
+
+#include <kis_debug.h>
+
+extern "C" {
+#include <libavutil/imgutils.h>
+#include <libavutil/pixfmt.h>
+#include <libswscale/swscale.h>
+}
+
+class KisLibavEncoderContext
+{
+    Q_DISABLE_COPY_MOVE(KisLibavEncoderContext)
+public:
+    explicit KisLibavEncoderContext(QString *outErrorMessage = nullptr)
+        : m_outErrorMessage(outErrorMessage)
+    {
+    }
+
+    virtual ~KisLibavEncoderContext()
+    {
+        sws_freeContext(m_swsContext);
+    };
+
+    bool convertFrame(QImage &inOutImage, AVPixelFormat &outPixelFormat) const
+    {
+        switch (inOutImage.format()) {
+        case QImage::Format_RGB32:
+            outPixelFormat = AV_PIX_FMT_BGR0;
+            break;
+        case QImage::Format_ARGB32:
+            outPixelFormat = AV_PIX_FMT_BGRA;
+            break;
+        default:
+            // The above are the only formats I can get the the recorder to
+            // produce, so I'm not gonna get experimental with this.
+            outPixelFormat = AV_PIX_FMT_BGRA;
+            inOutImage = inOutImage.convertToFormat(QImage::Format_ARGB32);
+            if (inOutImage.isNull()) {
+                warnFile << "Frame conversion from" << inOutImage.format() << "failed";
+                return false;
+            }
+            break;
+        }
+        return true;
+    }
+
+    SwsContext *getSwsContextFor(int inputWidth,
+                                 int inputHeight,
+                                 AVPixelFormat inputFormat,
+                                 int outputWidth,
+                                 int outputHeight,
+                                 AVPixelFormat outputFormat,
+                                 int flags)
+    {
+        return sws_getCachedContext(m_swsContext,
+                                    inputWidth,
+                                    inputHeight,
+                                    inputFormat,
+                                    outputWidth,
+                                    outputHeight,
+                                    outputFormat,
+                                    flags,
+                                    nullptr,
+                                    nullptr,
+                                    nullptr);
+    }
+
+    void setInternalErrorMessage(const QString &detail)
+    {
+        warnFile << "Media encoder error:" << detail;
+        // Internal encoder errors are only really useful for developers,
+        // so there's no point in translating them.
+        setErrorMessage(i18n("Internal error (%1)", detail));
+    }
+
+    void setErrorMessage(const QString &errorMessage)
+    {
+        if (m_outErrorMessage) {
+            *m_outErrorMessage = errorMessage;
+        }
+    }
+
+private:
+    QString *m_outErrorMessage;
+    SwsContext *m_swsContext = nullptr;
+};
+
+#endif
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.