nsBMPEncoder.cpp (25288B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "nsCRT.h" 7 #include "mozilla/EndianUtils.h" 8 #include "mozilla/UniquePtrExtensions.h" 9 #include "nsBMPEncoder.h" 10 #include "nsString.h" 11 #include "nsStreamUtils.h" 12 #include "nsTArray.h" 13 #include "mozilla/CheckedInt.h" 14 #include "BMPHeaders.h" 15 16 using namespace mozilla; 17 using namespace mozilla::image; 18 using namespace mozilla::image::bmp; 19 20 NS_IMPL_ISUPPORTS(nsBMPEncoder, imgIEncoder, nsIInputStream, 21 nsIAsyncInputStream) 22 23 nsBMPEncoder::nsBMPEncoder() 24 : mBMPInfoHeader{}, 25 mImageBufferStart(nullptr), 26 mImageBufferCurr(0), 27 mImageBufferSize(0), 28 mImageBufferReadPoint(0), 29 mFinished(false), 30 mCallback(nullptr), 31 mCallbackTarget(nullptr), 32 mNotifyThreshold(0) { 33 this->mBMPFileHeader.filesize = 0; 34 this->mBMPFileHeader.reserved = 0; 35 this->mBMPFileHeader.dataoffset = 0; 36 } 37 38 nsBMPEncoder::~nsBMPEncoder() { 39 if (mImageBufferStart) { 40 free(mImageBufferStart); 41 mImageBufferStart = nullptr; 42 mImageBufferCurr = nullptr; 43 } 44 } 45 46 // nsBMPEncoder::InitFromData 47 // 48 // One output option is supported: bpp=<bpp_value> 49 // bpp specifies the bits per pixel to use where bpp_value can be 24 or 32 50 NS_IMETHODIMP 51 nsBMPEncoder::InitFromData(const uint8_t* aData, 52 uint32_t aLength, // (unused, req'd by JS) 53 uint32_t aWidth, uint32_t aHeight, uint32_t aStride, 54 uint32_t aInputFormat, 55 const nsAString& aOutputOptions, 56 const nsACString& aRandomizationKey) { 57 // validate input format 58 if (aInputFormat != INPUT_FORMAT_RGB && aInputFormat != INPUT_FORMAT_RGBA && 59 aInputFormat != INPUT_FORMAT_HOSTARGB) { 60 return NS_ERROR_INVALID_ARG; 61 } 62 63 CheckedInt32 check = CheckedInt32(aWidth) * 4; 64 if (MOZ_UNLIKELY(!check.isValid())) { 65 return NS_ERROR_INVALID_ARG; 66 } 67 68 // Stride is the padded width of each row, so it better be longer 69 if ((aInputFormat == INPUT_FORMAT_RGB && aStride < aWidth * 3) || 70 ((aInputFormat == INPUT_FORMAT_RGBA || 71 aInputFormat == INPUT_FORMAT_HOSTARGB) && 72 aStride < aWidth * 4)) { 73 NS_WARNING("Invalid stride for InitFromData"); 74 return NS_ERROR_INVALID_ARG; 75 } 76 77 nsresult rv; 78 rv = StartImageEncode(aWidth, aHeight, aInputFormat, aOutputOptions); 79 if (NS_FAILED(rv)) { 80 return rv; 81 } 82 83 rv = AddImageFrame(aData, aLength, aWidth, aHeight, aStride, aInputFormat, 84 aOutputOptions); 85 if (NS_FAILED(rv)) { 86 return rv; 87 } 88 89 rv = EndImageEncode(); 90 return rv; 91 } 92 93 // Just a helper method to make it explicit in calculations that we are dealing 94 // with bytes and not bits 95 static inline uint16_t BytesPerPixel(uint16_t aBPP) { return aBPP / 8; } 96 97 // Calculates the number of padding bytes that are needed per row of image data 98 static inline uint32_t PaddingBytes(uint16_t aBPP, uint32_t aWidth) { 99 uint32_t rowSize = aWidth * BytesPerPixel(aBPP); 100 uint8_t paddingSize = 0; 101 if (rowSize % 4) { 102 paddingSize = (4 - (rowSize % 4)); 103 } 104 return paddingSize; 105 } 106 107 // See ::InitFromData for other info. 108 NS_IMETHODIMP 109 nsBMPEncoder::StartImageEncode(uint32_t aWidth, uint32_t aHeight, 110 uint32_t aInputFormat, 111 const nsAString& aOutputOptions) { 112 // can't initialize more than once 113 if (mImageBufferStart || mImageBufferCurr) { 114 return NS_ERROR_ALREADY_INITIALIZED; 115 } 116 117 // validate input format 118 if (aInputFormat != INPUT_FORMAT_RGB && aInputFormat != INPUT_FORMAT_RGBA && 119 aInputFormat != INPUT_FORMAT_HOSTARGB) { 120 return NS_ERROR_INVALID_ARG; 121 } 122 123 // parse and check any provided output options 124 Version version; 125 uint16_t bpp; 126 nsresult rv = ParseOptions(aOutputOptions, version, bpp); 127 if (NS_FAILED(rv)) { 128 return rv; 129 } 130 MOZ_ASSERT(bpp <= 32); 131 132 rv = InitFileHeader(version, bpp, aWidth, aHeight); 133 if (NS_FAILED(rv)) { 134 return rv; 135 } 136 rv = InitInfoHeader(version, bpp, aWidth, aHeight); 137 if (NS_FAILED(rv)) { 138 return rv; 139 } 140 141 mImageBufferSize = mBMPFileHeader.filesize; 142 mImageBufferStart = static_cast<uint8_t*>(malloc(mImageBufferSize)); 143 if (!mImageBufferStart) { 144 return NS_ERROR_OUT_OF_MEMORY; 145 } 146 mImageBufferCurr = mImageBufferStart; 147 148 EncodeFileHeader(); 149 EncodeInfoHeader(); 150 151 return NS_OK; 152 } 153 154 // Returns the number of bytes in the image buffer used. 155 // For a BMP file, this is all bytes in the buffer. 156 NS_IMETHODIMP 157 nsBMPEncoder::GetImageBufferUsed(uint32_t* aOutputSize) { 158 NS_ENSURE_ARG_POINTER(aOutputSize); 159 *aOutputSize = mImageBufferSize; 160 return NS_OK; 161 } 162 163 // Returns a pointer to the start of the image buffer 164 NS_IMETHODIMP 165 nsBMPEncoder::GetImageBuffer(char** aOutputBuffer) { 166 NS_ENSURE_ARG_POINTER(aOutputBuffer); 167 *aOutputBuffer = reinterpret_cast<char*>(mImageBufferStart); 168 return NS_OK; 169 } 170 171 NS_IMETHODIMP 172 nsBMPEncoder::AddImageFrame(const uint8_t* aData, 173 uint32_t aLength, // (unused, req'd by JS) 174 uint32_t aWidth, uint32_t aHeight, uint32_t aStride, 175 uint32_t aInputFormat, 176 const nsAString& aFrameOptions) { 177 // must be initialized 178 if (!mImageBufferStart || !mImageBufferCurr) { 179 return NS_ERROR_NOT_INITIALIZED; 180 } 181 182 // validate input format 183 if (aInputFormat != INPUT_FORMAT_RGB && aInputFormat != INPUT_FORMAT_RGBA && 184 aInputFormat != INPUT_FORMAT_HOSTARGB) { 185 return NS_ERROR_INVALID_ARG; 186 } 187 188 if (mBMPInfoHeader.width < 0) { 189 return NS_ERROR_ILLEGAL_VALUE; 190 } 191 192 CheckedUint32 size = CheckedUint32(mBMPInfoHeader.width) * 193 CheckedUint32(BytesPerPixel(mBMPInfoHeader.bpp)); 194 if (MOZ_UNLIKELY(!size.isValid())) { 195 return NS_ERROR_FAILURE; 196 } 197 198 auto row = MakeUniqueFallible<uint8_t[]>(size.value()); 199 if (!row) { 200 return NS_ERROR_OUT_OF_MEMORY; 201 } 202 203 CheckedUint32 check = CheckedUint32(mBMPInfoHeader.height) * aStride; 204 if (MOZ_UNLIKELY(!check.isValid())) { 205 return NS_ERROR_FAILURE; 206 } 207 208 // write each row: if we add more input formats, we may want to 209 // generalize the conversions 210 if (aInputFormat == INPUT_FORMAT_HOSTARGB) { 211 // BMP requires RGBA with post-multiplied alpha, so we need to convert 212 for (int32_t y = mBMPInfoHeader.height - 1; y >= 0; y--) { 213 ConvertHostARGBRow(&aData[y * aStride], row, mBMPInfoHeader.width); 214 if (mBMPInfoHeader.bpp == 24) { 215 EncodeImageDataRow24(row.get()); 216 } else { 217 EncodeImageDataRow32(row.get()); 218 } 219 } 220 } else if (aInputFormat == INPUT_FORMAT_RGBA) { 221 // simple RGBA, no conversion needed 222 for (int32_t y = 0; y < mBMPInfoHeader.height; y++) { 223 if (mBMPInfoHeader.bpp == 24) { 224 EncodeImageDataRow24(row.get()); 225 } else { 226 EncodeImageDataRow32(row.get()); 227 } 228 } 229 } else if (aInputFormat == INPUT_FORMAT_RGB) { 230 // simple RGB, no conversion needed 231 for (int32_t y = 0; y < mBMPInfoHeader.height; y++) { 232 if (mBMPInfoHeader.bpp == 24) { 233 EncodeImageDataRow24(&aData[y * aStride]); 234 } else { 235 EncodeImageDataRow32(&aData[y * aStride]); 236 } 237 } 238 } else { 239 MOZ_ASSERT_UNREACHABLE("Bad format type"); 240 return NS_ERROR_INVALID_ARG; 241 } 242 243 return NS_OK; 244 } 245 246 NS_IMETHODIMP 247 nsBMPEncoder::EndImageEncode() { 248 // must be initialized 249 if (!mImageBufferStart || !mImageBufferCurr) { 250 return NS_ERROR_NOT_INITIALIZED; 251 } 252 253 mFinished = true; 254 NotifyListener(); 255 256 // if output callback can't get enough memory, it will free our buffer 257 if (!mImageBufferStart || !mImageBufferCurr) { 258 return NS_ERROR_OUT_OF_MEMORY; 259 } 260 261 return NS_OK; 262 } 263 264 // Parses the encoder options and sets the bits per pixel to use 265 // See InitFromData for a description of the parse options 266 nsresult nsBMPEncoder::ParseOptions(const nsAString& aOptions, 267 Version& aVersionOut, uint16_t& aBppOut) { 268 aVersionOut = VERSION_3; 269 aBppOut = 24; 270 271 // Parse the input string into a set of name/value pairs. 272 // From a format like: name=value;bpp=<bpp_value>;name=value 273 // to format: [0] = name=value, [1] = bpp=<bpp_value>, [2] = name=value 274 nsTArray<nsCString> nameValuePairs; 275 ParseString(NS_ConvertUTF16toUTF8(aOptions), ';', nameValuePairs); 276 277 // For each name/value pair in the set 278 for (uint32_t i = 0; i < nameValuePairs.Length(); ++i) { 279 // Split the name value pair [0] = name, [1] = value 280 nsTArray<nsCString> nameValuePair; 281 ParseString(nameValuePairs[i], '=', nameValuePair); 282 if (nameValuePair.Length() != 2) { 283 return NS_ERROR_INVALID_ARG; 284 } 285 286 // Parse the bpp portion of the string name=value;version=<version_value>; 287 // name=value 288 if (nameValuePair[0].Equals("version", 289 nsCaseInsensitiveCStringComparator)) { 290 if (nameValuePair[1].EqualsLiteral("3")) { 291 aVersionOut = VERSION_3; 292 } else if (nameValuePair[1].EqualsLiteral("5")) { 293 aVersionOut = VERSION_5; 294 } else { 295 return NS_ERROR_INVALID_ARG; 296 } 297 } 298 299 // Parse the bpp portion of the string name=value;bpp=<bpp_value>;name=value 300 if (nameValuePair[0].Equals("bpp", nsCaseInsensitiveCStringComparator)) { 301 if (nameValuePair[1].EqualsLiteral("24")) { 302 aBppOut = 24; 303 } else if (nameValuePair[1].EqualsLiteral("32")) { 304 aBppOut = 32; 305 } else { 306 return NS_ERROR_INVALID_ARG; 307 } 308 } 309 } 310 311 return NS_OK; 312 } 313 314 NS_IMETHODIMP 315 nsBMPEncoder::Close() { 316 if (mImageBufferStart) { 317 free(mImageBufferStart); 318 mImageBufferStart = nullptr; 319 mImageBufferSize = 0; 320 mImageBufferReadPoint = 0; 321 mImageBufferCurr = nullptr; 322 } 323 324 return NS_OK; 325 } 326 327 // Obtains the available bytes to read 328 NS_IMETHODIMP 329 nsBMPEncoder::Available(uint64_t* _retval) { 330 if (!mImageBufferStart || !mImageBufferCurr) { 331 return NS_BASE_STREAM_CLOSED; 332 } 333 334 *_retval = GetCurrentImageBufferOffset() - mImageBufferReadPoint; 335 return NS_OK; 336 } 337 338 // Obtains the stream's status 339 NS_IMETHODIMP 340 nsBMPEncoder::StreamStatus() { 341 return mImageBufferStart && mImageBufferCurr ? NS_OK : NS_BASE_STREAM_CLOSED; 342 } 343 344 // [noscript] Reads bytes which are available 345 NS_IMETHODIMP 346 nsBMPEncoder::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) { 347 return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval); 348 } 349 350 // [noscript] Reads segments 351 NS_IMETHODIMP 352 nsBMPEncoder::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, 353 uint32_t aCount, uint32_t* _retval) { 354 uint32_t maxCount = GetCurrentImageBufferOffset() - mImageBufferReadPoint; 355 if (maxCount == 0) { 356 *_retval = 0; 357 return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK; 358 } 359 360 if (aCount > maxCount) { 361 aCount = maxCount; 362 } 363 nsresult rv = aWriter( 364 this, aClosure, 365 reinterpret_cast<const char*>(mImageBufferStart + mImageBufferReadPoint), 366 0, aCount, _retval); 367 if (NS_SUCCEEDED(rv)) { 368 NS_ASSERTION(*_retval <= aCount, "bad write count"); 369 mImageBufferReadPoint += *_retval; 370 } 371 // errors returned from the writer end here! 372 return NS_OK; 373 } 374 375 NS_IMETHODIMP 376 nsBMPEncoder::IsNonBlocking(bool* _retval) { 377 *_retval = true; 378 return NS_OK; 379 } 380 381 NS_IMETHODIMP 382 nsBMPEncoder::AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags, 383 uint32_t aRequestedCount, nsIEventTarget* aTarget) { 384 if (aFlags != 0) { 385 return NS_ERROR_NOT_IMPLEMENTED; 386 } 387 388 if (mCallback || mCallbackTarget) { 389 return NS_ERROR_UNEXPECTED; 390 } 391 392 mCallbackTarget = aTarget; 393 // 0 means "any number of bytes except 0" 394 mNotifyThreshold = aRequestedCount; 395 if (!aRequestedCount) { 396 mNotifyThreshold = 1024; // We don't want to notify incessantly 397 } 398 399 // We set the callback absolutely last, because NotifyListener uses it to 400 // determine if someone needs to be notified. If we don't set it last, 401 // NotifyListener might try to fire off a notification to a null target 402 // which will generally cause non-threadsafe objects to be used off the 403 // main thread 404 mCallback = aCallback; 405 406 // What we are being asked for may be present already 407 NotifyListener(); 408 return NS_OK; 409 } 410 411 NS_IMETHODIMP 412 nsBMPEncoder::CloseWithStatus(nsresult aStatus) { return Close(); } 413 414 // nsBMPEncoder::ConvertHostARGBRow 415 // 416 // Our colors are stored with premultiplied alphas, but we need 417 // an output with no alpha in machine-independent byte order. 418 // 419 void nsBMPEncoder::ConvertHostARGBRow(const uint8_t* aSrc, 420 const UniquePtr<uint8_t[]>& aDest, 421 uint32_t aPixelWidth) { 422 uint16_t bytes = BytesPerPixel(mBMPInfoHeader.bpp); 423 424 if (mBMPInfoHeader.bpp == 32) { 425 for (uint32_t x = 0; x < aPixelWidth; x++) { 426 const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x]; 427 uint8_t* pixelOut = &aDest[x * bytes]; 428 429 pixelOut[0] = (pixelIn & 0x00ff0000) >> 16; 430 pixelOut[1] = (pixelIn & 0x0000ff00) >> 8; 431 pixelOut[2] = (pixelIn & 0x000000ff) >> 0; 432 pixelOut[3] = (pixelIn & 0xff000000) >> 24; 433 } 434 } else { 435 for (uint32_t x = 0; x < aPixelWidth; x++) { 436 const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x]; 437 uint8_t* pixelOut = &aDest[x * bytes]; 438 439 pixelOut[0] = (pixelIn & 0xff0000) >> 16; 440 pixelOut[1] = (pixelIn & 0x00ff00) >> 8; 441 pixelOut[2] = (pixelIn & 0x0000ff) >> 0; 442 } 443 } 444 } 445 446 void nsBMPEncoder::NotifyListener() { 447 if (mCallback && (GetCurrentImageBufferOffset() - mImageBufferReadPoint >= 448 mNotifyThreshold || 449 mFinished)) { 450 nsCOMPtr<nsIInputStreamCallback> callback; 451 if (mCallbackTarget) { 452 callback = NS_NewInputStreamReadyEvent("nsBMPEncoder::NotifyListener", 453 mCallback, mCallbackTarget); 454 } else { 455 callback = mCallback; 456 } 457 458 NS_ASSERTION(callback, "Shouldn't fail to make the callback"); 459 // Null the callback first because OnInputStreamReady could 460 // reenter AsyncWait 461 mCallback = nullptr; 462 mCallbackTarget = nullptr; 463 mNotifyThreshold = 0; 464 465 callback->OnInputStreamReady(this); 466 } 467 } 468 469 // Initializes the BMP file header mBMPFileHeader to the passed in values 470 nsresult nsBMPEncoder::InitFileHeader(Version aVersion, uint16_t aBPP, 471 uint32_t aWidth, uint32_t aHeight) { 472 memset(&mBMPFileHeader, 0, sizeof(mBMPFileHeader)); 473 mBMPFileHeader.signature[0] = 'B'; 474 mBMPFileHeader.signature[1] = 'M'; 475 476 if (aVersion == VERSION_3) { 477 mBMPFileHeader.dataoffset = FILE_HEADER_LENGTH + InfoHeaderLength::WIN_V3; 478 } else { // aVersion == 5 479 mBMPFileHeader.dataoffset = FILE_HEADER_LENGTH + InfoHeaderLength::WIN_V5; 480 } 481 482 // The color table is present only if BPP is <= 8 483 if (aBPP <= 8) { 484 uint32_t numColors = 1 << aBPP; 485 mBMPFileHeader.dataoffset += 4 * numColors; 486 CheckedUint32 filesize = CheckedUint32(mBMPFileHeader.dataoffset) + 487 CheckedUint32(aWidth) * aHeight; 488 if (MOZ_UNLIKELY(!filesize.isValid())) { 489 return NS_ERROR_INVALID_ARG; 490 } 491 mBMPFileHeader.filesize = filesize.value(); 492 } else { 493 CheckedUint32 filesize = CheckedUint32(mBMPFileHeader.dataoffset) + 494 (CheckedUint32(aWidth) * BytesPerPixel(aBPP) + 495 PaddingBytes(aBPP, aWidth)) * 496 aHeight; 497 if (MOZ_UNLIKELY(!filesize.isValid())) { 498 return NS_ERROR_INVALID_ARG; 499 } 500 mBMPFileHeader.filesize = filesize.value(); 501 } 502 503 mBMPFileHeader.reserved = 0; 504 505 return NS_OK; 506 } 507 508 #define ENCODE(pImageBufferCurr, value) \ 509 memcpy(*pImageBufferCurr, &value, sizeof value); \ 510 *pImageBufferCurr += sizeof value; 511 512 // Initializes the bitmap info header mBMPInfoHeader to the passed in values 513 nsresult nsBMPEncoder::InitInfoHeader(Version aVersion, uint16_t aBPP, 514 uint32_t aWidth, uint32_t aHeight) { 515 memset(&mBMPInfoHeader, 0, sizeof(mBMPInfoHeader)); 516 if (aVersion == VERSION_3) { 517 mBMPInfoHeader.bihsize = InfoHeaderLength::WIN_V3; 518 } else { 519 MOZ_ASSERT(aVersion == VERSION_5); 520 mBMPInfoHeader.bihsize = InfoHeaderLength::WIN_V5; 521 } 522 523 CheckedInt32 width(aWidth); 524 CheckedInt32 height(aHeight); 525 if (MOZ_UNLIKELY(!width.isValid() || !height.isValid())) { 526 return NS_ERROR_INVALID_ARG; 527 } 528 mBMPInfoHeader.width = width.value(); 529 mBMPInfoHeader.height = height.value(); 530 531 mBMPInfoHeader.planes = 1; 532 mBMPInfoHeader.bpp = aBPP; 533 mBMPInfoHeader.compression = 0; 534 mBMPInfoHeader.colors = 0; 535 mBMPInfoHeader.important_colors = 0; 536 537 CheckedUint32 check = CheckedUint32(aWidth) * BytesPerPixel(aBPP); 538 if (MOZ_UNLIKELY(!check.isValid())) { 539 return NS_ERROR_INVALID_ARG; 540 } 541 542 if (aBPP <= 8) { 543 CheckedUint32 imagesize = CheckedUint32(aWidth) * aHeight; 544 if (MOZ_UNLIKELY(!imagesize.isValid())) { 545 return NS_ERROR_INVALID_ARG; 546 } 547 mBMPInfoHeader.image_size = imagesize.value(); 548 } else { 549 CheckedUint32 imagesize = (CheckedUint32(aWidth) * BytesPerPixel(aBPP) + 550 PaddingBytes(aBPP, aWidth)) * 551 CheckedUint32(aHeight); 552 if (MOZ_UNLIKELY(!imagesize.isValid())) { 553 return NS_ERROR_INVALID_ARG; 554 } 555 mBMPInfoHeader.image_size = imagesize.value(); 556 } 557 mBMPInfoHeader.xppm = 0; 558 mBMPInfoHeader.yppm = 0; 559 if (aVersion >= VERSION_5) { 560 mBMPInfoHeader.red_mask = 0x000000FF; 561 mBMPInfoHeader.green_mask = 0x0000FF00; 562 mBMPInfoHeader.blue_mask = 0x00FF0000; 563 mBMPInfoHeader.alpha_mask = 0xFF000000; 564 mBMPInfoHeader.color_space = V5InfoHeader::COLOR_SPACE_LCS_SRGB; 565 mBMPInfoHeader.white_point.r.x = 0; 566 mBMPInfoHeader.white_point.r.y = 0; 567 mBMPInfoHeader.white_point.r.z = 0; 568 mBMPInfoHeader.white_point.g.x = 0; 569 mBMPInfoHeader.white_point.g.y = 0; 570 mBMPInfoHeader.white_point.g.z = 0; 571 mBMPInfoHeader.white_point.b.x = 0; 572 mBMPInfoHeader.white_point.b.y = 0; 573 mBMPInfoHeader.white_point.b.z = 0; 574 mBMPInfoHeader.gamma_red = 0; 575 mBMPInfoHeader.gamma_green = 0; 576 mBMPInfoHeader.gamma_blue = 0; 577 mBMPInfoHeader.intent = 0; 578 mBMPInfoHeader.profile_offset = 0; 579 mBMPInfoHeader.profile_size = 0; 580 mBMPInfoHeader.reserved = 0; 581 } 582 583 return NS_OK; 584 } 585 586 // Encodes the BMP file header mBMPFileHeader 587 void nsBMPEncoder::EncodeFileHeader() { 588 FileHeader littleEndianBFH = mBMPFileHeader; 589 NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.filesize, 1); 590 NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.reserved, 1); 591 NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.dataoffset, 1); 592 593 ENCODE(&mImageBufferCurr, littleEndianBFH.signature); 594 ENCODE(&mImageBufferCurr, littleEndianBFH.filesize); 595 ENCODE(&mImageBufferCurr, littleEndianBFH.reserved); 596 ENCODE(&mImageBufferCurr, littleEndianBFH.dataoffset); 597 } 598 599 // Encodes the BMP info header mBMPInfoHeader 600 void nsBMPEncoder::EncodeInfoHeader() { 601 V5InfoHeader littleEndianmBIH = mBMPInfoHeader; 602 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.bihsize, 1); 603 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.width, 1); 604 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.height, 1); 605 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.planes, 1); 606 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.bpp, 1); 607 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.compression, 1); 608 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.image_size, 1); 609 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.xppm, 1); 610 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.yppm, 1); 611 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.colors, 1); 612 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.important_colors, 613 1); 614 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.red_mask, 1); 615 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.green_mask, 1); 616 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.blue_mask, 1); 617 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.alpha_mask, 1); 618 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.color_space, 1); 619 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.x, 1); 620 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.y, 1); 621 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.z, 1); 622 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.x, 1); 623 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.y, 1); 624 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.z, 1); 625 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.x, 1); 626 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.y, 1); 627 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.z, 1); 628 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_red, 1); 629 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_green, 1); 630 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_blue, 1); 631 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.intent, 1); 632 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_offset, 1); 633 NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_size, 1); 634 635 ENCODE(&mImageBufferCurr, littleEndianmBIH.bihsize); 636 ENCODE(&mImageBufferCurr, littleEndianmBIH.width); 637 ENCODE(&mImageBufferCurr, littleEndianmBIH.height); 638 ENCODE(&mImageBufferCurr, littleEndianmBIH.planes); 639 ENCODE(&mImageBufferCurr, littleEndianmBIH.bpp); 640 ENCODE(&mImageBufferCurr, littleEndianmBIH.compression); 641 ENCODE(&mImageBufferCurr, littleEndianmBIH.image_size); 642 ENCODE(&mImageBufferCurr, littleEndianmBIH.xppm); 643 ENCODE(&mImageBufferCurr, littleEndianmBIH.yppm); 644 ENCODE(&mImageBufferCurr, littleEndianmBIH.colors); 645 ENCODE(&mImageBufferCurr, littleEndianmBIH.important_colors); 646 647 if (mBMPInfoHeader.bihsize > InfoHeaderLength::WIN_V3) { 648 ENCODE(&mImageBufferCurr, littleEndianmBIH.red_mask); 649 ENCODE(&mImageBufferCurr, littleEndianmBIH.green_mask); 650 ENCODE(&mImageBufferCurr, littleEndianmBIH.blue_mask); 651 ENCODE(&mImageBufferCurr, littleEndianmBIH.alpha_mask); 652 ENCODE(&mImageBufferCurr, littleEndianmBIH.color_space); 653 ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.x); 654 ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.y); 655 ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.z); 656 ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.x); 657 ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.y); 658 ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.z); 659 ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.x); 660 ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.y); 661 ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.z); 662 ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_red); 663 ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_green); 664 ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_blue); 665 ENCODE(&mImageBufferCurr, littleEndianmBIH.intent); 666 ENCODE(&mImageBufferCurr, littleEndianmBIH.profile_offset); 667 ENCODE(&mImageBufferCurr, littleEndianmBIH.profile_size); 668 ENCODE(&mImageBufferCurr, littleEndianmBIH.reserved); 669 } 670 } 671 672 // Sets a pixel in the image buffer that doesn't have alpha data 673 static inline void SetPixel24(uint8_t*& imageBufferCurr, uint8_t aRed, 674 uint8_t aGreen, uint8_t aBlue) { 675 *imageBufferCurr = aBlue; 676 *(imageBufferCurr + 1) = aGreen; 677 *(imageBufferCurr + 2) = aRed; 678 } 679 680 // Sets a pixel in the image buffer with alpha data 681 static inline void SetPixel32(uint8_t*& imageBufferCurr, uint8_t aRed, 682 uint8_t aGreen, uint8_t aBlue, 683 uint8_t aAlpha = 0xFF) { 684 *imageBufferCurr = aBlue; 685 *(imageBufferCurr + 1) = aGreen; 686 *(imageBufferCurr + 2) = aRed; 687 *(imageBufferCurr + 3) = aAlpha; 688 } 689 690 // Encodes a row of image data which does not have alpha data 691 void nsBMPEncoder::EncodeImageDataRow24(const uint8_t* aData) { 692 for (int32_t x = 0; x < mBMPInfoHeader.width; x++) { 693 uint32_t pos = x * BytesPerPixel(mBMPInfoHeader.bpp); 694 SetPixel24(mImageBufferCurr, aData[pos], aData[pos + 1], aData[pos + 2]); 695 mImageBufferCurr += BytesPerPixel(mBMPInfoHeader.bpp); 696 } 697 698 for (uint32_t x = 0; 699 x < PaddingBytes(mBMPInfoHeader.bpp, mBMPInfoHeader.width); x++) { 700 *mImageBufferCurr++ = 0; 701 } 702 } 703 704 // Encodes a row of image data which does have alpha data 705 void nsBMPEncoder::EncodeImageDataRow32(const uint8_t* aData) { 706 for (int32_t x = 0; x < mBMPInfoHeader.width; x++) { 707 uint32_t pos = x * BytesPerPixel(mBMPInfoHeader.bpp); 708 SetPixel32(mImageBufferCurr, aData[pos], aData[pos + 1], aData[pos + 2], 709 aData[pos + 3]); 710 mImageBufferCurr += 4; 711 } 712 713 for (uint32_t x = 0; 714 x < PaddingBytes(mBMPInfoHeader.bpp, mBMPInfoHeader.width); x++) { 715 *mImageBufferCurr++ = 0; 716 } 717 }