1/*
2 * Copyright 2010-2011, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Philippe Houdoin
7 */
8
9
10#include "WebPTranslator.h"
11
12#include <BufferIO.h>
13#include <Catalog.h>
14#include <Messenger.h>
15#include <TranslatorRoster.h>
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21#include "webp/encode.h"
22#include "webp/decode.h"
23
24#include "ConfigView.h"
25#include "TranslatorSettings.h"
26
27
28#undef B_TRANSLATION_CONTEXT
29#define B_TRANSLATION_CONTEXT "WebPTranslator"
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
51// The input formats that this translator knows how to read
52static const translation_format sInputFormats[] = {
53	{
54		WEBP_IMAGE_FORMAT,
55		B_TRANSLATOR_BITMAP,
56		WEBP_IN_QUALITY,
57		WEBP_IN_CAPABILITY,
58		"image/webp",
59		"WebP image"
60	},
61	{
62		B_TRANSLATOR_BITMAP,
63		B_TRANSLATOR_BITMAP,
64		BITS_IN_QUALITY,
65		BITS_IN_CAPABILITY,
66		"image/x-be-bitmap",
67		"Be Bitmap Format (WebPTranslator)"
68	},
69};
70
71// The output formats that this translator knows how to write
72static const translation_format sOutputFormats[] = {
73	{
74		WEBP_IMAGE_FORMAT,
75		B_TRANSLATOR_BITMAP,
76		WEBP_OUT_QUALITY,
77		WEBP_OUT_CAPABILITY,
78		"image/webp",
79		"WebP image"
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 (WebPTranslator)"
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	{ WEBP_SETTING_QUALITY, TRAN_SETTING_INT32, 60 },
96	{ WEBP_SETTING_PRESET, TRAN_SETTING_INT32, 0 },
97	{ WEBP_SETTING_METHOD, TRAN_SETTING_INT32, 2 },
98	{ WEBP_SETTING_PREPROCESSING, TRAN_SETTING_BOOL, false },
99};
100
101const uint32 kNumInputFormats = sizeof(sInputFormats) /
102	sizeof(translation_format);
103const uint32 kNumOutputFormats = sizeof(sOutputFormats) /
104	sizeof(translation_format);
105const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) /
106	sizeof(TranSetting);
107
108
109//	#pragma mark -
110
111
112WebPTranslator::WebPTranslator()
113	: BaseTranslator(B_TRANSLATE("WebP images"),
114		B_TRANSLATE("WebP image translator"),
115		WEBP_TRANSLATOR_VERSION,
116		sInputFormats, kNumInputFormats,
117		sOutputFormats, kNumOutputFormats,
118		"WebPTranslator_Settings", sDefaultSettings, kNumDefaultSettings,
119		B_TRANSLATOR_BITMAP, WEBP_IMAGE_FORMAT)
120{
121}
122
123
124WebPTranslator::~WebPTranslator()
125{
126}
127
128
129status_t
130WebPTranslator::DerivedIdentify(BPositionIO* stream,
131	const translation_format* format, BMessage* settings,
132	translator_info* info, uint32 outType)
133{
134	if (!outType)
135		outType = B_TRANSLATOR_BITMAP;
136	if (outType != B_TRANSLATOR_BITMAP)
137		return B_NO_TRANSLATOR;
138
139	// Check RIFF and 'WEBPVP8 ' signatures...
140	uint32 buf[4];
141	ssize_t size = 16;
142	if (stream->Read(buf, size) != size)
143		return B_IO_ERROR;
144
145	const uint32 kRIFFMagic = B_HOST_TO_BENDIAN_INT32('RIFF');
146	const uint32 kWEBPMagic = B_HOST_TO_BENDIAN_INT32('WEBP');
147	const uint32 kVP8Magic  = B_HOST_TO_BENDIAN_INT32('VP8 ');
148	if (buf[0] != kRIFFMagic || buf[2] != kWEBPMagic || buf[3] != kVP8Magic)
149		return B_ILLEGAL_DATA;
150
151	info->type = WEBP_IMAGE_FORMAT;
152	info->group = B_TRANSLATOR_BITMAP;
153	info->quality = WEBP_IN_QUALITY;
154	info->capability = WEBP_IN_CAPABILITY;
155	snprintf(info->name, sizeof(info->name), B_TRANSLATE("WebP image"));
156	strcpy(info->MIME, "image/webp");
157
158	return B_OK;
159}
160
161
162status_t
163WebPTranslator::DerivedTranslate(BPositionIO* stream,
164		const translator_info* info, BMessage* ioExtension,
165		uint32 outType, BPositionIO* target, int32 baseType)
166{
167	if (baseType == 1)
168		// if stream is in bits format
169		return _TranslateFromBits(stream, ioExtension, outType, target);
170	else if (baseType == 0)
171		// if stream is NOT in bits format
172		return _TranslateFromWebP(stream, ioExtension, outType, target);
173	else
174		// if BaseTranslator dit not properly identify the data as
175		// bits or not bits
176		return B_NO_TRANSLATOR;
177}
178
179
180BView*
181WebPTranslator::NewConfigView(TranslatorSettings* settings)
182{
183	return new ConfigView(settings);
184}
185
186
187status_t
188WebPTranslator::_TranslateFromBits(BPositionIO* stream, BMessage* ioExtension,
189		uint32 outType, BPositionIO* target)
190{
191	if (!outType)
192		outType = WEBP_IMAGE_FORMAT;
193	if (outType != WEBP_IMAGE_FORMAT)
194		return B_NO_TRANSLATOR;
195
196	TranslatorBitmap bitsHeader;
197	status_t status;
198
199	status = identify_bits_header(stream, NULL, &bitsHeader);
200	if (status != B_OK)
201		return status;
202
203	if (bitsHeader.colors == B_CMAP8) {
204		// TODO: support whatever colospace by intermediate colorspace conversion
205		printf("Error! Colorspace not supported\n");
206		return B_NO_TRANSLATOR;
207	}
208
209	int32 bitsBytesPerPixel = 0;
210	switch (bitsHeader.colors) {
211		case B_RGB32:
212		case B_RGB32_BIG:
213		case B_RGBA32:
214		case B_RGBA32_BIG:
215		case B_CMY32:
216		case B_CMYA32:
217		case B_CMYK32:
218			bitsBytesPerPixel = 4;
219			break;
220
221		case B_RGB24:
222		case B_RGB24_BIG:
223		case B_CMY24:
224			bitsBytesPerPixel = 3;
225			break;
226
227		case B_RGB16:
228		case B_RGB16_BIG:
229		case B_RGBA15:
230		case B_RGBA15_BIG:
231		case B_RGB15:
232		case B_RGB15_BIG:
233			bitsBytesPerPixel = 2;
234			break;
235
236		case B_CMAP8:
237		case B_GRAY8:
238			bitsBytesPerPixel = 1;
239			break;
240
241		default:
242			return B_ERROR;
243	}
244
245	if (bitsBytesPerPixel < 3) {
246		// TODO support
247		return B_NO_TRANSLATOR;
248	}
249
250	WebPPicture picture;
251	WebPConfig config;
252
253	if (!WebPPictureInit(&picture) || !WebPConfigInit(&config)) {
254		printf("Error! Version mismatch!\n");
255  		return B_ERROR;
256	}
257
258	WebPPreset preset = (WebPPreset)fSettings->SetGetInt32(WEBP_SETTING_PRESET);
259	config.quality = (float)fSettings->SetGetInt32(WEBP_SETTING_QUALITY);
260
261	if (!WebPConfigPreset(&config, (WebPPreset)preset, config.quality)) {
262		printf("Error! Could initialize configuration with preset.");
263		return B_ERROR;
264	}
265
266	config.method = fSettings->SetGetInt32(WEBP_SETTING_METHOD);
267	config.preprocessing = fSettings->SetGetBool(WEBP_SETTING_PREPROCESSING);
268
269	if (!WebPValidateConfig(&config)) {
270		printf("Error! Invalid configuration.\n");
271 		return B_ERROR;
272	}
273
274	picture.width = bitsHeader.bounds.IntegerWidth() + 1;
275	picture.height = bitsHeader.bounds.IntegerHeight() + 1;
276
277	int stride = bitsHeader.rowBytes;
278	int bitsSize = picture.height * stride;
279	uint8* bits = (uint8*)malloc(bitsSize);
280	if (bits == NULL)
281		return B_NO_MEMORY;
282
283	if (stream->Read(bits, bitsSize) != bitsSize) {
284		free(bits);
285		return B_IO_ERROR;
286	}
287
288	if (!WebPPictureImportBGRA(&picture, bits, stride)) {
289		printf("Error! WebPEncode() failed!\n");
290		free(bits);
291		return B_ERROR;
292	}
293	free(bits);
294
295	picture.writer = _EncodedWriter;
296	picture.custom_ptr = target;
297	picture.stats = NULL;
298
299	if (!WebPEncode(&config, &picture)) {
300		printf("Error! WebPEncode() failed!\n");
301		status = B_NO_TRANSLATOR;
302	} else
303		status = B_OK;
304
305	WebPPictureFree(&picture);
306	return status;
307}
308
309
310status_t
311WebPTranslator::_TranslateFromWebP(BPositionIO* stream, BMessage* ioExtension,
312		uint32 outType, BPositionIO* target)
313{
314	if (!outType)
315		outType = B_TRANSLATOR_BITMAP;
316	if (outType != B_TRANSLATOR_BITMAP)
317		return B_NO_TRANSLATOR;
318
319	off_t streamLength = 0;
320	stream->GetSize(&streamLength);
321
322	off_t streamSize = stream->Seek(0, SEEK_END);
323	stream->Seek(0, SEEK_SET);
324
325	void* streamData = malloc(streamSize);
326	if (streamData == NULL)
327		return B_NO_MEMORY;
328
329	if (stream->Read(streamData, streamSize) != streamSize) {
330		free(streamData);
331		return B_IO_ERROR;
332	}
333
334	int width, height;
335	uint8* out = WebPDecodeBGRA((const uint8*)streamData, streamSize, &width,
336		&height);
337	free(streamData);
338
339	if (out == NULL)
340		return B_ILLEGAL_DATA;
341
342	FreeAllocation _(out);
343
344 	uint32 dataSize = width * 4 * height;
345
346	TranslatorBitmap bitmapHeader;
347	bitmapHeader.magic = B_TRANSLATOR_BITMAP;
348	bitmapHeader.bounds.Set(0, 0, width - 1, height - 1);
349	bitmapHeader.rowBytes = width * 4;
350	bitmapHeader.colors = B_RGBA32;
351	bitmapHeader.dataSize = width * 4 * height;
352
353	// write out Be's Bitmap header
354	swap_data(B_UINT32_TYPE, &bitmapHeader, sizeof(TranslatorBitmap),
355		B_SWAP_HOST_TO_BENDIAN);
356	ssize_t bytesWritten = target->Write(&bitmapHeader,
357		sizeof(TranslatorBitmap));
358	if (bytesWritten < B_OK)
359		return bytesWritten;
360
361	if ((size_t)bytesWritten != sizeof(TranslatorBitmap))
362		return B_IO_ERROR;
363
364	bool headerOnly = false;
365	if (ioExtension != NULL)
366		ioExtension->FindBool(B_TRANSLATOR_EXT_HEADER_ONLY, &headerOnly);
367
368	if (headerOnly)
369		return B_OK;
370
371	uint32 dataLeft = dataSize;
372	uint8* p = out;
373	while (dataLeft) {
374		bytesWritten = target->Write(p, 4);
375		if (bytesWritten < B_OK)
376			return bytesWritten;
377
378		if (bytesWritten != 4)
379			return B_IO_ERROR;
380
381		p += 4;
382		dataLeft -= 4;
383	}
384
385	return B_OK;
386}
387
388
389/* static */ int
390WebPTranslator::_EncodedWriter(const uint8_t* data, size_t dataSize,
391	const WebPPicture* const picture)
392{
393	BPositionIO* target = (BPositionIO*)picture->custom_ptr;
394	return dataSize ? (target->Write(data, dataSize) == (ssize_t)dataSize) : 1;
395}
396
397
398//	#pragma mark -
399
400
401BTranslator*
402make_nth_translator(int32 n, image_id you, uint32 flags, ...)
403{
404	if (n != 0)
405		return NULL;
406
407	return new WebPTranslator();
408}
409