1/*
2 * Copyright 2021, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Emmanuel Gil Peyrot
7 */
8
9
10#include "AVIFTranslator.h"
11
12#include <BufferIO.h>
13#include <Catalog.h>
14#include <Messenger.h>
15#include <TranslatorRoster.h>
16
17#include <assert.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include "avif/avif.h"
23
24#include "ConfigView.h"
25#include "TranslatorSettings.h"
26
27
28#undef B_TRANSLATION_CONTEXT
29#define B_TRANSLATION_CONTEXT "AVIFTranslator"
30
31
32class FreeAllocation {
33	public:
34		FreeAllocation(void* buffer)
35			:
36			fBuffer(buffer)
37		{
38		}
39
40		~FreeAllocation()
41		{
42			free(fBuffer);
43		}
44
45	private:
46		void*	fBuffer;
47};
48
49
50// The input formats that this translator knows how to read
51static const translation_format sInputFormats[] = {
52	{
53		AVIF_IMAGE_FORMAT,
54		B_TRANSLATOR_BITMAP,
55		AVIF_IN_QUALITY,
56		AVIF_IN_CAPABILITY,
57		"image/avif",
58		"AV1 Image File Format"
59	},
60	{
61		B_TRANSLATOR_BITMAP,
62		B_TRANSLATOR_BITMAP,
63		BITS_IN_QUALITY,
64		BITS_IN_CAPABILITY,
65		"image/x-be-bitmap",
66		"Be Bitmap Format (AVIFTranslator)"
67	},
68};
69
70
71// The output formats that this translator knows how to write
72static const translation_format sOutputFormats[] = {
73	{
74		AVIF_IMAGE_FORMAT,
75		B_TRANSLATOR_BITMAP,
76		AVIF_OUT_QUALITY,
77		AVIF_OUT_CAPABILITY,
78		"image/avif",
79		"AV1 Image File Format"
80	},
81	{
82		B_TRANSLATOR_BITMAP,
83		B_TRANSLATOR_BITMAP,
84		BITS_OUT_QUALITY,
85		BITS_OUT_CAPABILITY,
86		"image/x-be-bitmap",
87		"Be Bitmap Format (AVIFTranslator)"
88	},
89};
90
91// Default settings for the Translator
92static const TranSetting sDefaultSettings[] = {
93	{ B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false },
94	{ B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false },
95	{ AVIF_SETTING_LOSSLESS, TRAN_SETTING_BOOL, false },
96	{ AVIF_SETTING_PIXEL_FORMAT, TRAN_SETTING_INT32,
97		AVIF_PIXEL_FORMAT_YUV444 },
98	{ AVIF_SETTING_QUALITY, TRAN_SETTING_INT32, 60 },
99	{ AVIF_SETTING_SPEED, TRAN_SETTING_INT32, -1 },
100	{ AVIF_SETTING_TILES_HORIZONTAL, TRAN_SETTING_INT32, 1 },
101	{ AVIF_SETTING_TILES_VERTICAL, TRAN_SETTING_INT32, 1 },
102};
103
104const uint32 kNumInputFormats = sizeof(sInputFormats) /
105	sizeof(translation_format);
106const uint32 kNumOutputFormats = sizeof(sOutputFormats) /
107	sizeof(translation_format);
108const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) /
109	sizeof(TranSetting);
110
111
112//	#pragma mark -
113
114
115AVIFTranslator::AVIFTranslator()
116	:
117	BaseTranslator(B_TRANSLATE("AVIF images"),
118		B_TRANSLATE("AVIF image translator"),
119		AVIF_TRANSLATOR_VERSION,
120		sInputFormats, kNumInputFormats,
121		sOutputFormats, kNumOutputFormats,
122		"AVIFTranslator_Settings", sDefaultSettings,
123		kNumDefaultSettings, B_TRANSLATOR_BITMAP, AVIF_IMAGE_FORMAT)
124{
125}
126
127
128AVIFTranslator::~AVIFTranslator()
129{
130}
131
132
133status_t
134AVIFTranslator::DerivedIdentify(BPositionIO* stream,
135	const translation_format* format, BMessage* settings,
136	translator_info* info, uint32 outType)
137{
138	(void)format;
139	(void)settings;
140	if (!outType)
141		outType = B_TRANSLATOR_BITMAP;
142	if (outType != B_TRANSLATOR_BITMAP)
143		return B_NO_TRANSLATOR;
144
145	// Read header and first chunck bytes...
146	uint32 buf[64];
147	ssize_t size = sizeof(buf);
148	if (stream->Read(buf, size) != size)
149		return B_IO_ERROR;
150
151	// Check it's a valid AVIF format
152	avifROData data;
153	data.data = reinterpret_cast<const uint8_t*>(buf);
154	data.size = static_cast<size_t>(size);
155	if (!avifPeekCompatibleFileType(&data))
156		return B_ILLEGAL_DATA;
157
158	info->type = AVIF_IMAGE_FORMAT;
159	info->group = B_TRANSLATOR_BITMAP;
160	info->quality = AVIF_IN_QUALITY;
161	info->capability = AVIF_IN_CAPABILITY;
162	snprintf(info->name, sizeof(info->name), B_TRANSLATE("AVIF image"));
163	strcpy(info->MIME, "image/avif");
164
165	return B_OK;
166}
167
168
169status_t
170AVIFTranslator::DerivedTranslate(BPositionIO* stream,
171	const translator_info* info, BMessage* ioExtension, uint32 outType,
172	BPositionIO* target, int32 baseType)
173{
174	(void)info;
175	if (baseType == 1)
176		// if stream is in bits format
177		return _TranslateFromBits(stream, ioExtension, outType, target);
178	else if (baseType == 0)
179		// if stream is NOT in bits format
180		return _TranslateFromAVIF(stream, ioExtension, outType, target);
181	else
182		// if BaseTranslator dit not properly identify the data as
183		// bits or not bits
184		return B_NO_TRANSLATOR;
185}
186
187
188BView*
189AVIFTranslator::NewConfigView(TranslatorSettings* settings)
190{
191	return new ConfigView(settings);
192}
193
194
195status_t
196AVIFTranslator::_TranslateFromBits(BPositionIO* stream, BMessage* ioExtension,
197	uint32 outType, BPositionIO* target)
198{
199	// FIXME: This codepath is completely untested for now, due to libavif
200	// being built without any encoder in haikuports.
201
202	(void)ioExtension;
203	if (!outType)
204		outType = AVIF_IMAGE_FORMAT;
205	if (outType != AVIF_IMAGE_FORMAT)
206		return B_NO_TRANSLATOR;
207
208	TranslatorBitmap bitsHeader;
209	status_t status;
210
211	status = identify_bits_header(stream, NULL, &bitsHeader);
212	if (status != B_OK)
213		return status;
214
215	avifPixelFormat format = static_cast<avifPixelFormat>(
216		fSettings->SetGetInt32(AVIF_SETTING_PIXEL_FORMAT));
217	int32 bytesPerPixel;
218	avifRGBFormat rgbFormat;
219	bool isRGB = true;
220	bool ignoreAlpha = false;
221	switch (bitsHeader.colors) {
222		case B_RGB32:
223			rgbFormat = AVIF_RGB_FORMAT_BGRA;
224			ignoreAlpha = true;
225			bytesPerPixel = 4;
226			break;
227
228		case B_RGB32_BIG:
229			rgbFormat = AVIF_RGB_FORMAT_ARGB;
230			ignoreAlpha = true;
231			bytesPerPixel = 4;
232			break;
233
234		case B_RGBA32:
235			rgbFormat = AVIF_RGB_FORMAT_BGRA;
236			bytesPerPixel = 4;
237			break;
238
239		case B_RGBA32_BIG:
240			rgbFormat = AVIF_RGB_FORMAT_ARGB;
241			bytesPerPixel = 4;
242			break;
243
244		case B_RGB24:
245			rgbFormat = AVIF_RGB_FORMAT_BGR;
246			bytesPerPixel = 3;
247			break;
248
249		case B_RGB24_BIG:
250			rgbFormat = AVIF_RGB_FORMAT_RGB;
251			bytesPerPixel = 3;
252			break;
253
254		case B_YCbCr444:
255			bytesPerPixel = 3;
256			isRGB = false;
257			break;
258
259		case B_GRAY8:
260			bytesPerPixel = 1;
261			isRGB = false;
262			break;
263
264		default:
265			printf("ERROR: Colorspace not supported: %d\n",
266				bitsHeader.colors);
267			return B_NO_TRANSLATOR;
268	}
269
270	int width = bitsHeader.bounds.IntegerWidth() + 1;
271	int height = bitsHeader.bounds.IntegerHeight() + 1;
272	int depth = 8;
273
274	avifImage* image = avifImageCreate(width, height, depth, format);
275	image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
276	image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
277	image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY;
278
279	if (isRGB) {
280		image->yuvRange = AVIF_RANGE_FULL;
281
282		avifRGBImage rgb;
283		avifRGBImageSetDefaults(&rgb, image);
284		rgb.depth = depth;
285		rgb.format = rgbFormat;
286		rgb.ignoreAlpha = ignoreAlpha;
287		int bitsSize = height * bitsHeader.rowBytes;
288		rgb.pixels = static_cast<uint8_t*>(malloc(bitsSize));
289		if (rgb.pixels == NULL)
290			return B_NO_MEMORY;
291		rgb.rowBytes = bitsHeader.rowBytes;
292
293		if (stream->Read(rgb.pixels, bitsSize) != bitsSize) {
294			free(rgb.pixels);
295			return B_IO_ERROR;
296		}
297
298		avifResult conversionResult = avifImageRGBToYUV(image, &rgb);
299		free(rgb.pixels);
300		if (conversionResult != AVIF_RESULT_OK)
301			return B_ERROR;
302	} else if (bytesPerPixel == 3) {
303		// TODO: Investigate moving that to libavif instead, and do so
304		// for other Y'CbCr formats too.
305		//
306		// See also https://github.com/AOMediaCodec/libavif/pull/235
307		assert(bitsHeader.colors == B_YCbCr444);
308		int bitsSize = height * bitsHeader.rowBytes;
309		uint8_t* pixels = static_cast<uint8_t*>(malloc(bitsSize));
310		if (stream->Read(pixels, bitsSize) != bitsSize)
311			return B_IO_ERROR;
312
313		uint8_t* luma = static_cast<uint8_t*>(malloc(bitsSize / 3));
314		uint8_t* cb = static_cast<uint8_t*>(malloc(bitsSize / 3));
315		uint8_t* cr = static_cast<uint8_t*>(malloc(bitsSize / 3));
316
317		for (int i = 0; i < bitsSize / 3; ++i) {
318			luma[i] = pixels[3 * i + 0];
319			cb[i] = pixels[3 * i + 1];
320			cr[i] = pixels[3 * i + 2];
321		}
322
323		image->yuvPlanes[0] = luma;
324		image->yuvPlanes[1] = cb;
325		image->yuvPlanes[2] = cr;
326
327		image->yuvRowBytes[0] = bitsHeader.rowBytes / 3;
328		image->yuvRowBytes[1] = bitsHeader.rowBytes / 3;
329		image->yuvRowBytes[2] = bitsHeader.rowBytes / 3;
330
331		image->yuvRange = AVIF_RANGE_LIMITED;
332	} else {
333		assert(bitsHeader.colors == B_GRAY8);
334		int bitsSize = height * bitsHeader.rowBytes;
335		uint8_t* luma = static_cast<uint8_t*>(malloc(bitsSize));
336		if (stream->Read(luma, bitsSize) != bitsSize)
337			return B_IO_ERROR;
338
339		image->yuvPlanes[0] = luma;
340		image->yuvPlanes[1] = nullptr;
341		image->yuvPlanes[2] = nullptr;
342
343		image->yuvRowBytes[0] = bitsHeader.rowBytes;
344		image->yuvRowBytes[1] = 0;
345		image->yuvRowBytes[2] = 0;
346	}
347
348	avifRWData output = AVIF_DATA_EMPTY;
349	avifEncoder* encoder = avifEncoderCreate();
350
351	system_info info;
352	encoder->maxThreads = (get_system_info(&info) == B_OK) ?
353		info.cpu_count : 1;
354
355	if (fSettings->SetGetBool(AVIF_SETTING_LOSSLESS)) {
356		encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS;
357		encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
358	} else {
359		encoder->minQuantizer = encoder->maxQuantizer
360			= fSettings->SetGetInt32(AVIF_SETTING_QUALITY);
361	}
362	encoder->speed = fSettings->SetGetInt32(AVIF_SETTING_SPEED);
363	encoder->tileColsLog2
364		= fSettings->SetGetInt32(AVIF_SETTING_TILES_HORIZONTAL);
365	encoder->tileRowsLog2
366		= fSettings->SetGetInt32(AVIF_SETTING_TILES_VERTICAL);
367
368	avifResult encodeResult = avifEncoderWrite(encoder, image, &output);
369	avifImageDestroy(image);
370	avifEncoderDestroy(encoder);
371
372	if (encodeResult != AVIF_RESULT_OK) {
373		printf("ERROR: Failed to encode: %s\n",
374			avifResultToString(encodeResult));
375		avifRWDataFree(&output);
376		return B_ERROR;
377	}
378
379	// output contains a valid .avif file's contents
380	target->Write(output.data, output.size);
381	avifRWDataFree(&output);
382	return B_OK;
383}
384
385
386status_t
387AVIFTranslator::_TranslateFromAVIF(BPositionIO* stream, BMessage* ioExtension,
388	uint32 outType, BPositionIO* target)
389{
390	if (!outType)
391		outType = B_TRANSLATOR_BITMAP;
392	if (outType != B_TRANSLATOR_BITMAP)
393		return B_NO_TRANSLATOR;
394
395	off_t streamLength = 0;
396	stream->GetSize(&streamLength);
397
398	off_t streamSize = stream->Seek(0, SEEK_END);
399	stream->Seek(0, SEEK_SET);
400
401	void* streamData = malloc(streamSize);
402	if (streamData == NULL)
403		return B_NO_MEMORY;
404
405	if (stream->Read(streamData, streamSize) != streamSize) {
406		free(streamData);
407		return B_IO_ERROR;
408	}
409
410	avifDecoder* decoder = avifDecoderCreate();
411	if (decoder == NULL) {
412		free(streamData);
413		return B_NO_MEMORY;
414	}
415
416	avifResult setIOMemoryResult = avifDecoderSetIOMemory(decoder,
417		reinterpret_cast<const uint8_t *>(streamData), streamSize);
418	if (setIOMemoryResult != AVIF_RESULT_OK) {
419		free(streamData);
420		return B_NO_MEMORY;
421	}
422
423	avifResult decodeResult = avifDecoderParse(decoder);
424	if (decodeResult != AVIF_RESULT_OK) {
425		free(streamData);
426		return B_ILLEGAL_DATA;
427	}
428
429	// We don���t support animations yet.
430	if (decoder->imageCount != 1) {
431		free(streamData);
432		return B_ILLEGAL_DATA;
433	}
434
435	avifResult nextImageResult = avifDecoderNextImage(decoder);
436	free(streamData);
437	if (nextImageResult != AVIF_RESULT_OK)
438		return B_ILLEGAL_DATA;
439
440	avifImage* image = decoder->image;
441	int width = image->width;
442	int height = image->height;
443	avifRGBFormat format;
444	uint8_t* pixels;
445	uint32_t rowBytes;
446	color_space colors;
447
448	bool convertToRGB = true;
449	if (image->alphaPlane) {
450		format = AVIF_RGB_FORMAT_BGRA;
451		colors = B_RGBA32;
452	} else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
453		colors = B_GRAY8;
454		convertToRGB = false;
455	} else {
456		format = AVIF_RGB_FORMAT_BGR;
457		colors = B_RGB24;
458	}
459
460	if (convertToRGB) {
461		avifRGBImage rgb;
462		avifRGBImageSetDefaults(&rgb, image);
463		rgb.depth = 8;
464		rgb.format = format;
465
466		avifRGBImageAllocatePixels(&rgb);
467		avifResult conversionResult = avifImageYUVToRGB(image, &rgb);
468		if (conversionResult != AVIF_RESULT_OK)
469			return B_ILLEGAL_DATA;
470
471		pixels = rgb.pixels;
472		rowBytes = rgb.rowBytes;
473	} else {
474		// TODO: Add a downsampling (with dithering?) path here, or
475		// alternatively add support for higher bit depth to Haiku
476		// bitmaps, possibly with HDR too.
477		if (image->depth > 8)
478			return B_ILLEGAL_DATA;
479
480		// TODO: Add support for more than just the luma plane.
481		pixels = image->yuvPlanes[0];
482		rowBytes = image->yuvRowBytes[0];
483	}
484
485	uint32 dataSize = rowBytes * height;
486
487	TranslatorBitmap bitmapHeader;
488	bitmapHeader.magic = B_TRANSLATOR_BITMAP;
489	bitmapHeader.bounds.Set(0, 0, width - 1, height - 1);
490	bitmapHeader.rowBytes = rowBytes;
491	bitmapHeader.colors = colors;
492	bitmapHeader.dataSize = dataSize;
493
494	// write out Be's Bitmap header
495	swap_data(B_UINT32_TYPE, &bitmapHeader, sizeof(TranslatorBitmap),
496		B_SWAP_HOST_TO_BENDIAN);
497	ssize_t bytesWritten = target->Write(&bitmapHeader,
498		sizeof(TranslatorBitmap));
499	if (bytesWritten < B_OK)
500		return bytesWritten;
501
502	if ((size_t)bytesWritten != sizeof(TranslatorBitmap))
503		return B_IO_ERROR;
504
505	bool headerOnly = false;
506	if (ioExtension != NULL)
507		ioExtension->FindBool(B_TRANSLATOR_EXT_HEADER_ONLY,
508			&headerOnly);
509
510	if (headerOnly)
511		return B_OK;
512
513	bytesWritten = target->Write(pixels, dataSize);
514	if (bytesWritten < B_OK)
515		return bytesWritten;
516	return B_OK;
517}
518
519
520//	#pragma mark -
521
522
523BTranslator*
524make_nth_translator(int32 n, image_id you, uint32 flags, ...)
525{
526	(void)you;
527	(void)flags;
528	if (n != 0)
529		return NULL;
530
531	return new AVIFTranslator();
532}
533