feat(tools/eaglemode/plugins/avif): AVIF image plugin

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>
This commit is contained in:
Emery Hemingway 2024-09-03 13:54:42 +03:00 committed by emery
parent e4714db2d5
commit 1c898f7ddc
5 changed files with 277 additions and 0 deletions

View file

@ -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 ];
})

View file

@ -0,0 +1,6 @@
#%rec:emFpPlugin%#
FileTypes = { ".avif" }
Priority = 1.0
Library = "PlAvif"
Function = "PlAvifFpPluginFunc"

View file

@ -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;
}

View file

@ -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
)
);
}
}

View file

@ -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
];
};