image/encoders/bmp/nsBMPEncoder.cpp

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

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

mercurial