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