1 /* 2 * Copyright 2021, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Emmanuel Gil Peyrot 7 */ 8 9 10 #include "AVIFTranslator.h" 11 12 #include <BufferIO.h> 13 #include <Catalog.h> 14 #include <Messenger.h> 15 #include <TranslatorRoster.h> 16 17 #include <assert.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <string.h> 21 22 #include "avif/avif.h" 23 24 #include "ConfigView.h" 25 #include "TranslatorSettings.h" 26 27 28 #undef B_TRANSLATION_CONTEXT 29 #define B_TRANSLATION_CONTEXT "AVIFTranslator" 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 // The input formats that this translator knows how to read 51 static const translation_format sInputFormats[] = { 52 { 53 AVIF_IMAGE_FORMAT, 54 B_TRANSLATOR_BITMAP, 55 AVIF_IN_QUALITY, 56 AVIF_IN_CAPABILITY, 57 "image/avif", 58 "AV1 Image File Format" 59 }, 60 { 61 B_TRANSLATOR_BITMAP, 62 B_TRANSLATOR_BITMAP, 63 BITS_IN_QUALITY, 64 BITS_IN_CAPABILITY, 65 "image/x-be-bitmap", 66 "Be Bitmap Format (AVIFTranslator)" 67 }, 68 }; 69 70 71 // The output formats that this translator knows how to write 72 static const translation_format sOutputFormats[] = { 73 { 74 AVIF_IMAGE_FORMAT, 75 B_TRANSLATOR_BITMAP, 76 AVIF_OUT_QUALITY, 77 AVIF_OUT_CAPABILITY, 78 "image/avif", 79 "AV1 Image File Format" 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 (AVIFTranslator)" 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 { AVIF_SETTING_LOSSLESS, TRAN_SETTING_BOOL, false }, 96 { AVIF_SETTING_PIXEL_FORMAT, TRAN_SETTING_INT32, 97 AVIF_PIXEL_FORMAT_YUV444 }, 98 { AVIF_SETTING_QUALITY, TRAN_SETTING_INT32, 60 }, 99 { AVIF_SETTING_SPEED, TRAN_SETTING_INT32, -1 }, 100 { AVIF_SETTING_TILES_HORIZONTAL, TRAN_SETTING_INT32, 1 }, 101 { AVIF_SETTING_TILES_VERTICAL, TRAN_SETTING_INT32, 1 }, 102 }; 103 104 const uint32 kNumInputFormats = sizeof(sInputFormats) / 105 sizeof(translation_format); 106 const uint32 kNumOutputFormats = sizeof(sOutputFormats) / 107 sizeof(translation_format); 108 const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / 109 sizeof(TranSetting); 110 111 112 // #pragma mark - 113 114 115 AVIFTranslator::AVIFTranslator() 116 : 117 BaseTranslator(B_TRANSLATE("AVIF images"), 118 B_TRANSLATE("AVIF image translator"), 119 AVIF_TRANSLATOR_VERSION, 120 sInputFormats, kNumInputFormats, 121 sOutputFormats, kNumOutputFormats, 122 "AVIFTranslator_Settings", sDefaultSettings, 123 kNumDefaultSettings, B_TRANSLATOR_BITMAP, AVIF_IMAGE_FORMAT) 124 { 125 } 126 127 128 AVIFTranslator::~AVIFTranslator() 129 { 130 } 131 132 133 status_t 134 AVIFTranslator::DerivedIdentify(BPositionIO* stream, 135 const translation_format* format, BMessage* settings, 136 translator_info* info, uint32 outType) 137 { 138 (void)format; 139 (void)settings; 140 if (!outType) 141 outType = B_TRANSLATOR_BITMAP; 142 if (outType != B_TRANSLATOR_BITMAP) 143 return B_NO_TRANSLATOR; 144 145 // Read header and first chunck bytes... 146 uint32 buf[64]; 147 ssize_t size = sizeof(buf); 148 if (stream->Read(buf, size) != size) 149 return B_IO_ERROR; 150 151 // Check it's a valid AVIF format 152 avifROData data; 153 data.data = reinterpret_cast<const uint8_t*>(buf); 154 data.size = static_cast<size_t>(size); 155 if (!avifPeekCompatibleFileType(&data)) 156 return B_ILLEGAL_DATA; 157 158 info->type = AVIF_IMAGE_FORMAT; 159 info->group = B_TRANSLATOR_BITMAP; 160 info->quality = AVIF_IN_QUALITY; 161 info->capability = AVIF_IN_CAPABILITY; 162 snprintf(info->name, sizeof(info->name), B_TRANSLATE("AVIF image")); 163 strcpy(info->MIME, "image/avif"); 164 165 return B_OK; 166 } 167 168 169 status_t 170 AVIFTranslator::DerivedTranslate(BPositionIO* stream, 171 const translator_info* info, BMessage* ioExtension, uint32 outType, 172 BPositionIO* target, int32 baseType) 173 { 174 (void)info; 175 if (baseType == 1) 176 // if stream is in bits format 177 return _TranslateFromBits(stream, ioExtension, outType, target); 178 else if (baseType == 0) 179 // if stream is NOT in bits format 180 return _TranslateFromAVIF(stream, ioExtension, outType, target); 181 else 182 // if BaseTranslator dit not properly identify the data as 183 // bits or not bits 184 return B_NO_TRANSLATOR; 185 } 186 187 188 BView* 189 AVIFTranslator::NewConfigView(TranslatorSettings* settings) 190 { 191 return new ConfigView(settings); 192 } 193 194 195 status_t 196 AVIFTranslator::_TranslateFromBits(BPositionIO* stream, BMessage* ioExtension, 197 uint32 outType, BPositionIO* target) 198 { 199 // FIXME: This codepath is completely untested for now, due to libavif 200 // being built without any encoder in haikuports. 201 202 (void)ioExtension; 203 if (!outType) 204 outType = AVIF_IMAGE_FORMAT; 205 if (outType != AVIF_IMAGE_FORMAT) 206 return B_NO_TRANSLATOR; 207 208 TranslatorBitmap bitsHeader; 209 status_t status; 210 211 status = identify_bits_header(stream, NULL, &bitsHeader); 212 if (status != B_OK) 213 return status; 214 215 avifPixelFormat format = static_cast<avifPixelFormat>( 216 fSettings->SetGetInt32(AVIF_SETTING_PIXEL_FORMAT)); 217 int32 bytesPerPixel; 218 avifRGBFormat rgbFormat; 219 bool isRGB = true; 220 bool ignoreAlpha = false; 221 switch (bitsHeader.colors) { 222 case B_RGB32: 223 rgbFormat = AVIF_RGB_FORMAT_BGRA; 224 ignoreAlpha = true; 225 bytesPerPixel = 4; 226 break; 227 228 case B_RGB32_BIG: 229 rgbFormat = AVIF_RGB_FORMAT_ARGB; 230 ignoreAlpha = true; 231 bytesPerPixel = 4; 232 break; 233 234 case B_RGBA32: 235 rgbFormat = AVIF_RGB_FORMAT_BGRA; 236 bytesPerPixel = 4; 237 break; 238 239 case B_RGBA32_BIG: 240 rgbFormat = AVIF_RGB_FORMAT_ARGB; 241 bytesPerPixel = 4; 242 break; 243 244 case B_RGB24: 245 rgbFormat = AVIF_RGB_FORMAT_BGR; 246 bytesPerPixel = 3; 247 break; 248 249 case B_RGB24_BIG: 250 rgbFormat = AVIF_RGB_FORMAT_RGB; 251 bytesPerPixel = 3; 252 break; 253 254 case B_YCbCr444: 255 bytesPerPixel = 3; 256 isRGB = false; 257 break; 258 259 case B_GRAY8: 260 bytesPerPixel = 1; 261 isRGB = false; 262 break; 263 264 default: 265 printf("ERROR: Colorspace not supported: %d\n", 266 bitsHeader.colors); 267 return B_NO_TRANSLATOR; 268 } 269 270 int width = bitsHeader.bounds.IntegerWidth() + 1; 271 int height = bitsHeader.bounds.IntegerHeight() + 1; 272 int depth = 8; 273 274 avifImage* image = avifImageCreate(width, height, depth, format); 275 image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709; 276 image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB; 277 image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY; 278 279 if (isRGB) { 280 image->yuvRange = AVIF_RANGE_FULL; 281 282 avifRGBImage rgb; 283 avifRGBImageSetDefaults(&rgb, image); 284 rgb.depth = depth; 285 rgb.format = rgbFormat; 286 rgb.ignoreAlpha = ignoreAlpha; 287 int bitsSize = height * bitsHeader.rowBytes; 288 rgb.pixels = static_cast<uint8_t*>(malloc(bitsSize)); 289 if (rgb.pixels == NULL) 290 return B_NO_MEMORY; 291 rgb.rowBytes = bitsHeader.rowBytes; 292 293 if (stream->Read(rgb.pixels, bitsSize) != bitsSize) { 294 free(rgb.pixels); 295 return B_IO_ERROR; 296 } 297 298 avifResult conversionResult = avifImageRGBToYUV(image, &rgb); 299 free(rgb.pixels); 300 if (conversionResult != AVIF_RESULT_OK) 301 return B_ERROR; 302 } else if (bytesPerPixel == 3) { 303 // TODO: Investigate moving that to libavif instead, and do so 304 // for other Y'CbCr formats too. 305 // 306 // See also https://github.com/AOMediaCodec/libavif/pull/235 307 assert(bitsHeader.colors == B_YCbCr444); 308 int bitsSize = height * bitsHeader.rowBytes; 309 uint8_t* pixels = static_cast<uint8_t*>(malloc(bitsSize)); 310 if (stream->Read(pixels, bitsSize) != bitsSize) 311 return B_IO_ERROR; 312 313 uint8_t* luma = static_cast<uint8_t*>(malloc(bitsSize / 3)); 314 uint8_t* cb = static_cast<uint8_t*>(malloc(bitsSize / 3)); 315 uint8_t* cr = static_cast<uint8_t*>(malloc(bitsSize / 3)); 316 317 for (int i = 0; i < bitsSize / 3; ++i) { 318 luma[i] = pixels[3 * i + 0]; 319 cb[i] = pixels[3 * i + 1]; 320 cr[i] = pixels[3 * i + 2]; 321 } 322 323 image->yuvPlanes[0] = luma; 324 image->yuvPlanes[1] = cb; 325 image->yuvPlanes[2] = cr; 326 327 image->yuvRowBytes[0] = bitsHeader.rowBytes / 3; 328 image->yuvRowBytes[1] = bitsHeader.rowBytes / 3; 329 image->yuvRowBytes[2] = bitsHeader.rowBytes / 3; 330 331 image->yuvRange = AVIF_RANGE_LIMITED; 332 } else { 333 assert(bitsHeader.colors == B_GRAY8); 334 int bitsSize = height * bitsHeader.rowBytes; 335 uint8_t* luma = static_cast<uint8_t*>(malloc(bitsSize)); 336 if (stream->Read(luma, bitsSize) != bitsSize) 337 return B_IO_ERROR; 338 339 image->yuvPlanes[0] = luma; 340 image->yuvPlanes[1] = nullptr; 341 image->yuvPlanes[2] = nullptr; 342 343 image->yuvRowBytes[0] = bitsHeader.rowBytes; 344 image->yuvRowBytes[1] = 0; 345 image->yuvRowBytes[2] = 0; 346 } 347 348 avifRWData output = AVIF_DATA_EMPTY; 349 avifEncoder* encoder = avifEncoderCreate(); 350 351 system_info info; 352 encoder->maxThreads = (get_system_info(&info) == B_OK) ? 353 info.cpu_count : 1; 354 355 if (fSettings->SetGetBool(AVIF_SETTING_LOSSLESS)) { 356 encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS; 357 encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS; 358 } else { 359 encoder->minQuantizer = encoder->maxQuantizer 360 = fSettings->SetGetInt32(AVIF_SETTING_QUALITY); 361 } 362 encoder->speed = fSettings->SetGetInt32(AVIF_SETTING_SPEED); 363 encoder->tileColsLog2 364 = fSettings->SetGetInt32(AVIF_SETTING_TILES_HORIZONTAL); 365 encoder->tileRowsLog2 366 = fSettings->SetGetInt32(AVIF_SETTING_TILES_VERTICAL); 367 368 avifResult encodeResult = avifEncoderWrite(encoder, image, &output); 369 avifImageDestroy(image); 370 avifEncoderDestroy(encoder); 371 372 if (encodeResult != AVIF_RESULT_OK) { 373 printf("ERROR: Failed to encode: %s\n", 374 avifResultToString(encodeResult)); 375 avifRWDataFree(&output); 376 return B_ERROR; 377 } 378 379 // output contains a valid .avif file's contents 380 target->Write(output.data, output.size); 381 avifRWDataFree(&output); 382 return B_OK; 383 } 384 385 386 status_t 387 AVIFTranslator::_TranslateFromAVIF(BPositionIO* stream, BMessage* ioExtension, 388 uint32 outType, BPositionIO* target) 389 { 390 if (!outType) 391 outType = B_TRANSLATOR_BITMAP; 392 if (outType != B_TRANSLATOR_BITMAP) 393 return B_NO_TRANSLATOR; 394 395 off_t streamLength = 0; 396 stream->GetSize(&streamLength); 397 398 off_t streamSize = stream->Seek(0, SEEK_END); 399 stream->Seek(0, SEEK_SET); 400 401 void* streamData = malloc(streamSize); 402 if (streamData == NULL) 403 return B_NO_MEMORY; 404 405 if (stream->Read(streamData, streamSize) != streamSize) { 406 free(streamData); 407 return B_IO_ERROR; 408 } 409 410 avifDecoder* decoder = avifDecoderCreate(); 411 if (decoder == NULL) { 412 free(streamData); 413 return B_NO_MEMORY; 414 } 415 416 avifResult setIOMemoryResult = avifDecoderSetIOMemory(decoder, 417 reinterpret_cast<const uint8_t *>(streamData), streamSize); 418 if (setIOMemoryResult != AVIF_RESULT_OK) { 419 free(streamData); 420 return B_NO_MEMORY; 421 } 422 423 avifResult decodeResult = avifDecoderParse(decoder); 424 if (decodeResult != AVIF_RESULT_OK) { 425 free(streamData); 426 return B_ILLEGAL_DATA; 427 } 428 429 // We don’t support animations yet. 430 if (decoder->imageCount != 1) { 431 free(streamData); 432 return B_ILLEGAL_DATA; 433 } 434 435 avifResult nextImageResult = avifDecoderNextImage(decoder); 436 free(streamData); 437 if (nextImageResult != AVIF_RESULT_OK) 438 return B_ILLEGAL_DATA; 439 440 avifImage* image = decoder->image; 441 int width = image->width; 442 int height = image->height; 443 avifRGBFormat format; 444 uint8_t* pixels; 445 uint32_t rowBytes; 446 color_space colors; 447 448 bool convertToRGB = true; 449 if (image->alphaPlane) { 450 format = AVIF_RGB_FORMAT_BGRA; 451 colors = B_RGBA32; 452 } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { 453 colors = B_GRAY8; 454 convertToRGB = false; 455 } else { 456 format = AVIF_RGB_FORMAT_BGR; 457 colors = B_RGB24; 458 } 459 460 if (convertToRGB) { 461 avifRGBImage rgb; 462 avifRGBImageSetDefaults(&rgb, image); 463 rgb.depth = 8; 464 rgb.format = format; 465 466 avifRGBImageAllocatePixels(&rgb); 467 avifResult conversionResult = avifImageYUVToRGB(image, &rgb); 468 if (conversionResult != AVIF_RESULT_OK) 469 return B_ILLEGAL_DATA; 470 471 pixels = rgb.pixels; 472 rowBytes = rgb.rowBytes; 473 } else { 474 // TODO: Add a downsampling (with dithering?) path here, or 475 // alternatively add support for higher bit depth to Haiku 476 // bitmaps, possibly with HDR too. 477 if (image->depth > 8) 478 return B_ILLEGAL_DATA; 479 480 // TODO: Add support for more than just the luma plane. 481 pixels = image->yuvPlanes[0]; 482 rowBytes = image->yuvRowBytes[0]; 483 } 484 485 uint32 dataSize = rowBytes * height; 486 487 TranslatorBitmap bitmapHeader; 488 bitmapHeader.magic = B_TRANSLATOR_BITMAP; 489 bitmapHeader.bounds.Set(0, 0, width - 1, height - 1); 490 bitmapHeader.rowBytes = rowBytes; 491 bitmapHeader.colors = colors; 492 bitmapHeader.dataSize = dataSize; 493 494 // write out Be's Bitmap header 495 swap_data(B_UINT32_TYPE, &bitmapHeader, sizeof(TranslatorBitmap), 496 B_SWAP_HOST_TO_BENDIAN); 497 ssize_t bytesWritten = target->Write(&bitmapHeader, 498 sizeof(TranslatorBitmap)); 499 if (bytesWritten < B_OK) 500 return bytesWritten; 501 502 if ((size_t)bytesWritten != sizeof(TranslatorBitmap)) 503 return B_IO_ERROR; 504 505 bool headerOnly = false; 506 if (ioExtension != NULL) 507 ioExtension->FindBool(B_TRANSLATOR_EXT_HEADER_ONLY, 508 &headerOnly); 509 510 if (headerOnly) 511 return B_OK; 512 513 bytesWritten = target->Write(pixels, dataSize); 514 if (bytesWritten < B_OK) 515 return bytesWritten; 516 return B_OK; 517 } 518 519 520 // #pragma mark - 521 522 523 BTranslator* 524 make_nth_translator(int32 n, image_id you, uint32 flags, ...) 525 { 526 (void)you; 527 (void)flags; 528 if (n != 0) 529 return NULL; 530 531 return new AVIFTranslator(); 532 } 533