about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEmery Hemingway <emery@dmz.rs>2024-09-03T10·54+0300
committeremery <emery@dmz.rs>2024-09-03T19·44+0000
commit1c898f7ddc5b3ad223d73972465c9fee206c9815 (patch)
treeefb35c2af80c74073cf2d956bf4ed91c24a848f7
parente4714db2d5f2b38b60f1ec8a200d0eb3bea0d79f (diff)
feat(tools/eaglemode/plugins/avif): AVIF image plugin r/8647
Animation not implemented.
https: //en.wikipedia.org/wiki/AVIF

Change-Id: I80f8c4132c4335b2e60ce7b70eb424457e50c73f
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12428
Tested-by: BuildkiteCI
Reviewed-by: tazjin <tazjin@tvl.su>
-rw-r--r--tools/eaglemode/plugins/avif/default.nix16
-rw-r--r--tools/eaglemode/plugins/avif/etc/emCore/FpPlugins/PlAvif.emFpPlugin6
-rw-r--r--tools/eaglemode/plugins/avif/makers/PlAvif.maker.pm64
-rw-r--r--tools/eaglemode/plugins/avif/src/PlAvif.cpp190
-rw-r--r--users/emery/eaglemode/default.nix1
5 files changed, 277 insertions, 0 deletions
diff --git a/tools/eaglemode/plugins/avif/default.nix b/tools/eaglemode/plugins/avif/default.nix
new file mode 100644
index 000000000000..5def86a6ed82
--- /dev/null
+++ b/tools/eaglemode/plugins/avif/default.nix
@@ -0,0 +1,16 @@
+{ depot, pkgs, ... }:
+
+let
+  em = depot.tools.eaglemode;
+  emSrc = pkgs.srcOnly pkgs.em;
+in
+(em.buildPlugin {
+  name = "avif";
+  version = "canon";
+  src = ./.;
+  target = "PlAvif";
+}).overrideAttrs
+  ({ buildInputs ? [ ], nativeBuildInputs ? [ ], ... }: {
+    buildInputs = buildInputs ++ [ pkgs.libavif ];
+    nativeBuildInputs = nativeBuildInputs ++ [ pkgs.pkg-config ];
+  })
diff --git a/tools/eaglemode/plugins/avif/etc/emCore/FpPlugins/PlAvif.emFpPlugin b/tools/eaglemode/plugins/avif/etc/emCore/FpPlugins/PlAvif.emFpPlugin
new file mode 100644
index 000000000000..61615f9fd391
--- /dev/null
+++ b/tools/eaglemode/plugins/avif/etc/emCore/FpPlugins/PlAvif.emFpPlugin
@@ -0,0 +1,6 @@
+#%rec:emFpPlugin%#
+
+FileTypes = { ".avif" }
+Priority = 1.0
+Library = "PlAvif"
+Function = "PlAvifFpPluginFunc"
diff --git a/tools/eaglemode/plugins/avif/makers/PlAvif.maker.pm b/tools/eaglemode/plugins/avif/makers/PlAvif.maker.pm
new file mode 100644
index 000000000000..00b927805a72
--- /dev/null
+++ b/tools/eaglemode/plugins/avif/makers/PlAvif.maker.pm
@@ -0,0 +1,64 @@
+package PlAvif;
+
+use strict;
+use warnings;
+
+sub GetDependencies
+{
+	return ('emCore');
+}
+
+sub IsEssential
+{
+	return 0;
+}
+
+sub GetFileHandlingrules
+{
+	return ();
+}
+
+sub GetExtraBuildOptions
+{
+	return ();
+}
+
+sub Build
+{
+	shift;
+	my %options=@_;
+
+	my @libAvifFlags=();
+	if ($options{'avif-inc-dir'} eq '' && $options{'avif-lib-dir'} eq '') {
+		@libAvifFlags=split("\n",readpipe(
+			"perl \"".$options{'utils'}."/PkgConfig.pl\" libavif"
+		));
+	}
+	if (!@libAvifFlags) {
+		if ($options{'avif-inc-dir'} ne '') {
+			push(@libAvifFlags, "--inc-search-dir", $options{'avif-inc-dir'});
+		}
+		if ($options{'avif-lib-dir'} ne '') {
+			push(@libAvifFlags, "--lib-search-dir", $options{'avif-lib-dir'});
+		}
+		push(@libAvifFlags, "--link", "avif");
+	}
+
+	system(
+		@{$options{'unicc_call'}},
+		"--math",
+		"--rtti",
+		"--exceptions",
+		"--bin-dir"       , "bin",
+		"--lib-dir"       , "lib",
+		"--obj-dir"       , "obj",
+		"--inc-search-dir", "include",
+		@libAvifFlags,
+		"--link"          , "emCore",
+		"--type"          , "dynlib",
+		"--name"          , "PlAvif",
+		"src/PlAvif.cpp"
+	)==0 or return 0;
+
+	return 1;
+}
diff --git a/tools/eaglemode/plugins/avif/src/PlAvif.cpp b/tools/eaglemode/plugins/avif/src/PlAvif.cpp
new file mode 100644
index 000000000000..e4807bacd49a
--- /dev/null
+++ b/tools/eaglemode/plugins/avif/src/PlAvif.cpp
@@ -0,0 +1,190 @@
+#include <emCore/emFpPlugin.h>
+#include <emCore/emImageFile.h>
+
+#include "avif/avif.h"
+
+class PlAvifImageFileModel : public emImageFileModel
+{
+public:
+
+	static emRef<PlAvifImageFileModel> Acquire(
+		emContext & context, const emString & name, bool common=true
+	);
+
+protected:
+	PlAvifImageFileModel(emContext & context, const emString & name);
+	virtual ~PlAvifImageFileModel();
+	virtual void TryStartLoading();
+	virtual bool TryContinueLoading();
+	virtual void QuitLoading();
+	virtual void TryStartSaving();
+	virtual bool TryContinueSaving();
+	virtual void QuitSaving();
+	virtual emUInt64 CalcMemoryNeed();
+	virtual double CalcFileProgress();
+
+private:
+	struct LoadingState;
+	LoadingState * L = NULL;
+};
+
+
+struct PlAvifImageFileModel::LoadingState {
+	avifRGBImage rgb;
+	avifDecoder * decoder;
+};
+
+
+emRef<PlAvifImageFileModel> PlAvifImageFileModel::Acquire(
+	emContext & context, const emString & name, bool common
+)
+{
+	EM_IMPL_ACQUIRE(PlAvifImageFileModel, context, name, common)
+}
+
+
+PlAvifImageFileModel::PlAvifImageFileModel(
+	emContext & context, const emString & name
+)
+	: emImageFileModel(context, name)
+{
+}
+
+
+PlAvifImageFileModel::~PlAvifImageFileModel()
+{
+	PlAvifImageFileModel::QuitLoading();
+	PlAvifImageFileModel::QuitSaving();
+}
+
+
+void PlAvifImageFileModel::TryStartLoading()
+{
+	avifResult result;
+
+	L = new LoadingState;
+	memset(L, 0, sizeof(LoadingState));
+
+	L->decoder = avifDecoderCreate();
+	if (L->decoder == NULL) {
+		throw emException("failed to create AVIF decoder");
+	}
+
+	result = avifDecoderSetIOFile(L->decoder, GetFilePath());
+	if (result != AVIF_RESULT_OK) {
+		throw emException("%s", avifResultToString(result));
+	}
+
+	result = avifDecoderParse(L->decoder);
+	if (result != AVIF_RESULT_OK) {
+		throw emException("%s", avifResultToString(result));
+	}
+
+	FileFormatInfo = emString::Format(
+		"AVIF %s %ubpc",
+			avifPixelFormatToString(L->decoder->image->yuvFormat),
+			L->decoder->image->depth
+	);
+
+
+	Signal(ChangeSignal);
+}
+
+
+bool PlAvifImageFileModel::TryContinueLoading()
+{
+	avifResult result;
+
+	if (!Image.GetHeight()) {
+		Image.Setup(
+			L->decoder->image->width,
+			L->decoder->image->height,
+			L->decoder->alphaPresent ? 4 : 3
+		);
+	}
+
+	result = avifDecoderNextImage(L->decoder);
+	if (result != AVIF_RESULT_OK) {
+		throw emException("%s", avifResultToString(result));
+	}
+
+	avifRGBImageSetDefaults(&L->rgb, L->decoder->image);
+	L->rgb.format = L->decoder->alphaPresent ?
+		AVIF_RGB_FORMAT_RGBA : AVIF_RGB_FORMAT_RGB;
+	L->rgb.pixels   = Image.GetWritableMap();
+	L->rgb.width    = Image.GetWidth();
+	L->rgb.height   = Image.GetHeight();
+	L->rgb.depth    = 8;
+	L->rgb.rowBytes = Image.GetWidth() * Image.GetChannelCount();
+
+	result = avifImageYUVToRGB(L->decoder->image, &L->rgb);
+	if (result != AVIF_RESULT_OK) {
+		throw emException("%s", avifResultToString(result));
+	}
+
+	Signal(ChangeSignal);
+	return true;
+}
+
+
+void PlAvifImageFileModel::QuitLoading()
+{
+	if (L) {
+		if (L->decoder) avifDecoderDestroy(L->decoder);
+		delete L;
+		L = NULL;
+	}
+}
+
+
+void PlAvifImageFileModel::TryStartSaving()
+{
+	throw emException("PlAvifImageFileModel: Saving not implemented.");
+}
+
+
+bool PlAvifImageFileModel::TryContinueSaving()
+{
+	return false;
+}
+
+
+void PlAvifImageFileModel::QuitSaving()
+{
+}
+
+
+emUInt64 PlAvifImageFileModel::CalcMemoryNeed()
+{
+	return
+		(emUInt64)
+			L->decoder->image->width *
+			L->decoder->image->height *
+			(L->decoder->alphaPresent ? 4 : 3);
+}
+
+
+double PlAvifImageFileModel::CalcFileProgress()
+{
+	return 0.0;
+}
+
+extern "C" {
+	emPanel * PlAvifFpPluginFunc(
+		emPanel::ParentArg parent, const emString & name,
+		const emString & path, emFpPlugin * plugin,
+		emString * errorBuf
+	)
+	{
+		if (plugin->Properties.GetCount()) {
+			*errorBuf="PlAvifFpPlugin: No properties allowed.";
+			return NULL;
+		}
+		return new emImageFilePanel(
+			parent, name,
+			PlAvifImageFileModel::Acquire(
+				parent.GetRootContext(), path
+			)
+		);
+	}
+}
diff --git a/users/emery/eaglemode/default.nix b/users/emery/eaglemode/default.nix
index 89b4f60ec174..471e8eda67f7 100644
--- a/users/emery/eaglemode/default.nix
+++ b/users/emery/eaglemode/default.nix
@@ -5,6 +5,7 @@ let
   config = depot.tools.eaglemode.etcDir {
     extraPaths = [
       depot.tools.eaglemode.commands.B
+      depot.tools.eaglemode.plugins.avif
       depot.tools.eaglemode.plugins.qoi
     ];
   };