123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- /**
- * @file LLImageBMP.cpp
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include "llimagebmp.h"
- #include "llendianswizzle.h"
- /**
- * @struct LLBMPHeader
- *
- * This struct helps deal with bmp files.
- */
- struct LLBMPHeader
- {
- S32 mSize;
- S32 mWidth;
- S32 mHeight;
- S16 mPlanes;
- S16 mBitsPerPixel;
- S16 mCompression;
- S16 mAlignmentPadding; // pads out to next word boundary
- S32 mImageSize;
- S32 mHorzPelsPerMeter;
- S32 mVertPelsPerMeter;
- S32 mNumColors;
- S32 mNumColorsImportant;
- };
- /**
- * @struct Win95BmpHeaderExtension
- */
- struct Win95BmpHeaderExtension
- {
- U32 mReadMask;
- U32 mGreenMask;
- U32 mBlueMask;
- U32 mAlphaMask;
- U32 mColorSpaceType;
- U16 mRed[3]; // Red CIE endpoint
- U16 mGreen[3]; // Green CIE endpoint
- U16 mBlue[3]; // Blue CIE endpoint
- U32 mGamma[3]; // Gamma scale for r g and b
- };
- /**
- * LLImageBMP
- */
- LLImageBMP::LLImageBMP()
- : LLImageFormatted(IMG_CODEC_BMP),
- mColorPaletteColors(0),
- mColorPalette(NULL),
- mBitmapOffset(0),
- mBitsPerPixel(0),
- mOriginAtTop(false)
- {
- mBitfieldMask[0] = 0;
- mBitfieldMask[1] = 0;
- mBitfieldMask[2] = 0;
- mBitfieldMask[3] = 0;
- }
- LLImageBMP::~LLImageBMP()
- {
- if (mColorPalette)
- {
- delete[] mColorPalette;
- mColorPalette = NULL;
- }
- }
- bool LLImageBMP::updateData()
- {
- resetLastError();
- // Check to make sure that this instance has been initialized with data
- U8* mdata = getData();
- if (!mdata || getDataSize() == 0)
- {
- setLastError("Uninitialized instance of LLImageBMP");
- return false;
- }
- // Read the bitmap headers in order to get all the useful info
- // about this image
- ////////////////////////////////////////////////////////////////////
- // Part 1: "File Header"
- // 14 bytes consisting of
- // 2 bytes: either BM or BA
- // 4 bytes: file size in bytes
- // 4 bytes: reserved (always 0)
- // 4 bytes: bitmap offset (starting position of image data in bytes)
- constexpr S32 FILE_HEADER_SIZE = 14;
- if (mdata[0] != 'B' || mdata[1] != 'M')
- {
- if (mdata[0] != 'B' || mdata[1] != 'A')
- {
- setLastError("OS/2 bitmap array BMP files are not supported");
- return false;
- }
- else
- {
- setLastError("Does not appear to be a bitmap file");
- return false;
- }
- }
- mBitmapOffset = mdata[13];
- mBitmapOffset <<= 8; mBitmapOffset += mdata[12];
- mBitmapOffset <<= 8; mBitmapOffset += mdata[11];
- mBitmapOffset <<= 8; mBitmapOffset += mdata[10];
- ////////////////////////////////////////////////////////////////////
- // Part 2: "Bitmap Header"
- constexpr S32 BITMAP_HEADER_SIZE = 40;
- LLBMPHeader header;
- llassert(sizeof(header) == BITMAP_HEADER_SIZE);
- memcpy((void*)&header, mdata + FILE_HEADER_SIZE, BITMAP_HEADER_SIZE);
- // Convert BMP header from little endian (no-op on little endian builds)
- llendianswizzleone(header.mSize);
- llendianswizzleone(header.mWidth);
- llendianswizzleone(header.mHeight);
- llendianswizzleone(header.mPlanes);
- llendianswizzleone(header.mBitsPerPixel);
- llendianswizzleone(header.mCompression);
- llendianswizzleone(header.mAlignmentPadding);
- llendianswizzleone(header.mImageSize);
- llendianswizzleone(header.mHorzPelsPerMeter);
- llendianswizzleone(header.mVertPelsPerMeter);
- llendianswizzleone(header.mNumColors);
- llendianswizzleone(header.mNumColorsImportant);
- bool windows_nt_version = false;
- bool windows_95_version = false;
- if (12 == header.mSize)
- {
- setLastError("Windows 2.x and OS/2 1.x BMP files are not supported");
- return false;
- }
- else if (40 == header.mSize)
- {
- if (3 == header.mCompression)
- {
- // Windows NT
- windows_nt_version = true;
- }
- }
- else if (12 <= header.mSize && header.mSize <= 64)
- {
- setLastError("OS/2 2.x BMP files are not supported");
- return false;
- }
- else if (108 == header.mSize)
- {
- // BITMAPV4HEADER
- windows_95_version = true;
- }
- else if (108 < header.mSize)
- {
- // BITMAPV5HEADER or greater
- // Should work as long at Microsoft maintained backwards compatibility
- // (which they did in V4 and V5)
- windows_95_version = true;
- }
- S32 width = header.mWidth;
- S32 height = header.mHeight;
- if (height < 0)
- {
- mOriginAtTop = true;
- height = -height;
- }
- else
- {
- mOriginAtTop = false;
- }
- mBitsPerPixel = header.mBitsPerPixel;
- S32 components;
- switch (mBitsPerPixel)
- {
- case 8:
- components = 1;
- break;
- case 24:
- case 32:
- components = 3;
- break;
- case 1:
- case 4:
- case 16: // Started work on 16, but doesn't work yet
- // These are legal, but we don't support them yet.
- setLastError("Unsupported bit depth");
- return false;
- default:
- setLastError("Unrecognized bit depth");
- return false;
- }
- setSize(width, height, components);
- switch (header.mCompression)
- {
- case 0:
- // Uncompressed
- break;
- case 1:
- setLastError("8 bit RLE compression not supported.");
- return false;
- case 2:
- setLastError("4 bit RLE compression not supported.");
- return false;
- case 3:
- // Windows NT or Windows 95
- break;
- default:
- setLastError("Unsupported compression format.");
- return false;
- }
- ////////////////////////////////////////////////////////////////////
- // Part 3: Bitfield Masks and other color data
- S32 extension_size = 0;
- if (windows_nt_version)
- {
- if (header.mBitsPerPixel != 16 && header.mBitsPerPixel != 32)
- {
- setLastError("Bitfield encoding requires 16 or 32 bits per pixel.");
- return false;
- }
- if (header.mNumColors != 0)
- {
- setLastError("Bitfield encoding is not compatible with a color table.");
- return false;
- }
- extension_size = 4 * 3;
- memcpy(mBitfieldMask, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE,
- extension_size);
- }
- else if (windows_95_version)
- {
- Win95BmpHeaderExtension win_95_extension;
- extension_size = sizeof(win_95_extension);
- llassert(sizeof(win_95_extension) + BITMAP_HEADER_SIZE == 108);
- memcpy(&win_95_extension,
- mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE,
- sizeof(win_95_extension));
- if (3 == header.mCompression)
- {
- memcpy(mBitfieldMask,
- mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, 4 * 4);
- }
- // Color correction ignored for now
- }
- ////////////////////////////////////////////////////////////////////
- // Part 4: Color Palette (optional)
- // Note: There's no color palette if there are 16 or more bits per pixel
- S32 color_palette_size = 0;
- mColorPaletteColors = 0;
- if (header.mBitsPerPixel < 16)
- {
- if (header.mNumColors == 0)
- {
- mColorPaletteColors = 1 << header.mBitsPerPixel;
- }
- else
- {
- mColorPaletteColors = header.mNumColors;
- }
- }
- color_palette_size = mColorPaletteColors * 4;
- if (mColorPaletteColors > 0)
- {
- mColorPalette = new (std::nothrow) U8[color_palette_size];
- if (!mColorPalette)
- {
- LLMemory::allocationFailed(color_palette_size);
- return false;
- }
- memcpy(mColorPalette,
- mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE + extension_size,
- color_palette_size);
- }
- return true;
- }
- bool LLImageBMP::decode(LLImageRaw* raw_image)
- {
- if (!raw_image)
- {
- llwarns << "Attempted to decode a NULL raw image" << llendl;
- llassert(false);
- return false;
- }
- resetLastError();
- // Check to make sure that this instance has been initialized with data
- U8* mdata = getData();
- if (!mdata || getDataSize() == 0)
- {
- setLastError("LLImageBMP trying to decode an image with no data");
- return false;
- }
- if (!raw_image->resize(getWidth(), getHeight(), 3))
- {
- setLastError("LLImageBMP failed to resize image");
- return false;
- }
- U8* src = mdata + mBitmapOffset;
- U8* dst = raw_image->getData();
- bool success = false;
- switch (mBitsPerPixel)
- {
- case 8:
- if (mColorPaletteColors >= 256)
- {
- success = decodeColorTable8(dst, src);
- }
- break;
- case 16:
- success = decodeColorMask16(dst, src);
- break;
- case 24:
- success = decodeTruecolor24(dst, src);
- break;
- case 32:
- success = decodeColorMask32(dst, src);
- break;
- }
- if (success && mOriginAtTop)
- {
- raw_image->verticalFlip();
- }
- return success;
- }
- U32 LLImageBMP::countTrailingZeros(U32 m)
- {
- U32 shift_count = 0;
- while (!(m & 1))
- {
- ++shift_count;
- m >>= 1;
- }
- return shift_count;
- }
- bool LLImageBMP::decodeColorMask16(U8* dst, U8* src)
- {
- if (mBitsPerPixel != 16)
- {
- llassert(false);
- return false;
- }
- if (!mBitfieldMask[0] && !mBitfieldMask[1] && !mBitfieldMask[2])
- {
- // Use default values
- mBitfieldMask[0] = 0x00007C00;
- mBitfieldMask[1] = 0x000003E0;
- mBitfieldMask[2] = 0x0000001F;
- }
- S32 src_row_span = getWidth() * 2;
- // Round up to nearest multiple of 4:
- S32 alignment_bytes = (3 * src_row_span) % 4;
- U32 r_shift = countTrailingZeros(mBitfieldMask[2]);
- U32 g_shift = countTrailingZeros(mBitfieldMask[1]);
- U32 b_shift = countTrailingZeros(mBitfieldMask[0]);
- for (S32 row = 0, height = getHeight(); row < height; ++row)
- {
- for (S32 col = 0, width = getWidth(); col < width; ++col)
- {
- U32 value = *((U16*)src);
- dst[0] = U8((value & mBitfieldMask[2]) >> r_shift); // Red
- dst[1] = U8((value & mBitfieldMask[1]) >> g_shift); // Green
- dst[2] = U8((value & mBitfieldMask[0]) >> b_shift); // Blue
- src += 2;
- dst += 3;
- }
- src += alignment_bytes;
- }
- return true;
- }
- bool LLImageBMP::decodeColorMask32(U8* dst, U8* src)
- {
- if (mBitsPerPixel != 32)
- {
- // Alpha is not supported
- llassert(false);
- return false;
- }
- if (!mBitfieldMask[0] && !mBitfieldMask[1] && !mBitfieldMask[2])
- {
- // Use default values
- mBitfieldMask[0] = 0x00FF0000;
- mBitfieldMask[1] = 0x0000FF00;
- mBitfieldMask[2] = 0x000000FF;
- }
- if (4 * getWidth() * getHeight() > getDataSize() - mBitmapOffset)
- {
- // here we have situation when data size in src less than actually
- // needed
- return false;
- }
- S32 src_row_span = getWidth() * 4;
- // Round up to nearest multiple of 4:
- S32 alignment_bytes = (3 * src_row_span) % 4;
- U32 r_shift = countTrailingZeros(mBitfieldMask[0]);
- U32 g_shift = countTrailingZeros(mBitfieldMask[1]);
- U32 b_shift = countTrailingZeros(mBitfieldMask[2]);
- for (S32 row = 0, height = getHeight(); row < height; ++row)
- {
- for (S32 col = 0, width = getWidth(); col < width; ++col)
- {
- U32 value = *((U32*)src);
- dst[0] = U8((value & mBitfieldMask[0]) >> r_shift); // Red
- dst[1] = U8((value & mBitfieldMask[1]) >> g_shift); // Green
- dst[2] = U8((value & mBitfieldMask[2]) >> b_shift); // Blue
- src += 4;
- dst += 3;
- }
- src += alignment_bytes;
- }
- return true;
- }
- bool LLImageBMP::decodeColorTable8(U8* dst, U8* src)
- {
- if (mBitsPerPixel != 8 || mColorPaletteColors < 256)
- {
- llassert(false);
- return false;
- }
- S32 src_row_span = getWidth() * 1;
- // Round up to nearest multiple of 4:
- S32 alignment_bytes = (3 * src_row_span) % 4;
- if (getWidth() * getHeight() + getHeight() * alignment_bytes >
- getDataSize() - mBitmapOffset)
- {
- // here we have situation when data size in src less than actually
- // needed
- return false;
- }
- for (S32 row = 0, height = getHeight(); row < height; ++row)
- {
- for (S32 col = 0, width = getWidth(); col < width; ++col)
- {
- S32 index = 4 * src[0];
- dst[0] = mColorPalette[index + 2]; // Red
- dst[1] = mColorPalette[index + 1]; // Green
- dst[2] = mColorPalette[index + 0]; // Blue
- src++;
- dst += 3;
- }
- src += alignment_bytes;
- }
- return true;
- }
- bool LLImageBMP::decodeTruecolor24(U8* dst, U8* src)
- {
- if (mBitsPerPixel != 24 || getComponents() != 3)
- {
- llassert(false);
- return false;
- }
- S32 src_row_span = getWidth() * 3;
- // Round up to nearest multiple of 4:
- S32 alignment_bytes = (3 * src_row_span) % 4;
- if (3 * getWidth() * getHeight() + getHeight() * alignment_bytes >
- getDataSize() - mBitmapOffset)
- {
- // here we have situation when data size in src less than actually
- // needed
- return false;
- }
- for (S32 row = 0, height = getHeight(); row < height; ++row)
- {
- for (S32 col = 0, width = getWidth(); col < width; ++col)
- {
- dst[0] = src[2]; // Red
- dst[1] = src[1]; // Green
- dst[2] = src[0]; // Blue
- src += 3;
- dst += 3;
- }
- src += alignment_bytes;
- }
- return true;
- }
- bool LLImageBMP::encode(const LLImageRaw* raw_image)
- {
- if (!raw_image)
- {
- llwarns << "Attempted to decode a NULL raw image" << llendl;
- llassert(false);
- return false;
- }
- resetLastError();
- S32 src_components = raw_image->getComponents();
- S32 dst_components = (src_components < 3) ? 1 : 3;
- if (src_components == 2 || src_components == 4)
- {
- llinfos << "Dropping alpha information during BMP encoding" << llendl;
- }
- setSize(raw_image->getWidth(), raw_image->getHeight(), dst_components);
- U8 magic[14];
- LLBMPHeader header;
- int header_bytes = 14 + sizeof(header);
- llassert(header_bytes == 54);
- if (getComponents() == 1)
- {
- header_bytes += 1024; // Need colour LUT.
- }
- int line_bytes = getComponents() * getWidth();
- int alignment_bytes = (3 * line_bytes) % 4;
- line_bytes += alignment_bytes;
- int file_bytes = line_bytes*getHeight() + header_bytes;
- // Allocate the new buffer for the data.
- if (!allocateData(file_bytes)) // memory allocation failed
- {
- return false;
- }
- magic[0] = 'B'; magic[1] = 'M';
- magic[2] = (U8) file_bytes;
- magic[3] = (U8)(file_bytes>>8);
- magic[4] = (U8)(file_bytes>>16);
- magic[5] = (U8)(file_bytes>>24);
- magic[6] = magic[7] = magic[8] = magic[9] = 0;
- magic[10] = (U8) header_bytes;
- magic[11] = (U8)(header_bytes>>8);
- magic[12] = (U8)(header_bytes>>16);
- magic[13] = (U8)(header_bytes>>24);
- header.mSize = 40;
- header.mWidth = getWidth();
- header.mHeight = getHeight();
- header.mPlanes = 1;
- header.mBitsPerPixel = (getComponents()==1)?8:24;
- header.mCompression = 0;
- header.mAlignmentPadding = 0;
- header.mImageSize = 0;
- #if LL_DARWIN
- header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 2834; // 72dpi
- #else
- header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 0;
- #endif
- header.mNumColors = header.mNumColorsImportant = 0;
- // convert BMP header to little endian (no-op on little endian builds)
- llendianswizzleone(header.mSize);
- llendianswizzleone(header.mWidth);
- llendianswizzleone(header.mHeight);
- llendianswizzleone(header.mPlanes);
- llendianswizzleone(header.mBitsPerPixel);
- llendianswizzleone(header.mCompression);
- llendianswizzleone(header.mAlignmentPadding);
- llendianswizzleone(header.mImageSize);
- llendianswizzleone(header.mHorzPelsPerMeter);
- llendianswizzleone(header.mVertPelsPerMeter);
- llendianswizzleone(header.mNumColors);
- llendianswizzleone(header.mNumColorsImportant);
- U8* mdata = getData();
- // Output magic, then header, then the palette table, then the data.
- U32 cur_pos = 0;
- memcpy(mdata, magic, 14);
- cur_pos += 14;
- memcpy(mdata + cur_pos, &header, 40);
- cur_pos += 40;
- if (getComponents() == 1)
- {
- for (S32 n = 0; n < 256; ++n)
- {
- mdata[cur_pos++] = (U8)n;
- mdata[cur_pos++] = (U8)n;
- mdata[cur_pos++] = (U8)n;
- mdata[cur_pos++] = 0;
- }
- }
- // Need to iterate through, because we need to flip the RGB.
- const U8* src = raw_image->getData();
- U8* dst = mdata + cur_pos;
- for (S32 row = 0, height = getHeight(); row < height; ++row)
- {
- for (S32 col = 0, width = getWidth(); col < width; ++col)
- {
- switch (src_components)
- {
- case 1:
- {
- *dst++ = *src++;
- break;
- }
- case 2:
- {
- U32 lum = src[0];
- U32 alpha = src[1];
- *dst++ = (U8)(lum * alpha / 255);
- src += 2;
- break;
- }
- case 3:
- case 4:
- {
- dst[0] = src[2];
- dst[1] = src[1];
- dst[2] = src[0];
- src += src_components;
- dst += 3;
- break;
- }
- }
- }
- for (S32 i = 0; i < alignment_bytes; ++i)
- {
- *dst++ = 0;
- }
- }
- return true;
- }
|