1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/encoders/bmp/nsBMPEncoder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,705 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "nsCRT.h" 1.9 +#include "mozilla/Endian.h" 1.10 +#include "nsBMPEncoder.h" 1.11 +#include "prprf.h" 1.12 +#include "nsString.h" 1.13 +#include "nsStreamUtils.h" 1.14 +#include "nsTArray.h" 1.15 +#include "nsAutoPtr.h" 1.16 + 1.17 +using namespace mozilla; 1.18 + 1.19 +NS_IMPL_ISUPPORTS(nsBMPEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream) 1.20 + 1.21 +nsBMPEncoder::nsBMPEncoder() : mImageBufferStart(nullptr), 1.22 + mImageBufferCurr(0), 1.23 + mImageBufferSize(0), 1.24 + mImageBufferReadPoint(0), 1.25 + mFinished(false), 1.26 + mCallback(nullptr), 1.27 + mCallbackTarget(nullptr), 1.28 + mNotifyThreshold(0) 1.29 +{ 1.30 +} 1.31 + 1.32 +nsBMPEncoder::~nsBMPEncoder() 1.33 +{ 1.34 + if (mImageBufferStart) { 1.35 + moz_free(mImageBufferStart); 1.36 + mImageBufferStart = nullptr; 1.37 + mImageBufferCurr = nullptr; 1.38 + } 1.39 +} 1.40 + 1.41 +// nsBMPEncoder::InitFromData 1.42 +// 1.43 +// One output option is supported: bpp=<bpp_value> 1.44 +// bpp specifies the bits per pixel to use where bpp_value can be 24 or 32 1.45 +NS_IMETHODIMP nsBMPEncoder::InitFromData(const uint8_t* aData, 1.46 + uint32_t aLength, // (unused, 1.47 + // req'd by JS) 1.48 + uint32_t aWidth, 1.49 + uint32_t aHeight, 1.50 + uint32_t aStride, 1.51 + uint32_t aInputFormat, 1.52 + const nsAString& aOutputOptions) 1.53 +{ 1.54 + // validate input format 1.55 + if (aInputFormat != INPUT_FORMAT_RGB && 1.56 + aInputFormat != INPUT_FORMAT_RGBA && 1.57 + aInputFormat != INPUT_FORMAT_HOSTARGB) { 1.58 + return NS_ERROR_INVALID_ARG; 1.59 + } 1.60 + 1.61 + // Stride is the padded width of each row, so it better be longer 1.62 + if ((aInputFormat == INPUT_FORMAT_RGB && 1.63 + aStride < aWidth * 3) || 1.64 + ((aInputFormat == INPUT_FORMAT_RGBA || aInputFormat == INPUT_FORMAT_HOSTARGB) && 1.65 + aStride < aWidth * 4)) { 1.66 + NS_WARNING("Invalid stride for InitFromData"); 1.67 + return NS_ERROR_INVALID_ARG; 1.68 + } 1.69 + 1.70 + nsresult rv; 1.71 + rv = StartImageEncode(aWidth, aHeight, aInputFormat, aOutputOptions); 1.72 + if (NS_FAILED(rv)) { 1.73 + return rv; 1.74 + } 1.75 + 1.76 + rv = AddImageFrame(aData, aLength, aWidth, aHeight, aStride, 1.77 + aInputFormat, aOutputOptions); 1.78 + if (NS_FAILED(rv)) { 1.79 + return rv; 1.80 + } 1.81 + 1.82 + rv = EndImageEncode(); 1.83 + return rv; 1.84 +} 1.85 + 1.86 +// Just a helper method to make it explicit in calculations that we are dealing 1.87 +// with bytes and not bits 1.88 +static inline uint32_t 1.89 +BytesPerPixel(uint32_t aBPP) 1.90 +{ 1.91 + return aBPP / 8; 1.92 +} 1.93 + 1.94 +// Calculates the number of padding bytes that are needed per row of image data 1.95 +static inline uint32_t 1.96 +PaddingBytes(uint32_t aBPP, uint32_t aWidth) 1.97 +{ 1.98 + uint32_t rowSize = aWidth * BytesPerPixel(aBPP); 1.99 + uint8_t paddingSize = 0; 1.100 + if(rowSize % 4) { 1.101 + paddingSize = (4 - (rowSize % 4)); 1.102 + } 1.103 + return paddingSize; 1.104 +} 1.105 + 1.106 +// See ::InitFromData for other info. 1.107 +NS_IMETHODIMP nsBMPEncoder::StartImageEncode(uint32_t aWidth, 1.108 + uint32_t aHeight, 1.109 + uint32_t aInputFormat, 1.110 + const nsAString& aOutputOptions) 1.111 +{ 1.112 + // can't initialize more than once 1.113 + if (mImageBufferStart || mImageBufferCurr) { 1.114 + return NS_ERROR_ALREADY_INITIALIZED; 1.115 + } 1.116 + 1.117 + // validate input format 1.118 + if (aInputFormat != INPUT_FORMAT_RGB && 1.119 + aInputFormat != INPUT_FORMAT_RGBA && 1.120 + aInputFormat != INPUT_FORMAT_HOSTARGB) { 1.121 + return NS_ERROR_INVALID_ARG; 1.122 + } 1.123 + 1.124 + // parse and check any provided output options 1.125 + Version version; 1.126 + uint32_t bpp; 1.127 + nsresult rv = ParseOptions(aOutputOptions, &version, &bpp); 1.128 + if (NS_FAILED(rv)) { 1.129 + return rv; 1.130 + } 1.131 + 1.132 + InitFileHeader(version, bpp, aWidth, aHeight); 1.133 + InitInfoHeader(version, bpp, aWidth, aHeight); 1.134 + 1.135 + mImageBufferSize = mBMPFileHeader.filesize; 1.136 + mImageBufferStart = static_cast<uint8_t*>(moz_malloc(mImageBufferSize)); 1.137 + if (!mImageBufferStart) { 1.138 + return NS_ERROR_OUT_OF_MEMORY; 1.139 + } 1.140 + mImageBufferCurr = mImageBufferStart; 1.141 + 1.142 + EncodeFileHeader(); 1.143 + EncodeInfoHeader(); 1.144 + 1.145 + return NS_OK; 1.146 +} 1.147 + 1.148 +// Returns the number of bytes in the image buffer used. 1.149 +// For a BMP file, this is all bytes in the buffer. 1.150 +NS_IMETHODIMP nsBMPEncoder::GetImageBufferUsed(uint32_t *aOutputSize) 1.151 +{ 1.152 + NS_ENSURE_ARG_POINTER(aOutputSize); 1.153 + *aOutputSize = mImageBufferSize; 1.154 + return NS_OK; 1.155 +} 1.156 + 1.157 +// Returns a pointer to the start of the image buffer 1.158 +NS_IMETHODIMP nsBMPEncoder::GetImageBuffer(char **aOutputBuffer) 1.159 +{ 1.160 + NS_ENSURE_ARG_POINTER(aOutputBuffer); 1.161 + *aOutputBuffer = reinterpret_cast<char*>(mImageBufferStart); 1.162 + return NS_OK; 1.163 +} 1.164 + 1.165 +NS_IMETHODIMP nsBMPEncoder::AddImageFrame(const uint8_t* aData, 1.166 + uint32_t aLength, // (unused, 1.167 + // req'd by JS) 1.168 + uint32_t aWidth, 1.169 + uint32_t aHeight, 1.170 + uint32_t aStride, 1.171 + uint32_t aInputFormat, 1.172 + const nsAString& aFrameOptions) 1.173 +{ 1.174 + // must be initialized 1.175 + if (!mImageBufferStart || !mImageBufferCurr) { 1.176 + return NS_ERROR_NOT_INITIALIZED; 1.177 + } 1.178 + 1.179 + // validate input format 1.180 + if (aInputFormat != INPUT_FORMAT_RGB && 1.181 + aInputFormat != INPUT_FORMAT_RGBA && 1.182 + aInputFormat != INPUT_FORMAT_HOSTARGB) { 1.183 + return NS_ERROR_INVALID_ARG; 1.184 + } 1.185 + 1.186 + static fallible_t fallible = fallible_t(); 1.187 + nsAutoArrayPtr<uint8_t> row(new (fallible) 1.188 + uint8_t[mBMPInfoHeader.width * 1.189 + BytesPerPixel(mBMPInfoHeader.bpp)]); 1.190 + if (!row) { 1.191 + return NS_ERROR_OUT_OF_MEMORY; 1.192 + } 1.193 + 1.194 + // write each row: if we add more input formats, we may want to 1.195 + // generalize the conversions 1.196 + if (aInputFormat == INPUT_FORMAT_HOSTARGB) { 1.197 + // BMP requires RGBA with post-multiplied alpha, so we need to convert 1.198 + for (int32_t y = mBMPInfoHeader.height - 1; y >= 0 ; y --) { 1.199 + ConvertHostARGBRow(&aData[y * aStride], row, mBMPInfoHeader.width); 1.200 + if(mBMPInfoHeader.bpp == 24) { 1.201 + EncodeImageDataRow24(row); 1.202 + } else { 1.203 + EncodeImageDataRow32(row); 1.204 + } 1.205 + } 1.206 + } else if (aInputFormat == INPUT_FORMAT_RGBA) { 1.207 + // simple RGBA, no conversion needed 1.208 + for (int32_t y = 0; y < mBMPInfoHeader.height; y ++) { 1.209 + if (mBMPInfoHeader.bpp == 24) { 1.210 + EncodeImageDataRow24(row); 1.211 + } else { 1.212 + EncodeImageDataRow32(row); 1.213 + } 1.214 + } 1.215 + } else if (aInputFormat == INPUT_FORMAT_RGB) { 1.216 + // simple RGB, no conversion needed 1.217 + for (int32_t y = 0; y < mBMPInfoHeader.height; y ++) { 1.218 + if (mBMPInfoHeader.bpp == 24) { 1.219 + EncodeImageDataRow24(&aData[y * aStride]); 1.220 + } else { 1.221 + EncodeImageDataRow32(&aData[y * aStride]); 1.222 + } 1.223 + } 1.224 + } else { 1.225 + NS_NOTREACHED("Bad format type"); 1.226 + return NS_ERROR_INVALID_ARG; 1.227 + } 1.228 + 1.229 + return NS_OK; 1.230 +} 1.231 + 1.232 + 1.233 +NS_IMETHODIMP nsBMPEncoder::EndImageEncode() 1.234 +{ 1.235 + // must be initialized 1.236 + if (!mImageBufferStart || !mImageBufferCurr) { 1.237 + return NS_ERROR_NOT_INITIALIZED; 1.238 + } 1.239 + 1.240 + mFinished = true; 1.241 + NotifyListener(); 1.242 + 1.243 + // if output callback can't get enough memory, it will free our buffer 1.244 + if (!mImageBufferStart || !mImageBufferCurr) { 1.245 + return NS_ERROR_OUT_OF_MEMORY; 1.246 + } 1.247 + 1.248 + return NS_OK; 1.249 +} 1.250 + 1.251 + 1.252 +// Parses the encoder options and sets the bits per pixel to use 1.253 +// See InitFromData for a description of the parse options 1.254 +nsresult 1.255 +nsBMPEncoder::ParseOptions(const nsAString& aOptions, Version* version, 1.256 + uint32_t* bpp) 1.257 +{ 1.258 + if (version) { 1.259 + *version = VERSION_3; 1.260 + } 1.261 + if (bpp) { 1.262 + *bpp = 24; 1.263 + } 1.264 + 1.265 + // Parse the input string into a set of name/value pairs. 1.266 + // From a format like: name=value;bpp=<bpp_value>;name=value 1.267 + // to format: [0] = name=value, [1] = bpp=<bpp_value>, [2] = name=value 1.268 + nsTArray<nsCString> nameValuePairs; 1.269 + if (!ParseString(NS_ConvertUTF16toUTF8(aOptions), ';', nameValuePairs)) { 1.270 + return NS_ERROR_INVALID_ARG; 1.271 + } 1.272 + 1.273 + // For each name/value pair in the set 1.274 + for (uint32_t i = 0; i < nameValuePairs.Length(); ++i) { 1.275 + 1.276 + // Split the name value pair [0] = name, [1] = value 1.277 + nsTArray<nsCString> nameValuePair; 1.278 + if (!ParseString(nameValuePairs[i], '=', nameValuePair)) { 1.279 + return NS_ERROR_INVALID_ARG; 1.280 + } 1.281 + if (nameValuePair.Length() != 2) { 1.282 + return NS_ERROR_INVALID_ARG; 1.283 + } 1.284 + 1.285 + // Parse the bpp portion of the string name=value;version=<version_value>; 1.286 + // name=value 1.287 + if (nameValuePair[0].Equals("version", 1.288 + nsCaseInsensitiveCStringComparator())) { 1.289 + if (nameValuePair[1].Equals("3")) { 1.290 + *version = VERSION_3; 1.291 + } else if (nameValuePair[1].Equals("5")) { 1.292 + *version = VERSION_5; 1.293 + } else { 1.294 + return NS_ERROR_INVALID_ARG; 1.295 + } 1.296 + } 1.297 + 1.298 + // Parse the bpp portion of the string name=value;bpp=<bpp_value>;name=value 1.299 + if (nameValuePair[0].Equals("bpp", nsCaseInsensitiveCStringComparator())) { 1.300 + if (nameValuePair[1].Equals("24")) { 1.301 + *bpp = 24; 1.302 + } else if (nameValuePair[1].Equals("32")) { 1.303 + *bpp = 32; 1.304 + } else { 1.305 + return NS_ERROR_INVALID_ARG; 1.306 + } 1.307 + } 1.308 + } 1.309 + 1.310 + return NS_OK; 1.311 +} 1.312 + 1.313 +NS_IMETHODIMP nsBMPEncoder::Close() 1.314 +{ 1.315 + if (mImageBufferStart) { 1.316 + moz_free(mImageBufferStart); 1.317 + mImageBufferStart = nullptr; 1.318 + mImageBufferSize = 0; 1.319 + mImageBufferReadPoint = 0; 1.320 + mImageBufferCurr = nullptr; 1.321 + } 1.322 + 1.323 + return NS_OK; 1.324 +} 1.325 + 1.326 +// Obtains the available bytes to read 1.327 +NS_IMETHODIMP nsBMPEncoder::Available(uint64_t *_retval) 1.328 +{ 1.329 + if (!mImageBufferStart || !mImageBufferCurr) { 1.330 + return NS_BASE_STREAM_CLOSED; 1.331 + } 1.332 + 1.333 + *_retval = GetCurrentImageBufferOffset() - mImageBufferReadPoint; 1.334 + return NS_OK; 1.335 +} 1.336 + 1.337 +// [noscript] Reads bytes which are available 1.338 +NS_IMETHODIMP nsBMPEncoder::Read(char * aBuf, uint32_t aCount, 1.339 + uint32_t *_retval) 1.340 +{ 1.341 + return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval); 1.342 +} 1.343 + 1.344 +// [noscript] Reads segments 1.345 +NS_IMETHODIMP nsBMPEncoder::ReadSegments(nsWriteSegmentFun aWriter, 1.346 + void *aClosure, uint32_t aCount, 1.347 + uint32_t *_retval) 1.348 +{ 1.349 + uint32_t maxCount = GetCurrentImageBufferOffset() - mImageBufferReadPoint; 1.350 + if (maxCount == 0) { 1.351 + *_retval = 0; 1.352 + return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK; 1.353 + } 1.354 + 1.355 + if (aCount > maxCount) { 1.356 + aCount = maxCount; 1.357 + } 1.358 + nsresult rv = aWriter(this, aClosure, 1.359 + reinterpret_cast<const char*>(mImageBufferStart + 1.360 + mImageBufferReadPoint), 1.361 + 0, aCount, _retval); 1.362 + if (NS_SUCCEEDED(rv)) { 1.363 + NS_ASSERTION(*_retval <= aCount, "bad write count"); 1.364 + mImageBufferReadPoint += *_retval; 1.365 + } 1.366 + // errors returned from the writer end here! 1.367 + return NS_OK; 1.368 +} 1.369 + 1.370 +NS_IMETHODIMP 1.371 +nsBMPEncoder::IsNonBlocking(bool *_retval) 1.372 +{ 1.373 + *_retval = true; 1.374 + return NS_OK; 1.375 +} 1.376 + 1.377 +NS_IMETHODIMP 1.378 +nsBMPEncoder::AsyncWait(nsIInputStreamCallback *aCallback, 1.379 + uint32_t aFlags, 1.380 + uint32_t aRequestedCount, 1.381 + nsIEventTarget *aTarget) 1.382 +{ 1.383 + if (aFlags != 0) { 1.384 + return NS_ERROR_NOT_IMPLEMENTED; 1.385 + } 1.386 + 1.387 + if (mCallback || mCallbackTarget) { 1.388 + return NS_ERROR_UNEXPECTED; 1.389 + } 1.390 + 1.391 + mCallbackTarget = aTarget; 1.392 + // 0 means "any number of bytes except 0" 1.393 + mNotifyThreshold = aRequestedCount; 1.394 + if (!aRequestedCount) { 1.395 + mNotifyThreshold = 1024; // We don't want to notify incessantly 1.396 + } 1.397 + 1.398 + // We set the callback absolutely last, because NotifyListener uses it to 1.399 + // determine if someone needs to be notified. If we don't set it last, 1.400 + // NotifyListener might try to fire off a notification to a null target 1.401 + // which will generally cause non-threadsafe objects to be used off the main thread 1.402 + mCallback = aCallback; 1.403 + 1.404 + // What we are being asked for may be present already 1.405 + NotifyListener(); 1.406 + return NS_OK; 1.407 +} 1.408 + 1.409 +NS_IMETHODIMP nsBMPEncoder::CloseWithStatus(nsresult aStatus) 1.410 +{ 1.411 + return Close(); 1.412 +} 1.413 + 1.414 +// nsBMPEncoder::ConvertHostARGBRow 1.415 +// 1.416 +// Our colors are stored with premultiplied alphas, but we need 1.417 +// an output with no alpha in machine-independent byte order. 1.418 +// 1.419 +void 1.420 +nsBMPEncoder::ConvertHostARGBRow(const uint8_t* aSrc, uint8_t* aDest, 1.421 + uint32_t aPixelWidth) 1.422 +{ 1.423 + int bytes = BytesPerPixel(mBMPInfoHeader.bpp); 1.424 + 1.425 + if (mBMPInfoHeader.bpp == 32) { 1.426 + for (uint32_t x = 0; x < aPixelWidth; x++) { 1.427 + const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x]; 1.428 + uint8_t *pixelOut = &aDest[x * bytes]; 1.429 + 1.430 + pixelOut[0] = (pixelIn & 0x00ff0000) >> 16; 1.431 + pixelOut[1] = (pixelIn & 0x0000ff00) >> 8; 1.432 + pixelOut[2] = (pixelIn & 0x000000ff) >> 0; 1.433 + pixelOut[3] = (pixelIn & 0xff000000) >> 24; 1.434 + } 1.435 + } else { 1.436 + for (uint32_t x = 0; x < aPixelWidth; x++) { 1.437 + const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x]; 1.438 + uint8_t *pixelOut = &aDest[x * bytes]; 1.439 + 1.440 + pixelOut[0] = (pixelIn & 0xff0000) >> 16; 1.441 + pixelOut[1] = (pixelIn & 0x00ff00) >> 8; 1.442 + pixelOut[2] = (pixelIn & 0x0000ff) >> 0; 1.443 + } 1.444 + } 1.445 +} 1.446 + 1.447 +void 1.448 +nsBMPEncoder::NotifyListener() 1.449 +{ 1.450 + if (mCallback && 1.451 + (GetCurrentImageBufferOffset() - mImageBufferReadPoint >= 1.452 + mNotifyThreshold || mFinished)) { 1.453 + nsCOMPtr<nsIInputStreamCallback> callback; 1.454 + if (mCallbackTarget) { 1.455 + callback = NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget); 1.456 + } else { 1.457 + callback = mCallback; 1.458 + } 1.459 + 1.460 + NS_ASSERTION(callback, "Shouldn't fail to make the callback"); 1.461 + // Null the callback first because OnInputStreamReady could 1.462 + // reenter AsyncWait 1.463 + mCallback = nullptr; 1.464 + mCallbackTarget = nullptr; 1.465 + mNotifyThreshold = 0; 1.466 + 1.467 + callback->OnInputStreamReady(this); 1.468 + } 1.469 +} 1.470 + 1.471 +// Initializes the BMP file header mBMPFileHeader to the passed in values 1.472 +void 1.473 +nsBMPEncoder::InitFileHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth, 1.474 + uint32_t aHeight) 1.475 +{ 1.476 + memset(&mBMPFileHeader, 0, sizeof(mBMPFileHeader)); 1.477 + mBMPFileHeader.signature[0] = 'B'; 1.478 + mBMPFileHeader.signature[1] = 'M'; 1.479 + 1.480 + if (aVersion == VERSION_3) { 1.481 + mBMPFileHeader.dataoffset = WIN_V3_HEADER_LENGTH; 1.482 + } else { // aVersion == 5 1.483 + mBMPFileHeader.dataoffset = WIN_V5_HEADER_LENGTH; 1.484 + } 1.485 + 1.486 + // The color table is present only if BPP is <= 8 1.487 + if (aBPP <= 8) { 1.488 + uint32_t numColors = 1 << aBPP; 1.489 + mBMPFileHeader.dataoffset += 4 * numColors; 1.490 + mBMPFileHeader.filesize = mBMPFileHeader.dataoffset + aWidth * aHeight; 1.491 + } else { 1.492 + mBMPFileHeader.filesize = mBMPFileHeader.dataoffset + (aWidth * 1.493 + BytesPerPixel(aBPP) + PaddingBytes(aBPP, aWidth)) * 1.494 + aHeight; 1.495 + } 1.496 + 1.497 + mBMPFileHeader.reserved = 0; 1.498 + 1.499 + if (aVersion == VERSION_3) { 1.500 + mBMPFileHeader.bihsize = WIN_V3_BIH_LENGTH; 1.501 + } else { // aVersion == VERSION_5 1.502 + mBMPFileHeader.bihsize = WIN_V5_BIH_LENGTH; 1.503 + } 1.504 +} 1.505 + 1.506 +#define ENCODE(pImageBufferCurr, value) \ 1.507 + memcpy(*pImageBufferCurr, &value, sizeof value); \ 1.508 + *pImageBufferCurr += sizeof value; 1.509 + 1.510 +// Initializes the bitmap info header mBMPInfoHeader to the passed in values 1.511 +void 1.512 +nsBMPEncoder::InitInfoHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth, 1.513 + uint32_t aHeight) 1.514 +{ 1.515 + memset(&mBMPInfoHeader, 0, sizeof(mBMPInfoHeader)); 1.516 + mBMPInfoHeader.width = aWidth; 1.517 + mBMPInfoHeader.height = aHeight; 1.518 + mBMPInfoHeader.planes = 1; 1.519 + mBMPInfoHeader.bpp = aBPP; 1.520 + mBMPInfoHeader.compression = 0; 1.521 + mBMPInfoHeader.colors = 0; 1.522 + mBMPInfoHeader.important_colors = 0; 1.523 + if (aBPP <= 8) { 1.524 + mBMPInfoHeader.image_size = aWidth * aHeight; 1.525 + } else { 1.526 + mBMPInfoHeader.image_size = (aWidth * BytesPerPixel(aBPP) + 1.527 + PaddingBytes(aBPP, aWidth)) * aHeight; 1.528 + } 1.529 + mBMPInfoHeader.xppm = 0; 1.530 + mBMPInfoHeader.yppm = 0; 1.531 + if (aVersion >= VERSION_5) { 1.532 + mBMPInfoHeader.red_mask = 0x000000FF; 1.533 + mBMPInfoHeader.green_mask = 0x0000FF00; 1.534 + mBMPInfoHeader.blue_mask = 0x00FF0000; 1.535 + mBMPInfoHeader.alpha_mask = 0xFF000000; 1.536 + mBMPInfoHeader.color_space = LCS_sRGB; 1.537 + mBMPInfoHeader.white_point.r.x = 0; 1.538 + mBMPInfoHeader.white_point.r.y = 0; 1.539 + mBMPInfoHeader.white_point.r.z = 0; 1.540 + mBMPInfoHeader.white_point.g.x = 0; 1.541 + mBMPInfoHeader.white_point.g.y = 0; 1.542 + mBMPInfoHeader.white_point.g.z = 0; 1.543 + mBMPInfoHeader.white_point.b.x = 0; 1.544 + mBMPInfoHeader.white_point.b.y = 0; 1.545 + mBMPInfoHeader.white_point.b.z = 0; 1.546 + mBMPInfoHeader.gamma_red = 0; 1.547 + mBMPInfoHeader.gamma_green = 0; 1.548 + mBMPInfoHeader.gamma_blue = 0; 1.549 + mBMPInfoHeader.intent = 0; 1.550 + mBMPInfoHeader.profile_offset = 0; 1.551 + mBMPInfoHeader.profile_size = 0; 1.552 + mBMPInfoHeader.reserved = 0; 1.553 + } 1.554 +} 1.555 + 1.556 +// Encodes the BMP file header mBMPFileHeader 1.557 +void 1.558 +nsBMPEncoder::EncodeFileHeader() 1.559 +{ 1.560 + mozilla::image::BMPFILEHEADER littleEndianBFH = mBMPFileHeader; 1.561 + NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.filesize, 1); 1.562 + NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.reserved, 1); 1.563 + NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.dataoffset, 1); 1.564 + NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.bihsize, 1); 1.565 + 1.566 + ENCODE(&mImageBufferCurr, littleEndianBFH.signature); 1.567 + ENCODE(&mImageBufferCurr, littleEndianBFH.filesize); 1.568 + ENCODE(&mImageBufferCurr, littleEndianBFH.reserved); 1.569 + ENCODE(&mImageBufferCurr, littleEndianBFH.dataoffset); 1.570 + ENCODE(&mImageBufferCurr, littleEndianBFH.bihsize); 1.571 +} 1.572 + 1.573 +// Encodes the BMP infor header mBMPInfoHeader 1.574 +void 1.575 +nsBMPEncoder::EncodeInfoHeader() 1.576 +{ 1.577 + mozilla::image::BITMAPV5HEADER littleEndianmBIH = mBMPInfoHeader; 1.578 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.width, 1); 1.579 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.height, 1); 1.580 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.planes, 1); 1.581 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.bpp, 1); 1.582 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.compression, 1); 1.583 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.image_size, 1); 1.584 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.xppm, 1); 1.585 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.yppm, 1); 1.586 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.colors, 1); 1.587 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.important_colors, 1); 1.588 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.red_mask, 1); 1.589 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.green_mask, 1); 1.590 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.blue_mask, 1); 1.591 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.alpha_mask, 1); 1.592 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.color_space, 1); 1.593 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.x, 1); 1.594 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.y, 1); 1.595 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.z, 1); 1.596 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.x, 1); 1.597 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.y, 1); 1.598 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.z, 1); 1.599 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.x, 1); 1.600 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.y, 1); 1.601 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.z, 1); 1.602 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_red, 1); 1.603 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_green, 1); 1.604 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_blue, 1); 1.605 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.intent, 1); 1.606 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_offset, 1); 1.607 + NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_size, 1); 1.608 + 1.609 + if (mBMPFileHeader.bihsize == OS2_BIH_LENGTH) { 1.610 + uint16_t width = (uint16_t) littleEndianmBIH.width; 1.611 + ENCODE(&mImageBufferCurr, width); 1.612 + uint16_t height = (uint16_t) littleEndianmBIH.width; 1.613 + ENCODE(&mImageBufferCurr, height); 1.614 + } else { 1.615 + ENCODE(&mImageBufferCurr, littleEndianmBIH.width); 1.616 + ENCODE(&mImageBufferCurr, littleEndianmBIH.height); 1.617 + } 1.618 + 1.619 + ENCODE(&mImageBufferCurr, littleEndianmBIH.planes); 1.620 + ENCODE(&mImageBufferCurr, littleEndianmBIH.bpp); 1.621 + 1.622 + if (mBMPFileHeader.bihsize > OS2_BIH_LENGTH) { 1.623 + ENCODE(&mImageBufferCurr, littleEndianmBIH.compression); 1.624 + ENCODE(&mImageBufferCurr, littleEndianmBIH.image_size); 1.625 + ENCODE(&mImageBufferCurr, littleEndianmBIH.xppm); 1.626 + ENCODE(&mImageBufferCurr, littleEndianmBIH.yppm); 1.627 + ENCODE(&mImageBufferCurr, littleEndianmBIH.colors); 1.628 + ENCODE(&mImageBufferCurr, littleEndianmBIH.important_colors); 1.629 + } 1.630 + 1.631 + if (mBMPFileHeader.bihsize > WIN_V3_BIH_LENGTH) { 1.632 + ENCODE(&mImageBufferCurr, littleEndianmBIH.red_mask); 1.633 + ENCODE(&mImageBufferCurr, littleEndianmBIH.green_mask); 1.634 + ENCODE(&mImageBufferCurr, littleEndianmBIH.blue_mask); 1.635 + ENCODE(&mImageBufferCurr, littleEndianmBIH.alpha_mask); 1.636 + ENCODE(&mImageBufferCurr, littleEndianmBIH.color_space); 1.637 + ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.x); 1.638 + ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.y); 1.639 + ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.z); 1.640 + ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.x); 1.641 + ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.y); 1.642 + ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.z); 1.643 + ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.x); 1.644 + ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.y); 1.645 + ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.z); 1.646 + ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_red); 1.647 + ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_green); 1.648 + ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_blue); 1.649 + ENCODE(&mImageBufferCurr, littleEndianmBIH.intent); 1.650 + ENCODE(&mImageBufferCurr, littleEndianmBIH.profile_offset); 1.651 + ENCODE(&mImageBufferCurr, littleEndianmBIH.profile_size); 1.652 + ENCODE(&mImageBufferCurr, littleEndianmBIH.reserved); 1.653 + } 1.654 +} 1.655 + 1.656 +// Sets a pixel in the image buffer that doesn't have alpha data 1.657 +static inline void 1.658 + SetPixel24(uint8_t*& imageBufferCurr, uint8_t aRed, uint8_t aGreen, 1.659 + uint8_t aBlue) 1.660 +{ 1.661 + *imageBufferCurr = aBlue; 1.662 + *(imageBufferCurr + 1) = aGreen; 1.663 + *(imageBufferCurr + 2) = aRed; 1.664 +} 1.665 + 1.666 +// Sets a pixel in the image buffer with alpha data 1.667 +static inline void 1.668 +SetPixel32(uint8_t*& imageBufferCurr, uint8_t aRed, uint8_t aGreen, 1.669 + uint8_t aBlue, uint8_t aAlpha = 0xFF) 1.670 +{ 1.671 + *imageBufferCurr = aBlue; 1.672 + *(imageBufferCurr + 1) = aGreen; 1.673 + *(imageBufferCurr + 2) = aRed; 1.674 + *(imageBufferCurr + 3) = aAlpha; 1.675 +} 1.676 + 1.677 +// Encodes a row of image data which does not have alpha data 1.678 +void 1.679 +nsBMPEncoder::EncodeImageDataRow24(const uint8_t* aData) 1.680 +{ 1.681 + for (int32_t x = 0; x < mBMPInfoHeader.width; x++) { 1.682 + uint32_t pos = x * BytesPerPixel(mBMPInfoHeader.bpp); 1.683 + SetPixel24(mImageBufferCurr, aData[pos], aData[pos + 1], aData[pos + 2]); 1.684 + mImageBufferCurr += BytesPerPixel(mBMPInfoHeader.bpp); 1.685 + } 1.686 + 1.687 + for (uint32_t x = 0; x < PaddingBytes(mBMPInfoHeader.bpp, 1.688 + mBMPInfoHeader.width); x++) { 1.689 + *mImageBufferCurr++ = 0; 1.690 + } 1.691 +} 1.692 + 1.693 +// Encodes a row of image data which does have alpha data 1.694 +void 1.695 +nsBMPEncoder::EncodeImageDataRow32(const uint8_t* aData) 1.696 +{ 1.697 + for (int32_t x = 0; x < mBMPInfoHeader.width; x ++) { 1.698 + uint32_t pos = x * BytesPerPixel(mBMPInfoHeader.bpp); 1.699 + SetPixel32(mImageBufferCurr, aData[pos], aData[pos + 1], 1.700 + aData[pos + 2], aData[pos + 3]); 1.701 + mImageBufferCurr += 4; 1.702 + } 1.703 + 1.704 + for (uint32_t x = 0; x < PaddingBytes(mBMPInfoHeader.bpp, 1.705 + mBMPInfoHeader.width); x ++) { 1.706 + *mImageBufferCurr++ = 0; 1.707 + } 1.708 +}