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