michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsCRT.h" michael@0: #include "mozilla/Endian.h" michael@0: #include "nsBMPEncoder.h" michael@0: #include "prprf.h" michael@0: #include "nsString.h" michael@0: #include "nsStreamUtils.h" michael@0: #include "nsTArray.h" michael@0: #include "nsAutoPtr.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsBMPEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream) michael@0: michael@0: nsBMPEncoder::nsBMPEncoder() : mImageBufferStart(nullptr), michael@0: mImageBufferCurr(0), michael@0: mImageBufferSize(0), michael@0: mImageBufferReadPoint(0), michael@0: mFinished(false), michael@0: mCallback(nullptr), michael@0: mCallbackTarget(nullptr), michael@0: mNotifyThreshold(0) michael@0: { michael@0: } michael@0: michael@0: nsBMPEncoder::~nsBMPEncoder() michael@0: { michael@0: if (mImageBufferStart) { michael@0: moz_free(mImageBufferStart); michael@0: mImageBufferStart = nullptr; michael@0: mImageBufferCurr = nullptr; michael@0: } michael@0: } michael@0: michael@0: // nsBMPEncoder::InitFromData michael@0: // michael@0: // One output option is supported: bpp= michael@0: // bpp specifies the bits per pixel to use where bpp_value can be 24 or 32 michael@0: NS_IMETHODIMP nsBMPEncoder::InitFromData(const uint8_t* aData, michael@0: uint32_t aLength, // (unused, michael@0: // req'd by JS) michael@0: uint32_t aWidth, michael@0: uint32_t aHeight, michael@0: uint32_t aStride, michael@0: uint32_t aInputFormat, michael@0: const nsAString& aOutputOptions) michael@0: { michael@0: // validate input format michael@0: if (aInputFormat != INPUT_FORMAT_RGB && michael@0: aInputFormat != INPUT_FORMAT_RGBA && michael@0: aInputFormat != INPUT_FORMAT_HOSTARGB) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // Stride is the padded width of each row, so it better be longer michael@0: if ((aInputFormat == INPUT_FORMAT_RGB && michael@0: aStride < aWidth * 3) || michael@0: ((aInputFormat == INPUT_FORMAT_RGBA || aInputFormat == INPUT_FORMAT_HOSTARGB) && michael@0: aStride < aWidth * 4)) { michael@0: NS_WARNING("Invalid stride for InitFromData"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: nsresult rv; michael@0: rv = StartImageEncode(aWidth, aHeight, aInputFormat, aOutputOptions); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: rv = AddImageFrame(aData, aLength, aWidth, aHeight, aStride, michael@0: aInputFormat, aOutputOptions); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: rv = EndImageEncode(); michael@0: return rv; michael@0: } michael@0: michael@0: // Just a helper method to make it explicit in calculations that we are dealing michael@0: // with bytes and not bits michael@0: static inline uint32_t michael@0: BytesPerPixel(uint32_t aBPP) michael@0: { michael@0: return aBPP / 8; michael@0: } michael@0: michael@0: // Calculates the number of padding bytes that are needed per row of image data michael@0: static inline uint32_t michael@0: PaddingBytes(uint32_t aBPP, uint32_t aWidth) michael@0: { michael@0: uint32_t rowSize = aWidth * BytesPerPixel(aBPP); michael@0: uint8_t paddingSize = 0; michael@0: if(rowSize % 4) { michael@0: paddingSize = (4 - (rowSize % 4)); michael@0: } michael@0: return paddingSize; michael@0: } michael@0: michael@0: // See ::InitFromData for other info. michael@0: NS_IMETHODIMP nsBMPEncoder::StartImageEncode(uint32_t aWidth, michael@0: uint32_t aHeight, michael@0: uint32_t aInputFormat, michael@0: const nsAString& aOutputOptions) michael@0: { michael@0: // can't initialize more than once michael@0: if (mImageBufferStart || mImageBufferCurr) { michael@0: return NS_ERROR_ALREADY_INITIALIZED; michael@0: } michael@0: michael@0: // validate input format michael@0: if (aInputFormat != INPUT_FORMAT_RGB && michael@0: aInputFormat != INPUT_FORMAT_RGBA && michael@0: aInputFormat != INPUT_FORMAT_HOSTARGB) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // parse and check any provided output options michael@0: Version version; michael@0: uint32_t bpp; michael@0: nsresult rv = ParseOptions(aOutputOptions, &version, &bpp); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: InitFileHeader(version, bpp, aWidth, aHeight); michael@0: InitInfoHeader(version, bpp, aWidth, aHeight); michael@0: michael@0: mImageBufferSize = mBMPFileHeader.filesize; michael@0: mImageBufferStart = static_cast(moz_malloc(mImageBufferSize)); michael@0: if (!mImageBufferStart) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: mImageBufferCurr = mImageBufferStart; michael@0: michael@0: EncodeFileHeader(); michael@0: EncodeInfoHeader(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Returns the number of bytes in the image buffer used. michael@0: // For a BMP file, this is all bytes in the buffer. michael@0: NS_IMETHODIMP nsBMPEncoder::GetImageBufferUsed(uint32_t *aOutputSize) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOutputSize); michael@0: *aOutputSize = mImageBufferSize; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Returns a pointer to the start of the image buffer michael@0: NS_IMETHODIMP nsBMPEncoder::GetImageBuffer(char **aOutputBuffer) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOutputBuffer); michael@0: *aOutputBuffer = reinterpret_cast(mImageBufferStart); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsBMPEncoder::AddImageFrame(const uint8_t* aData, michael@0: uint32_t aLength, // (unused, michael@0: // req'd by JS) michael@0: uint32_t aWidth, michael@0: uint32_t aHeight, michael@0: uint32_t aStride, michael@0: uint32_t aInputFormat, michael@0: const nsAString& aFrameOptions) michael@0: { michael@0: // must be initialized michael@0: if (!mImageBufferStart || !mImageBufferCurr) { michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: // validate input format michael@0: if (aInputFormat != INPUT_FORMAT_RGB && michael@0: aInputFormat != INPUT_FORMAT_RGBA && michael@0: aInputFormat != INPUT_FORMAT_HOSTARGB) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: static fallible_t fallible = fallible_t(); michael@0: nsAutoArrayPtr row(new (fallible) michael@0: uint8_t[mBMPInfoHeader.width * michael@0: BytesPerPixel(mBMPInfoHeader.bpp)]); michael@0: if (!row) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: // write each row: if we add more input formats, we may want to michael@0: // generalize the conversions michael@0: if (aInputFormat == INPUT_FORMAT_HOSTARGB) { michael@0: // BMP requires RGBA with post-multiplied alpha, so we need to convert michael@0: for (int32_t y = mBMPInfoHeader.height - 1; y >= 0 ; y --) { michael@0: ConvertHostARGBRow(&aData[y * aStride], row, mBMPInfoHeader.width); michael@0: if(mBMPInfoHeader.bpp == 24) { michael@0: EncodeImageDataRow24(row); michael@0: } else { michael@0: EncodeImageDataRow32(row); michael@0: } michael@0: } michael@0: } else if (aInputFormat == INPUT_FORMAT_RGBA) { michael@0: // simple RGBA, no conversion needed michael@0: for (int32_t y = 0; y < mBMPInfoHeader.height; y ++) { michael@0: if (mBMPInfoHeader.bpp == 24) { michael@0: EncodeImageDataRow24(row); michael@0: } else { michael@0: EncodeImageDataRow32(row); michael@0: } michael@0: } michael@0: } else if (aInputFormat == INPUT_FORMAT_RGB) { michael@0: // simple RGB, no conversion needed michael@0: for (int32_t y = 0; y < mBMPInfoHeader.height; y ++) { michael@0: if (mBMPInfoHeader.bpp == 24) { michael@0: EncodeImageDataRow24(&aData[y * aStride]); michael@0: } else { michael@0: EncodeImageDataRow32(&aData[y * aStride]); michael@0: } michael@0: } michael@0: } else { michael@0: NS_NOTREACHED("Bad format type"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP nsBMPEncoder::EndImageEncode() michael@0: { michael@0: // must be initialized michael@0: if (!mImageBufferStart || !mImageBufferCurr) { michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: mFinished = true; michael@0: NotifyListener(); michael@0: michael@0: // if output callback can't get enough memory, it will free our buffer michael@0: if (!mImageBufferStart || !mImageBufferCurr) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // Parses the encoder options and sets the bits per pixel to use michael@0: // See InitFromData for a description of the parse options michael@0: nsresult michael@0: nsBMPEncoder::ParseOptions(const nsAString& aOptions, Version* version, michael@0: uint32_t* bpp) michael@0: { michael@0: if (version) { michael@0: *version = VERSION_3; michael@0: } michael@0: if (bpp) { michael@0: *bpp = 24; michael@0: } michael@0: michael@0: // Parse the input string into a set of name/value pairs. michael@0: // From a format like: name=value;bpp=;name=value michael@0: // to format: [0] = name=value, [1] = bpp=, [2] = name=value michael@0: nsTArray nameValuePairs; michael@0: if (!ParseString(NS_ConvertUTF16toUTF8(aOptions), ';', nameValuePairs)) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // For each name/value pair in the set michael@0: for (uint32_t i = 0; i < nameValuePairs.Length(); ++i) { michael@0: michael@0: // Split the name value pair [0] = name, [1] = value michael@0: nsTArray nameValuePair; michael@0: if (!ParseString(nameValuePairs[i], '=', nameValuePair)) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: if (nameValuePair.Length() != 2) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // Parse the bpp portion of the string name=value;version=; michael@0: // name=value michael@0: if (nameValuePair[0].Equals("version", michael@0: nsCaseInsensitiveCStringComparator())) { michael@0: if (nameValuePair[1].Equals("3")) { michael@0: *version = VERSION_3; michael@0: } else if (nameValuePair[1].Equals("5")) { michael@0: *version = VERSION_5; michael@0: } else { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: } michael@0: michael@0: // Parse the bpp portion of the string name=value;bpp=;name=value michael@0: if (nameValuePair[0].Equals("bpp", nsCaseInsensitiveCStringComparator())) { michael@0: if (nameValuePair[1].Equals("24")) { michael@0: *bpp = 24; michael@0: } else if (nameValuePair[1].Equals("32")) { michael@0: *bpp = 32; michael@0: } else { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsBMPEncoder::Close() michael@0: { michael@0: if (mImageBufferStart) { michael@0: moz_free(mImageBufferStart); michael@0: mImageBufferStart = nullptr; michael@0: mImageBufferSize = 0; michael@0: mImageBufferReadPoint = 0; michael@0: mImageBufferCurr = nullptr; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Obtains the available bytes to read michael@0: NS_IMETHODIMP nsBMPEncoder::Available(uint64_t *_retval) michael@0: { michael@0: if (!mImageBufferStart || !mImageBufferCurr) { michael@0: return NS_BASE_STREAM_CLOSED; michael@0: } michael@0: michael@0: *_retval = GetCurrentImageBufferOffset() - mImageBufferReadPoint; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // [noscript] Reads bytes which are available michael@0: NS_IMETHODIMP nsBMPEncoder::Read(char * aBuf, uint32_t aCount, michael@0: uint32_t *_retval) michael@0: { michael@0: return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval); michael@0: } michael@0: michael@0: // [noscript] Reads segments michael@0: NS_IMETHODIMP nsBMPEncoder::ReadSegments(nsWriteSegmentFun aWriter, michael@0: void *aClosure, uint32_t aCount, michael@0: uint32_t *_retval) michael@0: { michael@0: uint32_t maxCount = GetCurrentImageBufferOffset() - mImageBufferReadPoint; michael@0: if (maxCount == 0) { michael@0: *_retval = 0; michael@0: return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK; michael@0: } michael@0: michael@0: if (aCount > maxCount) { michael@0: aCount = maxCount; michael@0: } michael@0: nsresult rv = aWriter(this, aClosure, michael@0: reinterpret_cast(mImageBufferStart + michael@0: mImageBufferReadPoint), michael@0: 0, aCount, _retval); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: NS_ASSERTION(*_retval <= aCount, "bad write count"); michael@0: mImageBufferReadPoint += *_retval; michael@0: } michael@0: // errors returned from the writer end here! michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBMPEncoder::IsNonBlocking(bool *_retval) michael@0: { michael@0: *_retval = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBMPEncoder::AsyncWait(nsIInputStreamCallback *aCallback, michael@0: uint32_t aFlags, michael@0: uint32_t aRequestedCount, michael@0: nsIEventTarget *aTarget) michael@0: { michael@0: if (aFlags != 0) { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: if (mCallback || mCallbackTarget) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: mCallbackTarget = aTarget; michael@0: // 0 means "any number of bytes except 0" michael@0: mNotifyThreshold = aRequestedCount; michael@0: if (!aRequestedCount) { michael@0: mNotifyThreshold = 1024; // We don't want to notify incessantly michael@0: } michael@0: michael@0: // We set the callback absolutely last, because NotifyListener uses it to michael@0: // determine if someone needs to be notified. If we don't set it last, michael@0: // NotifyListener might try to fire off a notification to a null target michael@0: // which will generally cause non-threadsafe objects to be used off the main thread michael@0: mCallback = aCallback; michael@0: michael@0: // What we are being asked for may be present already michael@0: NotifyListener(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsBMPEncoder::CloseWithStatus(nsresult aStatus) michael@0: { michael@0: return Close(); michael@0: } michael@0: michael@0: // nsBMPEncoder::ConvertHostARGBRow michael@0: // michael@0: // Our colors are stored with premultiplied alphas, but we need michael@0: // an output with no alpha in machine-independent byte order. michael@0: // michael@0: void michael@0: nsBMPEncoder::ConvertHostARGBRow(const uint8_t* aSrc, uint8_t* aDest, michael@0: uint32_t aPixelWidth) michael@0: { michael@0: int bytes = BytesPerPixel(mBMPInfoHeader.bpp); michael@0: michael@0: if (mBMPInfoHeader.bpp == 32) { michael@0: for (uint32_t x = 0; x < aPixelWidth; x++) { michael@0: const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x]; michael@0: uint8_t *pixelOut = &aDest[x * bytes]; michael@0: michael@0: pixelOut[0] = (pixelIn & 0x00ff0000) >> 16; michael@0: pixelOut[1] = (pixelIn & 0x0000ff00) >> 8; michael@0: pixelOut[2] = (pixelIn & 0x000000ff) >> 0; michael@0: pixelOut[3] = (pixelIn & 0xff000000) >> 24; michael@0: } michael@0: } else { michael@0: for (uint32_t x = 0; x < aPixelWidth; x++) { michael@0: const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x]; michael@0: uint8_t *pixelOut = &aDest[x * bytes]; michael@0: michael@0: pixelOut[0] = (pixelIn & 0xff0000) >> 16; michael@0: pixelOut[1] = (pixelIn & 0x00ff00) >> 8; michael@0: pixelOut[2] = (pixelIn & 0x0000ff) >> 0; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsBMPEncoder::NotifyListener() michael@0: { michael@0: if (mCallback && michael@0: (GetCurrentImageBufferOffset() - mImageBufferReadPoint >= michael@0: mNotifyThreshold || mFinished)) { michael@0: nsCOMPtr callback; michael@0: if (mCallbackTarget) { michael@0: callback = NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget); michael@0: } else { michael@0: callback = mCallback; michael@0: } michael@0: michael@0: NS_ASSERTION(callback, "Shouldn't fail to make the callback"); michael@0: // Null the callback first because OnInputStreamReady could michael@0: // reenter AsyncWait michael@0: mCallback = nullptr; michael@0: mCallbackTarget = nullptr; michael@0: mNotifyThreshold = 0; michael@0: michael@0: callback->OnInputStreamReady(this); michael@0: } michael@0: } michael@0: michael@0: // Initializes the BMP file header mBMPFileHeader to the passed in values michael@0: void michael@0: nsBMPEncoder::InitFileHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth, michael@0: uint32_t aHeight) michael@0: { michael@0: memset(&mBMPFileHeader, 0, sizeof(mBMPFileHeader)); michael@0: mBMPFileHeader.signature[0] = 'B'; michael@0: mBMPFileHeader.signature[1] = 'M'; michael@0: michael@0: if (aVersion == VERSION_3) { michael@0: mBMPFileHeader.dataoffset = WIN_V3_HEADER_LENGTH; michael@0: } else { // aVersion == 5 michael@0: mBMPFileHeader.dataoffset = WIN_V5_HEADER_LENGTH; michael@0: } michael@0: michael@0: // The color table is present only if BPP is <= 8 michael@0: if (aBPP <= 8) { michael@0: uint32_t numColors = 1 << aBPP; michael@0: mBMPFileHeader.dataoffset += 4 * numColors; michael@0: mBMPFileHeader.filesize = mBMPFileHeader.dataoffset + aWidth * aHeight; michael@0: } else { michael@0: mBMPFileHeader.filesize = mBMPFileHeader.dataoffset + (aWidth * michael@0: BytesPerPixel(aBPP) + PaddingBytes(aBPP, aWidth)) * michael@0: aHeight; michael@0: } michael@0: michael@0: mBMPFileHeader.reserved = 0; michael@0: michael@0: if (aVersion == VERSION_3) { michael@0: mBMPFileHeader.bihsize = WIN_V3_BIH_LENGTH; michael@0: } else { // aVersion == VERSION_5 michael@0: mBMPFileHeader.bihsize = WIN_V5_BIH_LENGTH; michael@0: } michael@0: } michael@0: michael@0: #define ENCODE(pImageBufferCurr, value) \ michael@0: memcpy(*pImageBufferCurr, &value, sizeof value); \ michael@0: *pImageBufferCurr += sizeof value; michael@0: michael@0: // Initializes the bitmap info header mBMPInfoHeader to the passed in values michael@0: void michael@0: nsBMPEncoder::InitInfoHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth, michael@0: uint32_t aHeight) michael@0: { michael@0: memset(&mBMPInfoHeader, 0, sizeof(mBMPInfoHeader)); michael@0: mBMPInfoHeader.width = aWidth; michael@0: mBMPInfoHeader.height = aHeight; michael@0: mBMPInfoHeader.planes = 1; michael@0: mBMPInfoHeader.bpp = aBPP; michael@0: mBMPInfoHeader.compression = 0; michael@0: mBMPInfoHeader.colors = 0; michael@0: mBMPInfoHeader.important_colors = 0; michael@0: if (aBPP <= 8) { michael@0: mBMPInfoHeader.image_size = aWidth * aHeight; michael@0: } else { michael@0: mBMPInfoHeader.image_size = (aWidth * BytesPerPixel(aBPP) + michael@0: PaddingBytes(aBPP, aWidth)) * aHeight; michael@0: } michael@0: mBMPInfoHeader.xppm = 0; michael@0: mBMPInfoHeader.yppm = 0; michael@0: if (aVersion >= VERSION_5) { michael@0: mBMPInfoHeader.red_mask = 0x000000FF; michael@0: mBMPInfoHeader.green_mask = 0x0000FF00; michael@0: mBMPInfoHeader.blue_mask = 0x00FF0000; michael@0: mBMPInfoHeader.alpha_mask = 0xFF000000; michael@0: mBMPInfoHeader.color_space = LCS_sRGB; michael@0: mBMPInfoHeader.white_point.r.x = 0; michael@0: mBMPInfoHeader.white_point.r.y = 0; michael@0: mBMPInfoHeader.white_point.r.z = 0; michael@0: mBMPInfoHeader.white_point.g.x = 0; michael@0: mBMPInfoHeader.white_point.g.y = 0; michael@0: mBMPInfoHeader.white_point.g.z = 0; michael@0: mBMPInfoHeader.white_point.b.x = 0; michael@0: mBMPInfoHeader.white_point.b.y = 0; michael@0: mBMPInfoHeader.white_point.b.z = 0; michael@0: mBMPInfoHeader.gamma_red = 0; michael@0: mBMPInfoHeader.gamma_green = 0; michael@0: mBMPInfoHeader.gamma_blue = 0; michael@0: mBMPInfoHeader.intent = 0; michael@0: mBMPInfoHeader.profile_offset = 0; michael@0: mBMPInfoHeader.profile_size = 0; michael@0: mBMPInfoHeader.reserved = 0; michael@0: } michael@0: } michael@0: michael@0: // Encodes the BMP file header mBMPFileHeader michael@0: void michael@0: nsBMPEncoder::EncodeFileHeader() michael@0: { michael@0: mozilla::image::BMPFILEHEADER littleEndianBFH = mBMPFileHeader; michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.filesize, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.reserved, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.dataoffset, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.bihsize, 1); michael@0: michael@0: ENCODE(&mImageBufferCurr, littleEndianBFH.signature); michael@0: ENCODE(&mImageBufferCurr, littleEndianBFH.filesize); michael@0: ENCODE(&mImageBufferCurr, littleEndianBFH.reserved); michael@0: ENCODE(&mImageBufferCurr, littleEndianBFH.dataoffset); michael@0: ENCODE(&mImageBufferCurr, littleEndianBFH.bihsize); michael@0: } michael@0: michael@0: // Encodes the BMP infor header mBMPInfoHeader michael@0: void michael@0: nsBMPEncoder::EncodeInfoHeader() michael@0: { michael@0: mozilla::image::BITMAPV5HEADER littleEndianmBIH = mBMPInfoHeader; michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.width, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.height, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.planes, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.bpp, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.compression, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.image_size, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.xppm, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.yppm, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.colors, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.important_colors, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.red_mask, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.green_mask, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.blue_mask, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.alpha_mask, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.color_space, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.x, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.y, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.z, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.x, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.y, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.z, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.x, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.y, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.z, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_red, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_green, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_blue, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.intent, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_offset, 1); michael@0: NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_size, 1); michael@0: michael@0: if (mBMPFileHeader.bihsize == OS2_BIH_LENGTH) { michael@0: uint16_t width = (uint16_t) littleEndianmBIH.width; michael@0: ENCODE(&mImageBufferCurr, width); michael@0: uint16_t height = (uint16_t) littleEndianmBIH.width; michael@0: ENCODE(&mImageBufferCurr, height); michael@0: } else { michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.width); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.height); michael@0: } michael@0: michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.planes); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.bpp); michael@0: michael@0: if (mBMPFileHeader.bihsize > OS2_BIH_LENGTH) { michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.compression); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.image_size); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.xppm); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.yppm); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.colors); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.important_colors); michael@0: } michael@0: michael@0: if (mBMPFileHeader.bihsize > WIN_V3_BIH_LENGTH) { michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.red_mask); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.green_mask); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.blue_mask); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.alpha_mask); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.color_space); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.x); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.y); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.z); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.x); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.y); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.z); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.x); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.y); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.z); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_red); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_green); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_blue); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.intent); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.profile_offset); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.profile_size); michael@0: ENCODE(&mImageBufferCurr, littleEndianmBIH.reserved); michael@0: } michael@0: } michael@0: michael@0: // Sets a pixel in the image buffer that doesn't have alpha data michael@0: static inline void michael@0: SetPixel24(uint8_t*& imageBufferCurr, uint8_t aRed, uint8_t aGreen, michael@0: uint8_t aBlue) michael@0: { michael@0: *imageBufferCurr = aBlue; michael@0: *(imageBufferCurr + 1) = aGreen; michael@0: *(imageBufferCurr + 2) = aRed; michael@0: } michael@0: michael@0: // Sets a pixel in the image buffer with alpha data michael@0: static inline void michael@0: SetPixel32(uint8_t*& imageBufferCurr, uint8_t aRed, uint8_t aGreen, michael@0: uint8_t aBlue, uint8_t aAlpha = 0xFF) michael@0: { michael@0: *imageBufferCurr = aBlue; michael@0: *(imageBufferCurr + 1) = aGreen; michael@0: *(imageBufferCurr + 2) = aRed; michael@0: *(imageBufferCurr + 3) = aAlpha; michael@0: } michael@0: michael@0: // Encodes a row of image data which does not have alpha data michael@0: void michael@0: nsBMPEncoder::EncodeImageDataRow24(const uint8_t* aData) michael@0: { michael@0: for (int32_t x = 0; x < mBMPInfoHeader.width; x++) { michael@0: uint32_t pos = x * BytesPerPixel(mBMPInfoHeader.bpp); michael@0: SetPixel24(mImageBufferCurr, aData[pos], aData[pos + 1], aData[pos + 2]); michael@0: mImageBufferCurr += BytesPerPixel(mBMPInfoHeader.bpp); michael@0: } michael@0: michael@0: for (uint32_t x = 0; x < PaddingBytes(mBMPInfoHeader.bpp, michael@0: mBMPInfoHeader.width); x++) { michael@0: *mImageBufferCurr++ = 0; michael@0: } michael@0: } michael@0: michael@0: // Encodes a row of image data which does have alpha data michael@0: void michael@0: nsBMPEncoder::EncodeImageDataRow32(const uint8_t* aData) michael@0: { michael@0: for (int32_t x = 0; x < mBMPInfoHeader.width; x ++) { michael@0: uint32_t pos = x * BytesPerPixel(mBMPInfoHeader.bpp); michael@0: SetPixel32(mImageBufferCurr, aData[pos], aData[pos + 1], michael@0: aData[pos + 2], aData[pos + 3]); michael@0: mImageBufferCurr += 4; michael@0: } michael@0: michael@0: for (uint32_t x = 0; x < PaddingBytes(mBMPInfoHeader.bpp, michael@0: mBMPInfoHeader.width); x ++) { michael@0: *mImageBufferCurr++ = 0; michael@0: } michael@0: }