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	:
114	BaseTranslator(B_TRANSLATE("WebP images"),
115	B_TRANSLATE("WebP image translator"),
116	WEBP_TRANSLATOR_VERSION,
117	sInputFormats, kNumInputFormats,
118	sOutputFormats, kNumOutputFormats,
119	"WebPTranslator_Settings", sDefaultSettings, kNumDefaultSettings,
120	B_TRANSLATOR_BITMAP, WEBP_IMAGE_FORMAT)
121{
122}
123
124
125WebPTranslator::~WebPTranslator()
126{
127}
128
129
130status_t
131WebPTranslator::DerivedIdentify(BPositionIO* stream,
132	const translation_format* format, BMessage* settings,
133	translator_info* info, uint32 outType)
134{
135	if (!outType)
136		outType = B_TRANSLATOR_BITMAP;
137	if (outType != B_TRANSLATOR_BITMAP)
138		return B_NO_TRANSLATOR;
139
140	// Read header and first chunck bytes...
141	uint32 buf[64];
142	ssize_t size = sizeof(buf);
143	if (stream->Read(buf, size) != size)
144		return B_IO_ERROR;
145
146	// Check it's a valid WebP format
147	if (!WebPGetInfo((const uint8_t*)buf, size, NULL, NULL))
148		return B_ILLEGAL_DATA;
149
150	info->type = WEBP_IMAGE_FORMAT;
151	info->group = B_TRANSLATOR_BITMAP;
152	info->quality = WEBP_IN_QUALITY;
153	info->capability = WEBP_IN_CAPABILITY;
154	strlcpy(info->name, B_TRANSLATE("WebP image"), sizeof(info->name));
155	strcpy(info->MIME, "image/webp");
156
157	return B_OK;
158}
159
160
161status_t
162WebPTranslator::DerivedTranslate(BPositionIO* stream,
163	const translator_info* info, BMessage* ioExtension, uint32 outType,
164	BPositionIO* target, int32 baseType)
165{
166	if (baseType == 1)
167		// if stream is in bits format
168		return _TranslateFromBits(stream, ioExtension, outType, target);
169	else if (baseType == 0)
170		// if stream is NOT in bits format
171		return _TranslateFromWebP(stream, ioExtension, outType, target);
172	else
173		// if BaseTranslator dit not properly identify the data as
174		// bits or not bits
175		return B_NO_TRANSLATOR;
176}
177
178
179BView*
180WebPTranslator::NewConfigView(TranslatorSettings* settings)
181{
182	return new ConfigView(settings);
183}
184
185
186status_t
187WebPTranslator::_TranslateFromBits(BPositionIO* stream, BMessage* ioExtension,
188	uint32 outType, BPositionIO* target)
189{
190	if (!outType)
191		outType = WEBP_IMAGE_FORMAT;
192	if (outType != WEBP_IMAGE_FORMAT)
193		return B_NO_TRANSLATOR;
194
195	TranslatorBitmap bitsHeader;
196	status_t status;
197
198	status = identify_bits_header(stream, NULL, &bitsHeader);
199	if (status != B_OK)
200		return status;
201
202	if (bitsHeader.colors == B_CMAP8) {
203		// TODO: support whatever colospace by intermediate colorspace conversion
204		printf("Error! Colorspace not supported\n");
205		return B_NO_TRANSLATOR;
206	}
207
208	int32 bitsBytesPerPixel = 0;
209	switch (bitsHeader.colors) {
210		case B_RGB32:
211		case B_RGB32_BIG:
212		case B_RGBA32:
213		case B_RGBA32_BIG:
214		case B_CMY32:
215		case B_CMYA32:
216		case B_CMYK32:
217			bitsBytesPerPixel = 4;
218			break;
219
220		case B_RGB24:
221		case B_RGB24_BIG:
222		case B_CMY24:
223			bitsBytesPerPixel = 3;
224			break;
225
226		case B_RGB16:
227		case B_RGB16_BIG:
228		case B_RGBA15:
229		case B_RGBA15_BIG:
230		case B_RGB15:
231		case B_RGB15_BIG:
232			bitsBytesPerPixel = 2;
233			break;
234
235		case B_CMAP8:
236		case B_GRAY8:
237			bitsBytesPerPixel = 1;
238			break;
239
240		default:
241			return B_ERROR;
242	}
243
244	if (bitsBytesPerPixel < 3) {
245		// TODO support
246		return B_NO_TRANSLATOR;
247	}
248
249	WebPPicture picture;
250	WebPConfig config;
251
252	if (!WebPPictureInit(&picture) || !WebPConfigInit(&config)) {
253		printf("Error! Version mismatch!\n");
254		return B_ERROR;
255	}
256
257	WebPPreset preset = (WebPPreset)fSettings->SetGetInt32(WEBP_SETTING_PRESET);
258	config.quality = (float)fSettings->SetGetInt32(WEBP_SETTING_QUALITY);
259
260	if (!WebPConfigPreset(&config, (WebPPreset)preset, config.quality)) {
261		printf("Error! Could initialize configuration with preset.");
262		return B_ERROR;
263	}
264
265	config.method = fSettings->SetGetInt32(WEBP_SETTING_METHOD);
266	config.preprocessing = fSettings->SetGetBool(WEBP_SETTING_PREPROCESSING);
267
268	if (!WebPValidateConfig(&config)) {
269		printf("Error! Invalid configuration.\n");
270		return B_ERROR;
271	}
272
273	picture.width = bitsHeader.bounds.IntegerWidth() + 1;
274	picture.height = bitsHeader.bounds.IntegerHeight() + 1;
275
276	int stride = bitsHeader.rowBytes;
277	int bitsSize = picture.height * stride;
278	uint8* bits = (uint8*)malloc(bitsSize);
279	if (bits == NULL)
280		return B_NO_MEMORY;
281
282	if (stream->Read(bits, bitsSize) != bitsSize) {
283		free(bits);
284		return B_IO_ERROR;
285	}
286
287	if (!WebPPictureImportBGRA(&picture, bits, stride)) {
288		printf("Error! WebPEncode() failed!\n");
289		free(bits);
290		return B_ERROR;
291	}
292	free(bits);
293
294	picture.writer = _EncodedWriter;
295	picture.custom_ptr = target;
296	picture.stats = NULL;
297
298	if (!WebPEncode(&config, &picture)) {
299		printf("Error! WebPEncode() failed!\n");
300		status = B_NO_TRANSLATOR;
301	} else
302		status = B_OK;
303
304	WebPPictureFree(&picture);
305	return status;
306}
307
308
309status_t
310WebPTranslator::_TranslateFromWebP(BPositionIO* stream, BMessage* ioExtension,
311	uint32 outType, BPositionIO* target)
312{
313	if (!outType)
314		outType = B_TRANSLATOR_BITMAP;
315	if (outType != B_TRANSLATOR_BITMAP)
316		return B_NO_TRANSLATOR;
317
318	off_t streamLength = 0;
319	stream->GetSize(&streamLength);
320
321	off_t streamSize = stream->Seek(0, SEEK_END);
322	stream->Seek(0, SEEK_SET);
323
324	void* streamData = malloc(streamSize);
325	if (streamData == NULL)
326		return B_NO_MEMORY;
327
328	if (stream->Read(streamData, streamSize) != streamSize) {
329		free(streamData);
330		return B_IO_ERROR;
331	}
332
333	int width, height;
334	uint8* out = WebPDecodeBGRA((const uint8*)streamData, streamSize, &width,
335		&height);
336	free(streamData);
337
338	if (out == NULL)
339		return B_ILLEGAL_DATA;
340
341	FreeAllocation _(out);
342
343	uint32 dataSize = width * 4 * height;
344
345	TranslatorBitmap bitmapHeader;
346	bitmapHeader.magic = B_TRANSLATOR_BITMAP;
347	bitmapHeader.bounds.Set(0, 0, width - 1, height - 1);
348	bitmapHeader.rowBytes = width * 4;
349	bitmapHeader.colors = B_RGBA32;
350	bitmapHeader.dataSize = width * 4 * height;
351
352	// write out Be's Bitmap header
353	swap_data(B_UINT32_TYPE, &bitmapHeader, sizeof(TranslatorBitmap),
354		B_SWAP_HOST_TO_BENDIAN);
355	ssize_t bytesWritten = target->Write(&bitmapHeader,
356		sizeof(TranslatorBitmap));
357	if (bytesWritten < B_OK)
358		return bytesWritten;
359
360	if ((size_t)bytesWritten != sizeof(TranslatorBitmap))
361		return B_IO_ERROR;
362
363	bool headerOnly = false;
364	if (ioExtension != NULL)
365		ioExtension->FindBool(B_TRANSLATOR_EXT_HEADER_ONLY, &headerOnly);
366
367	if (headerOnly)
368		return B_OK;
369
370	uint32 dataLeft = dataSize;
371	uint8* p = out;
372	while (dataLeft) {
373		bytesWritten = target->Write(p, 4);
374		if (bytesWritten < B_OK)
375			return bytesWritten;
376
377		if (bytesWritten != 4)
378			return B_IO_ERROR;
379
380		p += 4;
381		dataLeft -= 4;
382	}
383
384	return B_OK;
385}
386
387
388/* static */ int
389WebPTranslator::_EncodedWriter(const uint8_t* data, size_t dataSize,
390	const WebPPicture* const picture)
391{
392	BPositionIO* target = (BPositionIO*)picture->custom_ptr;
393	return dataSize ? (target->Write(data, dataSize) == (ssize_t)dataSize) : 1;
394}
395
396
397//	#pragma mark -
398
399
400BTranslator*
401make_nth_translator(int32 n, image_id you, uint32 flags, ...)
402{
403	if (n != 0)
404		return NULL;
405
406	return new WebPTranslator();
407}
408