Translators: Add an AVIF translator

This translator only supports still images for now, and supports both
decoding and encoding.

Encoding support has been tested only with aom, rav1e doesn’t build on
Haiku yet, see https://github.com/haikuports/haikuports/pull/5534 for
one of the missing dependencies.

Change-Id: I716f4b862ed316b89b227bfed38072d72074201f
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3040
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
This commit is contained in:
Emmanuel Gil Peyrot 2020-07-16 14:29:19 +00:00 committed by Jérôme Duval
parent 6bb8ecb02b
commit 147b47e086
14 changed files with 1039 additions and 1 deletions

View File

@ -60,7 +60,7 @@ if $(HAIKU_PACKAGING_ARCHS[2]) {
freetype icu libsolv zlib
regular_image @{
ffmpeg glu jasper jpeg libicns libpng16 libwebp mesa
ffmpeg glu jasper jpeg libavif libicns libpng16 libwebp mesa
}@
] ;
if $(TARGET_PACKAGING_ARCH) != x86_gcc2 {

View File

@ -650,6 +650,26 @@ if [ IsPackageAvailable libwebp_devel ] {
}
# libavif
if [ IsPackageAvailable libavif_devel ] {
if $(HAIKU_PACKAGING_ARCH) = x86_64 {
ExtractBuildFeatureArchives libavif :
file: base libavif
runtime: lib
file: devel libavif_devel
depends: base
library: $(developLibDir)/libavif.so.12
headers: $(developHeadersDir) $(developHeadersDir)/avif
;
EnableBuildFeatures libavif ;
} else {
unavailableBuildFeatures += libavif ;
}
} else {
unavailableBuildFeatures += libavif ;
}
# live555
if [ IsPackageAvailable live555_devel ] {
ExtractBuildFeatureArchives live555 :

View File

@ -124,6 +124,7 @@ SYSTEM_ADD_ONS_ACCELERANTS += [ FFilterByBuildFeatures
] ;
SYSTEM_ADD_ONS_TRANSLATORS += [ FFilterByBuildFeatures
AVIFTranslator@libavif
BMPTranslator
EXRTranslator@openexr
GIFTranslator

View File

@ -1,5 +1,6 @@
SubDir HAIKU_TOP src add-ons translators ;
SubInclude HAIKU_TOP src add-ons translators avif ;
SubInclude HAIKU_TOP src add-ons translators bmp ;
SubInclude HAIKU_TOP src add-ons translators exr ;
SubInclude HAIKU_TOP src add-ons translators gif ;

View File

@ -0,0 +1,532 @@
/*
* Copyright 2021, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Emmanuel Gil Peyrot
*/
#include "AVIFTranslator.h"
#include <BufferIO.h>
#include <Catalog.h>
#include <Messenger.h>
#include <TranslatorRoster.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "avif/avif.h"
#include "ConfigView.h"
#include "TranslatorSettings.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "AVIFTranslator"
class FreeAllocation {
public:
FreeAllocation(void* buffer)
:
fBuffer(buffer)
{
}
~FreeAllocation()
{
free(fBuffer);
}
private:
void* fBuffer;
};
// The input formats that this translator knows how to read
static const translation_format sInputFormats[] = {
{
AVIF_IMAGE_FORMAT,
B_TRANSLATOR_BITMAP,
AVIF_IN_QUALITY,
AVIF_IN_CAPABILITY,
"image/avif",
"AV1 Image File Format"
},
{
B_TRANSLATOR_BITMAP,
B_TRANSLATOR_BITMAP,
BITS_IN_QUALITY,
BITS_IN_CAPABILITY,
"image/x-be-bitmap",
"Be Bitmap Format (AVIFTranslator)"
},
};
// The output formats that this translator knows how to write
static const translation_format sOutputFormats[] = {
{
AVIF_IMAGE_FORMAT,
B_TRANSLATOR_BITMAP,
AVIF_OUT_QUALITY,
AVIF_OUT_CAPABILITY,
"image/avif",
"AV1 Image File Format"
},
{
B_TRANSLATOR_BITMAP,
B_TRANSLATOR_BITMAP,
BITS_OUT_QUALITY,
BITS_OUT_CAPABILITY,
"image/x-be-bitmap",
"Be Bitmap Format (AVIFTranslator)"
},
};
// Default settings for the Translator
static const TranSetting sDefaultSettings[] = {
{ B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false },
{ B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false },
{ AVIF_SETTING_LOSSLESS, TRAN_SETTING_BOOL, false },
{ AVIF_SETTING_PIXEL_FORMAT, TRAN_SETTING_INT32,
AVIF_PIXEL_FORMAT_YUV444 },
{ AVIF_SETTING_QUALITY, TRAN_SETTING_INT32, 60 },
{ AVIF_SETTING_SPEED, TRAN_SETTING_INT32, -1 },
{ AVIF_SETTING_TILES_HORIZONTAL, TRAN_SETTING_INT32, 1 },
{ AVIF_SETTING_TILES_VERTICAL, TRAN_SETTING_INT32, 1 },
};
const uint32 kNumInputFormats = sizeof(sInputFormats) /
sizeof(translation_format);
const uint32 kNumOutputFormats = sizeof(sOutputFormats) /
sizeof(translation_format);
const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) /
sizeof(TranSetting);
// #pragma mark -
AVIFTranslator::AVIFTranslator()
:
BaseTranslator(B_TRANSLATE("AVIF images"),
B_TRANSLATE("AVIF image translator"),
AVIF_TRANSLATOR_VERSION,
sInputFormats, kNumInputFormats,
sOutputFormats, kNumOutputFormats,
"AVIFTranslator_Settings", sDefaultSettings,
kNumDefaultSettings, B_TRANSLATOR_BITMAP, AVIF_IMAGE_FORMAT)
{
}
AVIFTranslator::~AVIFTranslator()
{
}
status_t
AVIFTranslator::DerivedIdentify(BPositionIO* stream,
const translation_format* format, BMessage* settings,
translator_info* info, uint32 outType)
{
(void)format;
(void)settings;
if (!outType)
outType = B_TRANSLATOR_BITMAP;
if (outType != B_TRANSLATOR_BITMAP)
return B_NO_TRANSLATOR;
// Read header and first chunck bytes...
uint32 buf[64];
ssize_t size = sizeof(buf);
if (stream->Read(buf, size) != size)
return B_IO_ERROR;
// Check it's a valid AVIF format
avifROData data;
data.data = reinterpret_cast<const uint8_t*>(buf);
data.size = static_cast<size_t>(size);
if (!avifPeekCompatibleFileType(&data))
return B_ILLEGAL_DATA;
info->type = AVIF_IMAGE_FORMAT;
info->group = B_TRANSLATOR_BITMAP;
info->quality = AVIF_IN_QUALITY;
info->capability = AVIF_IN_CAPABILITY;
snprintf(info->name, sizeof(info->name), B_TRANSLATE("AVIF image"));
strcpy(info->MIME, "image/avif");
return B_OK;
}
status_t
AVIFTranslator::DerivedTranslate(BPositionIO* stream,
const translator_info* info, BMessage* ioExtension, uint32 outType,
BPositionIO* target, int32 baseType)
{
(void)info;
if (baseType == 1)
// if stream is in bits format
return _TranslateFromBits(stream, ioExtension, outType, target);
else if (baseType == 0)
// if stream is NOT in bits format
return _TranslateFromAVIF(stream, ioExtension, outType, target);
else
// if BaseTranslator dit not properly identify the data as
// bits or not bits
return B_NO_TRANSLATOR;
}
BView*
AVIFTranslator::NewConfigView(TranslatorSettings* settings)
{
return new ConfigView(settings);
}
status_t
AVIFTranslator::_TranslateFromBits(BPositionIO* stream, BMessage* ioExtension,
uint32 outType, BPositionIO* target)
{
// FIXME: This codepath is completely untested for now, due to libavif
// being built without any encoder in haikuports.
(void)ioExtension;
if (!outType)
outType = AVIF_IMAGE_FORMAT;
if (outType != AVIF_IMAGE_FORMAT)
return B_NO_TRANSLATOR;
TranslatorBitmap bitsHeader;
status_t status;
status = identify_bits_header(stream, NULL, &bitsHeader);
if (status != B_OK)
return status;
avifPixelFormat format = static_cast<avifPixelFormat>(
fSettings->SetGetInt32(AVIF_SETTING_PIXEL_FORMAT));
int32 bytesPerPixel;
avifRGBFormat rgbFormat;
bool isRGB = true;
bool ignoreAlpha = false;
switch (bitsHeader.colors) {
case B_RGB32:
rgbFormat = AVIF_RGB_FORMAT_BGRA;
ignoreAlpha = true;
bytesPerPixel = 4;
break;
case B_RGB32_BIG:
rgbFormat = AVIF_RGB_FORMAT_ARGB;
ignoreAlpha = true;
bytesPerPixel = 4;
break;
case B_RGBA32:
rgbFormat = AVIF_RGB_FORMAT_BGRA;
bytesPerPixel = 4;
break;
case B_RGBA32_BIG:
rgbFormat = AVIF_RGB_FORMAT_ARGB;
bytesPerPixel = 4;
break;
case B_RGB24:
rgbFormat = AVIF_RGB_FORMAT_BGR;
bytesPerPixel = 3;
break;
case B_RGB24_BIG:
rgbFormat = AVIF_RGB_FORMAT_RGB;
bytesPerPixel = 3;
break;
case B_YCbCr444:
bytesPerPixel = 3;
isRGB = false;
break;
case B_GRAY8:
bytesPerPixel = 1;
isRGB = false;
break;
default:
printf("ERROR: Colorspace not supported: %d\n",
bitsHeader.colors);
return B_NO_TRANSLATOR;
}
int width = bitsHeader.bounds.IntegerWidth() + 1;
int height = bitsHeader.bounds.IntegerHeight() + 1;
int depth = 8;
avifImage* image = avifImageCreate(width, height, depth, format);
image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY;
if (isRGB) {
image->yuvRange = AVIF_RANGE_FULL;
avifRGBImage rgb;
avifRGBImageSetDefaults(&rgb, image);
rgb.depth = depth;
rgb.format = rgbFormat;
rgb.ignoreAlpha = ignoreAlpha;
int bitsSize = height * bitsHeader.rowBytes;
rgb.pixels = static_cast<uint8_t*>(malloc(bitsSize));
if (rgb.pixels == NULL)
return B_NO_MEMORY;
rgb.rowBytes = bitsHeader.rowBytes;
if (stream->Read(rgb.pixels, bitsSize) != bitsSize) {
free(rgb.pixels);
return B_IO_ERROR;
}
avifResult conversionResult = avifImageRGBToYUV(image, &rgb);
free(rgb.pixels);
if (conversionResult != AVIF_RESULT_OK)
return B_ERROR;
} else if (bytesPerPixel == 3) {
// TODO: Investigate moving that to libavif instead, and do so
// for other Y'CbCr formats too.
//
// See also https://github.com/AOMediaCodec/libavif/pull/235
assert(bitsHeader.colors == B_YCbCr444);
int bitsSize = height * bitsHeader.rowBytes;
uint8_t* pixels = static_cast<uint8_t*>(malloc(bitsSize));
if (stream->Read(pixels, bitsSize) != bitsSize)
return B_IO_ERROR;
uint8_t* luma = static_cast<uint8_t*>(malloc(bitsSize / 3));
uint8_t* cb = static_cast<uint8_t*>(malloc(bitsSize / 3));
uint8_t* cr = static_cast<uint8_t*>(malloc(bitsSize / 3));
for (int i = 0; i < bitsSize / 3; ++i) {
luma[i] = pixels[3 * i + 0];
cb[i] = pixels[3 * i + 1];
cr[i] = pixels[3 * i + 2];
}
image->yuvPlanes[0] = luma;
image->yuvPlanes[1] = cb;
image->yuvPlanes[2] = cr;
image->yuvRowBytes[0] = bitsHeader.rowBytes / 3;
image->yuvRowBytes[1] = bitsHeader.rowBytes / 3;
image->yuvRowBytes[2] = bitsHeader.rowBytes / 3;
image->yuvRange = AVIF_RANGE_LIMITED;
} else {
assert(bitsHeader.colors == B_GRAY8);
int bitsSize = height * bitsHeader.rowBytes;
uint8_t* luma = static_cast<uint8_t*>(malloc(bitsSize));
if (stream->Read(luma, bitsSize) != bitsSize)
return B_IO_ERROR;
image->yuvPlanes[0] = luma;
image->yuvPlanes[1] = nullptr;
image->yuvPlanes[2] = nullptr;
image->yuvRowBytes[0] = bitsHeader.rowBytes;
image->yuvRowBytes[1] = 0;
image->yuvRowBytes[2] = 0;
}
avifRWData output = AVIF_DATA_EMPTY;
avifEncoder* encoder = avifEncoderCreate();
system_info info;
encoder->maxThreads = (get_system_info(&info) == B_OK) ?
info.cpu_count : 1;
if (fSettings->SetGetBool(AVIF_SETTING_LOSSLESS)) {
encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS;
encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
} else {
encoder->minQuantizer = encoder->maxQuantizer
= fSettings->SetGetInt32(AVIF_SETTING_QUALITY);
}
encoder->speed = fSettings->SetGetInt32(AVIF_SETTING_SPEED);
encoder->tileColsLog2
= fSettings->SetGetInt32(AVIF_SETTING_TILES_HORIZONTAL);
encoder->tileRowsLog2
= fSettings->SetGetInt32(AVIF_SETTING_TILES_VERTICAL);
avifResult encodeResult = avifEncoderWrite(encoder, image, &output);
avifImageDestroy(image);
avifEncoderDestroy(encoder);
if (encodeResult != AVIF_RESULT_OK) {
printf("ERROR: Failed to encode: %s\n",
avifResultToString(encodeResult));
avifRWDataFree(&output);
return B_ERROR;
}
// output contains a valid .avif file's contents
target->Write(output.data, output.size);
avifRWDataFree(&output);
return B_OK;
}
status_t
AVIFTranslator::_TranslateFromAVIF(BPositionIO* stream, BMessage* ioExtension,
uint32 outType, BPositionIO* target)
{
if (!outType)
outType = B_TRANSLATOR_BITMAP;
if (outType != B_TRANSLATOR_BITMAP)
return B_NO_TRANSLATOR;
off_t streamLength = 0;
stream->GetSize(&streamLength);
off_t streamSize = stream->Seek(0, SEEK_END);
stream->Seek(0, SEEK_SET);
void* streamData = malloc(streamSize);
if (streamData == NULL)
return B_NO_MEMORY;
if (stream->Read(streamData, streamSize) != streamSize) {
free(streamData);
return B_IO_ERROR;
}
avifDecoder* decoder = avifDecoderCreate();
if (decoder == NULL) {
free(streamData);
return B_NO_MEMORY;
}
avifResult setIOMemoryResult = avifDecoderSetIOMemory(decoder,
reinterpret_cast<const uint8_t *>(streamData), streamSize);
if (setIOMemoryResult != AVIF_RESULT_OK) {
free(streamData);
return B_NO_MEMORY;
}
avifResult decodeResult = avifDecoderParse(decoder);
if (decodeResult != AVIF_RESULT_OK) {
free(streamData);
return B_ILLEGAL_DATA;
}
// We dont support animations yet.
if (decoder->imageCount != 1) {
free(streamData);
return B_ILLEGAL_DATA;
}
avifResult nextImageResult = avifDecoderNextImage(decoder);
free(streamData);
if (nextImageResult != AVIF_RESULT_OK)
return B_ILLEGAL_DATA;
avifImage* image = decoder->image;
int width = image->width;
int height = image->height;
avifRGBFormat format;
uint8_t* pixels;
uint32_t rowBytes;
color_space colors;
bool convertToRGB = true;
if (image->alphaPlane) {
format = AVIF_RGB_FORMAT_BGRA;
colors = B_RGBA32;
} else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
colors = B_GRAY8;
convertToRGB = false;
} else {
format = AVIF_RGB_FORMAT_BGR;
colors = B_RGB24;
}
if (convertToRGB) {
avifRGBImage rgb;
avifRGBImageSetDefaults(&rgb, image);
rgb.depth = 8;
rgb.format = format;
avifRGBImageAllocatePixels(&rgb);
avifResult conversionResult = avifImageYUVToRGB(image, &rgb);
if (conversionResult != AVIF_RESULT_OK)
return B_ILLEGAL_DATA;
pixels = rgb.pixels;
rowBytes = rgb.rowBytes;
} else {
// TODO: Add a downsampling (with dithering?) path here, or
// alternatively add support for higher bit depth to Haiku
// bitmaps, possibly with HDR too.
if (image->depth > 8)
return B_ILLEGAL_DATA;
// TODO: Add support for more than just the luma plane.
pixels = image->yuvPlanes[0];
rowBytes = image->yuvRowBytes[0];
}
uint32 dataSize = rowBytes * height;
TranslatorBitmap bitmapHeader;
bitmapHeader.magic = B_TRANSLATOR_BITMAP;
bitmapHeader.bounds.Set(0, 0, width - 1, height - 1);
bitmapHeader.rowBytes = rowBytes;
bitmapHeader.colors = colors;
bitmapHeader.dataSize = dataSize;
// write out Be's Bitmap header
swap_data(B_UINT32_TYPE, &bitmapHeader, sizeof(TranslatorBitmap),
B_SWAP_HOST_TO_BENDIAN);
ssize_t bytesWritten = target->Write(&bitmapHeader,
sizeof(TranslatorBitmap));
if (bytesWritten < B_OK)
return bytesWritten;
if ((size_t)bytesWritten != sizeof(TranslatorBitmap))
return B_IO_ERROR;
bool headerOnly = false;
if (ioExtension != NULL)
ioExtension->FindBool(B_TRANSLATOR_EXT_HEADER_ONLY,
&headerOnly);
if (headerOnly)
return B_OK;
bytesWritten = target->Write(pixels, dataSize);
if (bytesWritten < B_OK)
return bytesWritten;
return B_OK;
}
// #pragma mark -
BTranslator*
make_nth_translator(int32 n, image_id you, uint32 flags, ...)
{
(void)you;
(void)flags;
if (n != 0)
return NULL;
return new AVIFTranslator();
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2021, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Emmanuel Gil Peyrot
*/
#ifndef AVIF_TRANSLATOR_H
#define AVIF_TRANSLATOR_H
#include <ByteOrder.h>
#include <DataIO.h>
#include <File.h>
#include <fs_attr.h>
#include <GraphicsDefs.h>
#include <InterfaceDefs.h>
#include <TranslationDefs.h>
#include <Translator.h>
#include <TranslatorFormats.h>
#include "BaseTranslator.h"
#define AVIF_TRANSLATOR_VERSION B_TRANSLATION_MAKE_VERSION(0,1,0)
#define AVIF_IMAGE_FORMAT 'AVIF'
#define AVIF_SETTING_LOSSLESS "lossless"
#define AVIF_SETTING_PIXEL_FORMAT "pixfmt"
#define AVIF_SETTING_QUALITY "quality"
#define AVIF_SETTING_SPEED "speed"
#define AVIF_SETTING_TILES_HORIZONTAL "htiles"
#define AVIF_SETTING_TILES_VERTICAL "vtiles"
#define AVIF_IN_QUALITY 0.90
#define AVIF_IN_CAPABILITY 0.90
#define AVIF_OUT_QUALITY 0.90
#define AVIF_OUT_CAPABILITY 0.5
#define BITS_IN_QUALITY 0.8
#define BITS_IN_CAPABILITY 0.6
#define BITS_OUT_QUALITY 0.5
#define BITS_OUT_CAPABILITY 0.4
struct AVIFPicture;
class AVIFTranslator : public BaseTranslator {
public:
AVIFTranslator();
virtual status_t DerivedIdentify(BPositionIO* stream,
const translation_format* format,
BMessage* settings, translator_info* info,
uint32 outType);
virtual status_t DerivedTranslate(BPositionIO* stream,
const translator_info* info,
BMessage* settings, uint32 outType,
BPositionIO* target, int32 baseType);
virtual BView* NewConfigView(TranslatorSettings* settings);
protected:
virtual ~AVIFTranslator();
// this is protected because the object is deleted by the
// Release() function instead of being deleted directly by
// the user
private:
status_t _TranslateFromBits(BPositionIO* stream,
BMessage* settings, uint32 outType,
BPositionIO* target);
status_t _TranslateFromAVIF(BPositionIO* stream,
BMessage* settings, uint32 outType,
BPositionIO* target);
};
#endif // #ifndef AVIF_TRANSLATOR_H

View File

@ -0,0 +1,15 @@
/*
* AVIFTranslator.rdef
*/
resource app_signature "application/x-vnd.Haiku-AVIFTranslator";
resource app_version {
major = 0,
middle = 1,
minor = 0,
variety = 0,
internal = 0,
short_info = "0.1.0",
long_info = "Haiku AVIFTranslator Add-Ons."
};

View File

@ -0,0 +1,255 @@
/*
* Copyright 2021, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Emmanuel Gil Peyrot
*/
#include "ConfigView.h"
#include <stdio.h>
#include <string.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <LayoutBuilder.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <Message.h>
#include <PopUpMenu.h>
#include <Slider.h>
#include <StringView.h>
#include "avif/avif.h"
#include "TranslatorSettings.h"
#include "AVIFTranslator.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ConfigView"
static const uint32 kMsgLossless = 'losl';
static const uint32 kMsgPixelFormat = 'pfmt';
static const uint32 kMsgQuality = 'qlty';
static const uint32 kMsgSpeed = 'sped';
static const uint32 kMsgTilesHorizontal = 'tilh';
static const uint32 kMsgTilesVertical = 'tilv';
ConfigView::ConfigView(TranslatorSettings* settings)
:
BGroupView(B_TRANSLATE("AVIFTranslator Settings"), B_VERTICAL),
fSettings(settings)
{
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
BStringView* title = new BStringView("title",
B_TRANSLATE("AVIF image translator"));
title->SetFont(be_bold_font);
char versionString[256];
sprintf(versionString, "v%d.%d.%d, %s",
static_cast<int>(B_TRANSLATION_MAJOR_VERSION(AVIF_TRANSLATOR_VERSION)),
static_cast<int>(B_TRANSLATION_MINOR_VERSION(AVIF_TRANSLATOR_VERSION)),
static_cast<int>(B_TRANSLATION_REVISION_VERSION(
AVIF_TRANSLATOR_VERSION)),
__DATE__);
BStringView* version = new BStringView("version", versionString);
BString copyrightsText;
BStringView *copyrightView = new BStringView("Copyright",
B_TRANSLATE(B_UTF8_COPYRIGHT "2021 Emmanuel Gil Peyrot"));
BString libavifInfo = B_TRANSLATE(
"Based on libavif %version%");
libavifInfo.ReplaceAll("%version%", avifVersion());
BStringView *copyright2View = new BStringView("Copyright2",
libavifInfo.String());
BStringView *copyright3View = new BStringView("Copyright3",
B_TRANSLATE(B_UTF8_COPYRIGHT "2019 Joe Drago. All rights reserved."));
// output parameters
fLosslessCheckBox = new BCheckBox("lossless",
B_TRANSLATE("Lossless"), new BMessage(kMsgLossless));
bool lossless;
fSettings->SetGetBool(AVIF_SETTING_LOSSLESS, &lossless);
if (lossless)
fLosslessCheckBox->SetValue(B_CONTROL_ON);
fPixelFormatMenu = new BPopUpMenu(B_TRANSLATE("Pixel format"));
static const avifPixelFormat pixelFormats[4] = {
AVIF_PIXEL_FORMAT_YUV444,
AVIF_PIXEL_FORMAT_YUV420,
AVIF_PIXEL_FORMAT_YUV400,
AVIF_PIXEL_FORMAT_YUV422,
};
for (size_t i = 0; i < 4; ++i) {
BMessage* msg = new BMessage(kMsgPixelFormat);
msg->AddInt32("value", pixelFormats[i]);
BMenuItem* item = new BMenuItem(
avifPixelFormatToString(pixelFormats[i]), msg);
if (fSettings->SetGetInt32(AVIF_SETTING_PIXEL_FORMAT) == pixelFormats[i])
item->SetMarked(true);
fPixelFormatMenu->AddItem(item);
}
BMenuField* pixelFormatField = new BMenuField(B_TRANSLATE("Pixel format:"),
fPixelFormatMenu);
rgb_color barColor = { 0, 0, 229, 255 };
fQualitySlider = new BSlider("quality", B_TRANSLATE("Output quality:"),
new BMessage(kMsgQuality), AVIF_QUANTIZER_BEST_QUALITY,
AVIF_QUANTIZER_WORST_QUALITY, B_HORIZONTAL, B_BLOCK_THUMB);
fQualitySlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
fQualitySlider->SetHashMarkCount(8);
fQualitySlider->SetLimitLabels(B_TRANSLATE("Best"), B_TRANSLATE("Worst"));
fQualitySlider->UseFillColor(true, &barColor);
fQualitySlider->SetValue(fSettings->SetGetInt32(AVIF_SETTING_QUALITY));
fQualitySlider->SetEnabled(!lossless);
fSpeedSlider = new BSlider("speed", B_TRANSLATE("Compression speed:"),
new BMessage(kMsgSpeed), AVIF_SPEED_SLOWEST,
AVIF_SPEED_FASTEST, B_HORIZONTAL, B_BLOCK_THUMB);
fSpeedSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
fSpeedSlider->SetHashMarkCount(11);
fSpeedSlider->SetLimitLabels(B_TRANSLATE("Slow"),
B_TRANSLATE("Faster but worse quality"));
fSpeedSlider->UseFillColor(true, &barColor);
fSpeedSlider->SetValue(fSettings->SetGetInt32(AVIF_SETTING_SPEED));
fHTilesSlider = new BSlider("htiles", B_TRANSLATE("Horizontal tiles:"),
new BMessage(kMsgTilesHorizontal), 1, 6, B_HORIZONTAL,
B_BLOCK_THUMB);
fHTilesSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
fHTilesSlider->SetHashMarkCount(6);
fHTilesSlider->SetLimitLabels(B_TRANSLATE("1"),
B_TRANSLATE("2⁶"));
fHTilesSlider->UseFillColor(true, &barColor);
fHTilesSlider->SetValue(
fSettings->SetGetInt32(AVIF_SETTING_TILES_HORIZONTAL));
fVTilesSlider = new BSlider("vtiles", B_TRANSLATE("Vertical tiles:"),
new BMessage(kMsgTilesVertical), 1, 6, B_HORIZONTAL,
B_BLOCK_THUMB);
fVTilesSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
fVTilesSlider->SetHashMarkCount(6);
fVTilesSlider->SetLimitLabels(B_TRANSLATE("1"),
B_TRANSLATE("2⁶"));
fVTilesSlider->UseFillColor(true, &barColor);
fVTilesSlider->SetValue(
fSettings->SetGetInt32(AVIF_SETTING_TILES_VERTICAL));
// Build the layout
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
.SetInsets(B_USE_DEFAULT_SPACING)
.Add(title)
.Add(version)
.Add(copyrightView)
.AddGlue()
.Add(fLosslessCheckBox)
.AddGrid(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING)
.Add(pixelFormatField->CreateLabelLayoutItem(), 0, 0)
.AddGroup(B_HORIZONTAL, 0.0f, 1, 0)
.Add(pixelFormatField->CreateMenuBarLayoutItem(), 0.0f)
.AddGlue()
.End()
.End()
.Add(fQualitySlider)
.Add(fSpeedSlider)
.Add(fHTilesSlider)
.Add(fVTilesSlider)
.AddGlue()
.Add(copyright2View)
.Add(copyright3View);
BFont font;
GetFont(&font);
SetExplicitPreferredSize(BSize((font.Size() * 250) / 12,
(font.Size() * 350) / 12));
}
ConfigView::~ConfigView()
{
fSettings->Release();
}
void
ConfigView::AttachedToWindow()
{
BGroupView::AttachedToWindow();
fQualitySlider->SetTarget(this);
fSpeedSlider->SetTarget(this);
fHTilesSlider->SetTarget(this);
fVTilesSlider->SetTarget(this);
if (Parent() == NULL && Window()->GetLayout() == NULL) {
Window()->SetLayout(new BGroupLayout(B_VERTICAL));
Window()->ResizeTo(PreferredSize().Width(),
PreferredSize().Height());
}
}
void
ConfigView::MessageReceived(BMessage* message)
{
struct {
const char* name;
uint32 what;
TranSettingType type;
} maps[] = {
{ AVIF_SETTING_LOSSLESS, kMsgLossless, TRAN_SETTING_BOOL },
{ AVIF_SETTING_PIXEL_FORMAT, kMsgPixelFormat,
TRAN_SETTING_INT32 },
{ AVIF_SETTING_QUALITY, kMsgQuality, TRAN_SETTING_INT32 },
{ AVIF_SETTING_SPEED, kMsgSpeed, TRAN_SETTING_INT32 },
{ AVIF_SETTING_TILES_HORIZONTAL, kMsgTilesHorizontal,
TRAN_SETTING_INT32 },
{ AVIF_SETTING_TILES_VERTICAL, kMsgTilesVertical,
TRAN_SETTING_INT32 },
{ NULL }
};
int i;
for (i = 0; maps[i].name != NULL; i++) {
if (maps[i].what == message->what)
break;
}
if (maps[i].name == NULL) {
BGroupView::MessageReceived(message);
return;
}
int32 value;
if (message->FindInt32("value", &value) == B_OK
|| message->FindInt32("be:value", &value) == B_OK) {
switch(maps[i].type) {
case TRAN_SETTING_BOOL:
{
bool boolValue = value;
fSettings->SetGetBool(maps[i].name, &boolValue);
if (maps[i].what == kMsgLossless)
fSpeedSlider->SetEnabled(!boolValue);
break;
}
case TRAN_SETTING_INT32:
fSettings->SetGetInt32(maps[i].name, &value);
break;
}
fSettings->SaveSettings();
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2021, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Emmanuel Gil Peyrot
*/
#ifndef CONFIG_VIEW_H
#define CONFIG_VIEW_H
#include <GroupView.h>
class BCheckBox;
class BPopUpMenu;
class BSlider;
class TranslatorSettings;
class ConfigView : public BGroupView {
public:
ConfigView(TranslatorSettings* settings);
virtual ~ConfigView();
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage *message);
private:
TranslatorSettings* fSettings;
BPopUpMenu* fPixelFormatMenu;
BCheckBox* fLosslessCheckBox;
BSlider* fQualitySlider;
BSlider* fSpeedSlider;
BSlider* fHTilesSlider;
BSlider* fVTilesSlider;
};
#endif /* CONFIG_VIEW_H */

View File

@ -0,0 +1,38 @@
SubDir HAIKU_TOP src add-ons translators avif ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) shared ] ;
AddResources AVIFTranslator : AVIFTranslator.rdef ;
local architectureObject ;
for architectureObject in [ MultiArchSubDirSetup ] {
on $(architectureObject) {
UseBuildFeatureHeaders libavif ;
Translator [ MultiArchDefaultGristFiles AVIFTranslator ] :
main.cpp
AVIFTranslator.cpp
ConfigView.cpp
:
be translation [ MultiArchDefaultGristFiles libtranslatorsutils.a ]
[ BuildFeatureAttribute libavif : library ]
[ TargetLibsupc++ ] localestub
: true
;
Includes [ FGristFiles ConfigView.cpp AVIFTranslator.cpp ]
: [ BuildFeatureAttribute libavif : headers ] ;
}
}
DoCatalogs AVIFTranslator :
x-vnd.Haiku-AVIFTranslator
:
main.cpp
ConfigView.cpp
AVIFTranslator.cpp
;

View File

@ -0,0 +1,31 @@
/*
* Copyright 2021, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Emmanuel Gil Peyrot
*/
#include <Application.h>
#include <Catalog.h>
#include "TranslatorWindow.h"
#include "AVIFTranslator.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "main"
int
main()
{
BApplication app("application/x-vnd.Haiku-AVIFTranslator");
if (LaunchTranslatorWindow(new AVIFTranslator,
B_TRANSLATE("AVIF Settings")) != B_OK)
return 1;
app.Run();
return 0;
}

View File

@ -1398,6 +1398,13 @@ AboutView::_CreateCreditsView()
.SetLicense(kBSDThreeClause)
.SetURL("http://www.webmproject.org/code/#libwebp_webp_image_library"));
// libavif
_AddPackageCredit(PackageCredit("libavif")
.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING
"2019 Joe Drago. All rights reserved."))
.SetLicense(kBSDThreeClause)
.SetURL("https://github.com/AOMediaCodec/libavif"));
// GTF
_AddPackageCredit(PackageCredit("GTF")
.SetCopyright(B_TRANSLATE("2001 by Andy Ritger based on the "

View File

@ -0,0 +1,13 @@
resource(0, "BEOS:TYPE") #'MIMS' "application/x-vnd.Be-meta-mime";
resource(1, "META:TYPE") "image/avif";
resource(2, "META:SNIFF_RULE") "0.50 \(\"\\000\\000\\000 ftypavif\"\)";
resource(3, "META:S:DESC") #'MSDC' "AV1 Image File Format";
resource(4, "META:EXTENS") message(234) {
"extensions" = "avif",
"type" = "image/avif"
};

View File

@ -130,6 +130,9 @@ requires {
#ifdef HAIKU_BUILD_FEATURE_%HAIKU_PACKAGING_ARCH%_libpng_ENABLED
lib:libpng16
#endif
#ifdef HAIKU_BUILD_FEATURE_%HAIKU_PACKAGING_ARCH%_libavif_ENABLED
lib:libavif >= 12
#endif
#ifdef HAIKU_BUILD_FEATURE_%HAIKU_PACKAGING_ARCH%_openssl_ENABLED
lib:libssl
lib:libcrypto