xref: /haiku/src/add-ons/translators/webp/WebPTranslator.cpp (revision 6011ce6c7495e4e707bd33b12a7e22d66c710aad)
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 	:
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 
125 WebPTranslator::~WebPTranslator()
126 {
127 }
128 
129 
130 status_t
131 WebPTranslator::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 
161 status_t
162 WebPTranslator::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 
179 BView*
180 WebPTranslator::NewConfigView(TranslatorSettings* settings)
181 {
182 	return new ConfigView(settings);
183 }
184 
185 
186 status_t
187 WebPTranslator::_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 
309 status_t
310 WebPTranslator::_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
389 WebPTranslator::_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 
400 BTranslator*
401 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
402 {
403 	if (n != 0)
404 		return NULL;
405 
406 	return new WebPTranslator();
407 }
408