xref: /haiku/src/add-ons/translators/webp/WebPTranslator.cpp (revision 22440f4105cafc95cc1d49f9bc65bb395c527d86)
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 
32 class 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
52 static 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
72 static 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
92 static 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 
101 const uint32 kNumInputFormats = sizeof(sInputFormats) /
102 	sizeof(translation_format);
103 const uint32 kNumOutputFormats = sizeof(sOutputFormats) /
104 	sizeof(translation_format);
105 const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) /
106 	sizeof(TranSetting);
107 
108 
109 //	#pragma mark -
110 
111 
112 WebPTranslator::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 
124 WebPTranslator::~WebPTranslator()
125 {
126 }
127 
128 
129 status_t
130 WebPTranslator::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 
162 status_t
163 WebPTranslator::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 
180 BView*
181 WebPTranslator::NewConfigView(TranslatorSettings* settings)
182 {
183 	return new ConfigView(settings);
184 }
185 
186 
187 status_t
188 WebPTranslator::_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 
310 status_t
311 WebPTranslator::_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
390 WebPTranslator::_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 
401 BTranslator*
402 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
403 {
404 	if (n != 0)
405 		return NULL;
406 
407 	return new WebPTranslator();
408 }
409