about summary refs log tree commit diff
path: root/tools/eaglemode/plugins/avif/src/PlAvif.cpp
#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
			)
		);
	}
}