summaryrefslogtreecommitdiff
path: root/indra/llimage/llimage.cpp
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llimage/llimage.cpp
Print done when done.
Diffstat (limited to 'indra/llimage/llimage.cpp')
-rw-r--r--indra/llimage/llimage.cpp1772
1 files changed, 1772 insertions, 0 deletions
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
new file mode 100644
index 0000000000..89b4a6d1cc
--- /dev/null
+++ b/indra/llimage/llimage.cpp
@@ -0,0 +1,1772 @@
+/**
+ * @file llimage.cpp
+ * @brief Base class for images.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <algorithm>
+#include <iostream>
+
+#include "llmath.h"
+#include "stdtypes.h"
+#include "v4coloru.h"
+#include "llmemory.h"
+
+#include "llimage.h"
+#include "llimagebmp.h"
+#include "llimagetga.h"
+#include "llimagej2c.h"
+#if JPEG_SUPPORT
+#include "llimagejpeg.h"
+#endif
+#include "llimagedxt.h"
+
+//---------------------------------------------------------------------------
+// LLImageBase
+//---------------------------------------------------------------------------
+
+LLImageBase::LLImageBase()
+ : mData(NULL),
+ mDataSize(0),
+ mWidth(0),
+ mHeight(0),
+ mComponents(0),
+ mMemType(LLMemType::MTYPE_IMAGEBASE)
+{
+}
+
+// virtual
+LLImageBase::~LLImageBase()
+{
+ deleteData(); // virtual
+}
+
+// virtual
+void LLImageBase::dump()
+{
+ llinfos << "LLImageBase mComponents " << mComponents
+ << " mData " << mData
+ << " mDataSize " << mDataSize
+ << " mWidth " << mWidth
+ << " mHeight " << mHeight
+ << llendl;
+}
+
+// virtual
+void LLImageBase::sanityCheck()
+{
+ if (mWidth > MAX_IMAGE_SIZE
+ || mHeight > MAX_IMAGE_SIZE
+ || mDataSize > (S32)MAX_IMAGE_DATA_SIZE
+ || mComponents > (S8)MAX_IMAGE_COMPONENTS
+ )
+ {
+ llerrs << "Failed LLImageBase::sanityCheck "
+ << "width " << mWidth
+ << "height " << mHeight
+ << "datasize " << mDataSize
+ << "components " << mComponents
+ << "data " << mData
+ << llendl;
+ }
+}
+
+LLString LLImageBase::sLastErrorMessage;
+BOOL LLImageBase::sSizeOverride = FALSE;
+
+BOOL LLImageBase::setLastError(const LLString& message, const LLString& filename)
+{
+ sLastErrorMessage = message;
+ if (filename != "")
+ {
+ sLastErrorMessage += LLString(" FILE:");
+ sLastErrorMessage += filename;
+ }
+ llwarns << sLastErrorMessage << llendl;
+ return FALSE;
+}
+
+// virtual
+void LLImageBase::deleteData()
+{
+ delete[] mData;
+ mData = NULL;
+ mDataSize = 0;
+}
+
+// virtual
+U8* LLImageBase::allocateData(S32 size)
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+
+ if (size < 0)
+ {
+ size = mWidth * mHeight * mComponents;
+ if (size <= 0)
+ {
+ llerrs << llformat("LLImageBase::allocateData called with bad dimentions: %dx%dx%d",mWidth,mHeight,mComponents) << llendl;
+ }
+ }
+ else if ((size <= 0 || size > 4096*4096*16) && sSizeOverride == FALSE)
+ {
+ llerrs << "LLImageBase::allocateData: bad size: " << size << llendl;
+ }
+
+ resetLastError();
+
+ if (!mData || size != mDataSize)
+ {
+ deleteData(); // virtual
+ mData = new U8[size];
+ if (!mData)
+ {
+ llerrs << "allocate image data: " << size << llendl;
+ }
+ mDataSize = size;
+ }
+
+ return mData;
+}
+
+// virtual
+U8* LLImageBase::reallocateData(S32 size)
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ U8 *new_datap = new U8[size];
+ if (!new_datap)
+ {
+ llwarns << "Out of memory in LLImageBase::reallocateData" << llendl;
+ return 0;
+ }
+ if (mData)
+ {
+ S32 bytes = llmin(mDataSize, size);
+ memcpy(new_datap, mData, bytes);
+ delete[] mData;
+ }
+ mData = new_datap;
+ mDataSize = size;
+ return mData;
+}
+
+
+void LLImageBase::setSize(S32 width, S32 height, S32 ncomponents)
+{
+ mWidth = width;
+ mHeight = height;
+ mComponents = ncomponents;
+}
+
+U8* LLImageBase::allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 size)
+{
+ setSize(width, height, ncomponents);
+ return allocateData(size); // virtual
+}
+
+//---------------------------------------------------------------------------
+// LLImageRaw
+//---------------------------------------------------------------------------
+
+S32 LLImageRaw::sGlobalRawMemory = 0;
+S32 LLImageRaw::sRawImageCount = 0;
+
+LLImageRaw::LLImageRaw()
+ : LLImageBase()
+{
+ mMemType = LLMemType::MTYPE_IMAGERAW;
+ ++sRawImageCount;
+}
+
+LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components)
+ : LLImageBase()
+{
+ mMemType = LLMemType::MTYPE_IMAGERAW;
+ llassert( S32(width) * S32(height) * S32(components) <= MAX_IMAGE_DATA_SIZE );
+ allocateDataSize(width, height, components);
+ ++sRawImageCount;
+}
+
+LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components)
+ : LLImageBase()
+{
+ mMemType = LLMemType::MTYPE_IMAGERAW;
+ copyData(data, width, height, components);
+ ++sRawImageCount;
+}
+
+LLImageRaw::LLImageRaw(const LLString &filename, bool j2c_lowest_mip_only)
+ : LLImageBase()
+{
+ createFromFile(filename, j2c_lowest_mip_only);
+}
+
+LLImageRaw::~LLImageRaw()
+{
+ // NOTE: ~LLimageBase() call to deleteData() calls LLImageBase::deleteData()
+ // NOT LLImageRaw::deleteData()
+ deleteData();
+ --sRawImageCount;
+}
+
+// virtual
+U8* LLImageRaw::allocateData(S32 size)
+{
+ U8* res = LLImageBase::allocateData(size);
+ sGlobalRawMemory += getDataSize();
+ return res;
+}
+
+// virtual
+U8* LLImageRaw::reallocateData(S32 size)
+{
+ sGlobalRawMemory -= getDataSize();
+ U8* res = LLImageBase::reallocateData(size);
+ sGlobalRawMemory += getDataSize();
+ return res;
+}
+
+// virtual
+void LLImageRaw::deleteData()
+{
+ sGlobalRawMemory -= getDataSize();
+ LLImageBase::deleteData();
+}
+
+BOOL LLImageRaw::copyData(U8 *data, U16 width, U16 height, S8 components)
+{
+ if (!resize(width, height, components))
+ {
+ return FALSE;
+ }
+ memcpy(getData(), data, width*height*components);
+ return TRUE;
+}
+
+BOOL LLImageRaw::resize(U16 width, U16 height, S8 components)
+{
+ if ((getWidth() == width) && (getHeight() == height) && (getComponents() == components))
+ {
+ return TRUE;
+ }
+ // Reallocate the data buffer.
+ deleteData();
+
+ allocateDataSize(width,height,components);
+
+ return TRUE;
+}
+
+U8 * LLImageRaw::getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ U8 *data = new U8[width*height*getComponents()];
+
+ // Should do some simple bounds checking
+
+ U32 i;
+ for (i = y_pos; i < y_pos+height; i++)
+ {
+ memcpy(data + i*width*getComponents(),
+ getData() + ((y_pos + i)*getWidth() + x_pos)*getComponents(), getComponents()*width);
+ }
+ return data;
+}
+
+BOOL LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height,
+ const U8 *data, U32 stride, BOOL reverse_y)
+{
+ if (!getData())
+ {
+ return FALSE;
+ }
+ if (!data)
+ {
+ return FALSE;
+ }
+
+ // Should do some simple bounds checking
+
+ U32 i;
+ U32 to_offset;
+ U32 from_offset;
+ if (!reverse_y)
+ {
+ for (i = 0; i < height; i++)
+ {
+ to_offset = (y_pos + i)*getWidth() + x_pos;
+ if (stride != 0)
+ {
+ from_offset = i*stride;
+ }
+ else
+ {
+ from_offset = i*width*getComponents();
+ }
+ memcpy(getData() + to_offset*getComponents(),
+ data + from_offset, getComponents()*width);
+ }
+ }
+ else
+ {
+ for (i = 0; i < height; i++)
+ {
+ to_offset = (y_pos + i)*getWidth() + x_pos;
+ if (stride != 0)
+ {
+ from_offset = (height - 1 - i)*stride;
+ }
+ else
+ {
+ from_offset = (height - 1 - i)*width*getComponents();
+ }
+ memcpy(getData() + to_offset*getComponents(),
+ data + from_offset, getComponents()*width);
+ }
+ }
+ return TRUE;
+}
+
+void LLImageRaw::clear(U8 r, U8 g, U8 b, U8 a)
+{
+ llassert( getComponents() <= 4 );
+ // This is fairly bogus, but it'll do for now.
+ U8 *pos = getData();
+ U32 x, y;
+ for (x = 0; x < getWidth(); x++)
+ {
+ for (y = 0; y < getHeight(); y++)
+ {
+ *pos = r;
+ pos++;
+ if (getComponents() == 1)
+ {
+ continue;
+ }
+ *pos = g;
+ pos++;
+ if (getComponents() == 2)
+ {
+ continue;
+ }
+ *pos = b;
+ pos++;
+ if (getComponents() == 3)
+ {
+ continue;
+ }
+ *pos = a;
+ pos++;
+ }
+ }
+}
+
+// Reverses the order of the rows in the image
+void LLImageRaw::verticalFlip()
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ S32 row_bytes = getWidth() * getComponents();
+ U8* line_buffer = new U8[row_bytes];
+ S32 mid_row = getHeight() / 2;
+ for( S32 row = 0; row < mid_row; row++ )
+ {
+ U8* row_a_data = getData() + row * row_bytes;
+ U8* row_b_data = getData() + (getHeight() - 1 - row) * row_bytes;
+ memcpy( line_buffer, row_a_data, row_bytes );
+ memcpy( row_a_data, row_b_data, row_bytes );
+ memcpy( row_b_data, line_buffer, row_bytes );
+ }
+ delete[] line_buffer;
+}
+
+
+void LLImageRaw::expandToPowerOfTwo(S32 max_dim, BOOL scale_image)
+{
+ // Find new sizes
+ S32 new_width = MIN_IMAGE_SIZE;
+ S32 new_height = MIN_IMAGE_SIZE;
+
+ while( (new_width < getWidth()) && (new_width < max_dim) )
+ {
+ new_width <<= 1;
+ }
+
+ while( (new_height < getHeight()) && (new_height < max_dim) )
+ {
+ new_height <<= 1;
+ }
+
+ scale( new_width, new_height, scale_image );
+}
+
+void LLImageRaw::contractToPowerOfTwo(S32 max_dim, BOOL scale_image)
+{
+ // Find new sizes
+ S32 new_width = max_dim;
+ S32 new_height = max_dim;
+
+ while( (new_width > getWidth()) && (new_width > MIN_IMAGE_SIZE) )
+ {
+ new_width >>= 1;
+ }
+
+ while( (new_height > getHeight()) && (new_height > MIN_IMAGE_SIZE) )
+ {
+ new_height >>= 1;
+ }
+
+ scale( new_width, new_height, scale_image );
+}
+
+void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
+{
+ // Strong bias towards rounding down (to save bandwidth)
+ // No bias would mean THRESHOLD == 1.5f;
+ const F32 THRESHOLD = 1.75f;
+
+ // Find new sizes
+ S32 larger_w = max_dim; // 2^n >= mWidth
+ S32 smaller_w = max_dim; // 2^(n-1) <= mWidth
+ while( (smaller_w > getWidth()) && (smaller_w > MIN_IMAGE_SIZE) )
+ {
+ larger_w = smaller_w;
+ smaller_w >>= 1;
+ }
+ S32 new_width = ( (F32)getWidth() / smaller_w > THRESHOLD ) ? larger_w : smaller_w;
+
+
+ S32 larger_h = max_dim; // 2^m >= mHeight
+ S32 smaller_h = max_dim; // 2^(m-1) <= mHeight
+ while( (smaller_h > getHeight()) && (smaller_h > MIN_IMAGE_SIZE) )
+ {
+ larger_h = smaller_h;
+ smaller_h >>= 1;
+ }
+ S32 new_height = ( (F32)getHeight() / smaller_h > THRESHOLD ) ? larger_h : smaller_h;
+
+
+ scale( new_width, new_height );
+}
+
+
+
+
+// Calculates (U8)(255*(a/255.f)*(b/255.f) + 0.5f). Thanks, Jim Blinn!
+inline U8 LLImageRaw::fastFractionalMult( U8 a, U8 b )
+{
+ U32 i = a * b + 128;
+ return U8((i + (i>>8)) >> 8);
+}
+
+
+void LLImageRaw::composite( LLImageRaw* src )
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
+ llassert( (3 == dst->getComponents()) || (4 == dst->getComponents()) );
+
+ if( 3 == dst->getComponents() )
+ {
+ if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) )
+ {
+ // No scaling needed
+ if( 3 == src->getComponents() )
+ {
+ copyUnscaled( src ); // alpha is one so just copy the data.
+ }
+ else
+ {
+ compositeUnscaled4onto3( src );
+ }
+ }
+ else
+ {
+ if( 3 == src->getComponents() )
+ {
+ copyScaled( src ); // alpha is one so just copy the data.
+ }
+ else
+ {
+ compositeScaled4onto3( src );
+ }
+ }
+ }
+ else
+ {
+ // 4 == dst->mComponents
+ llassert(0); // not implemented yet.
+ }
+}
+
+// Src and dst can be any size. Src has 4 components. Dst has 3 components.
+void LLImageRaw::compositeScaled4onto3(LLImageRaw* src)
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ llinfos << "compositeScaled4onto3" << llendl;
+
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (4 == src->getComponents()) && (3 == dst->getComponents()) );
+
+ // Vertical: scale but no composite
+ S32 temp_data_size = src->getWidth() * dst->getHeight() * src->getComponents();
+ U8* temp_buffer = new U8[ temp_data_size ];
+ for( S32 col = 0; col < src->getWidth(); col++ )
+ {
+ copyLineScaled( src->getData() + (src->getComponents() * col), temp_buffer + (src->getComponents() * col), src->getHeight(), dst->getHeight(), src->getWidth(), src->getWidth() );
+ }
+
+ // Horizontal: scale and composite
+ for( S32 row = 0; row < dst->getHeight(); row++ )
+ {
+ compositeRowScaled4onto3( temp_buffer + (src->getComponents() * src->getWidth() * row), dst->getData() + (dst->getComponents() * dst->getWidth() * row), src->getWidth(), dst->getWidth() );
+ }
+
+ // Clean up
+ delete[] temp_buffer;
+}
+
+
+// Src and dst are same size. Src has 4 components. Dst has 3 components.
+void LLImageRaw::compositeUnscaled4onto3( LLImageRaw* src )
+{
+ /*
+ //test fastFractionalMult()
+ {
+ U8 i = 255;
+ U8 j = 255;
+ do
+ {
+ do
+ {
+ llassert( fastFractionalMult(i, j) == (U8)(255*(i/255.f)*(j/255.f) + 0.5f) );
+ } while( j-- );
+ } while( i-- );
+ }
+ */
+
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
+ llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
+
+
+ U8* src_data = src->getData();
+ U8* dst_data = dst->getData();
+ S32 pixels = getWidth() * getHeight();
+ while( pixels-- )
+ {
+ U8 alpha = src_data[3];
+ if( alpha )
+ {
+ if( 255 == alpha )
+ {
+ dst_data[0] = src_data[0];
+ dst_data[1] = src_data[1];
+ dst_data[2] = src_data[2];
+ }
+ else
+ {
+
+ U8 transparency = 255 - alpha;
+ dst_data[0] = fastFractionalMult( dst_data[0], transparency ) + fastFractionalMult( src_data[0], alpha );
+ dst_data[1] = fastFractionalMult( dst_data[1], transparency ) + fastFractionalMult( src_data[1], alpha );
+ dst_data[2] = fastFractionalMult( dst_data[2], transparency ) + fastFractionalMult( src_data[2], alpha );
+ }
+ }
+
+ src_data += 4;
+ dst_data += 3;
+ }
+}
+
+// Fill the buffer with a constant color
+void LLImageRaw::fill( const LLColor4U& color )
+{
+ S32 pixels = getWidth() * getHeight();
+ if( 4 == getComponents() )
+ {
+ U32* data = (U32*) getData();
+ for( S32 i = 0; i < pixels; i++ )
+ {
+ data[i] = color.mAll;
+ }
+ }
+ else
+ if( 3 == getComponents() )
+ {
+ U8* data = getData();
+ for( S32 i = 0; i < pixels; i++ )
+ {
+ data[0] = color.mV[0];
+ data[1] = color.mV[1];
+ data[2] = color.mV[2];
+ data += 3;
+ }
+ }
+}
+
+
+
+
+// Src and dst can be any size. Src and dst can each have 3 or 4 components.
+void LLImageRaw::copy(LLImageRaw* src)
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
+ llassert( (3 == dst->getComponents()) || (4 == dst->getComponents()) );
+
+ if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) )
+ {
+ // No scaling needed
+ if( src->getComponents() == dst->getComponents() )
+ {
+ copyUnscaled( src );
+ }
+ else
+ if( 3 == src->getComponents() )
+ {
+ copyUnscaled3onto4( src );
+ }
+ else
+ {
+ // 4 == src->getComponents()
+ copyUnscaled4onto3( src );
+ }
+ }
+ else
+ {
+ // Scaling needed
+ // No scaling needed
+ if( src->getComponents() == dst->getComponents() )
+ {
+ copyScaled( src );
+ }
+ else
+ if( 3 == src->getComponents() )
+ {
+ copyScaled3onto4( src );
+ }
+ else
+ {
+ // 4 == src->getComponents()
+ copyScaled4onto3( src );
+ }
+ }
+}
+
+// Src and dst are same size. Src and dst have same number of components.
+void LLImageRaw::copyUnscaled(LLImageRaw* src)
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
+ llassert( src->getComponents() == dst->getComponents() );
+ llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
+
+ memcpy( dst->getData(), src->getData(), getWidth() * getHeight() * getComponents() );
+}
+
+
+// Src and dst can be any size. Src has 3 components. Dst has 4 components.
+void LLImageRaw::copyScaled3onto4(LLImageRaw* src)
+{
+ llassert( (3 == src->getComponents()) && (4 == getComponents()) );
+
+ // Slow, but simple. Optimize later if needed.
+ LLImageRaw temp( src->getWidth(), src->getHeight(), 4);
+ temp.copyUnscaled3onto4( src );
+ copyScaled( &temp );
+}
+
+
+// Src and dst can be any size. Src has 4 components. Dst has 3 components.
+void LLImageRaw::copyScaled4onto3(LLImageRaw* src)
+{
+ llassert( (4 == src->getComponents()) && (3 == getComponents()) );
+
+ // Slow, but simple. Optimize later if needed.
+ LLImageRaw temp( src->getWidth(), src->getHeight(), 3);
+ temp.copyUnscaled4onto3( src );
+ copyScaled( &temp );
+}
+
+
+// Src and dst are same size. Src has 4 components. Dst has 3 components.
+void LLImageRaw::copyUnscaled4onto3( LLImageRaw* src )
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == dst->getComponents()) && (4 == src->getComponents()) );
+ llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
+
+ S32 pixels = getWidth() * getHeight();
+ U8* src_data = src->getData();
+ U8* dst_data = dst->getData();
+ for( S32 i=0; i<pixels; i++ )
+ {
+ dst_data[0] = src_data[0];
+ dst_data[1] = src_data[1];
+ dst_data[2] = src_data[2];
+ src_data += 4;
+ dst_data += 3;
+ }
+}
+
+
+// Src and dst are same size. Src has 3 components. Dst has 4 components.
+void LLImageRaw::copyUnscaled3onto4( LLImageRaw* src )
+{
+ LLImageRaw* dst = this; // Just for clarity.
+ llassert( 3 == src->getComponents() );
+ llassert( 4 == dst->getComponents() );
+ llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
+
+ S32 pixels = getWidth() * getHeight();
+ U8* src_data = src->getData();
+ U8* dst_data = dst->getData();
+ for( S32 i=0; i<pixels; i++ )
+ {
+ dst_data[0] = src_data[0];
+ dst_data[1] = src_data[1];
+ dst_data[2] = src_data[2];
+ dst_data[3] = 255;
+ src_data += 3;
+ dst_data += 4;
+ }
+}
+
+
+// Src and dst can be any size. Src and dst have same number of components.
+void LLImageRaw::copyScaled( LLImageRaw* src )
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
+ llassert( src->getComponents() == dst->getComponents() );
+
+ if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) )
+ {
+ memcpy( dst->getData(), src->getData(), getWidth() * getHeight() * getComponents() );
+ return;
+ }
+
+ // Vertical
+ S32 temp_data_size = src->getWidth() * dst->getHeight() * getComponents();
+ U8* temp_buffer = new U8[ temp_data_size ];
+ for( S32 col = 0; col < src->getWidth(); col++ )
+ {
+ copyLineScaled( src->getData() + (getComponents() * col), temp_buffer + (getComponents() * col), src->getHeight(), dst->getHeight(), src->getWidth(), src->getWidth() );
+ }
+
+ // Horizontal
+ for( S32 row = 0; row < dst->getHeight(); row++ )
+ {
+ copyLineScaled( temp_buffer + (getComponents() * src->getWidth() * row), dst->getData() + (getComponents() * dst->getWidth() * row), src->getWidth(), dst->getWidth(), 1, 1 );
+ }
+
+ // Clean up
+ delete[] temp_buffer;
+}
+
+
+void LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data )
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ llassert( (3 == getComponents()) || (4 == getComponents()) );
+
+ S32 old_width = getWidth();
+ S32 old_height = getHeight();
+
+ if( (old_width == new_width) && (old_height == new_height) )
+ {
+ return; // Nothing to do.
+ }
+
+ // Reallocate the data buffer.
+
+ if (scale_image_data)
+ {
+ // Vertical
+ S32 temp_data_size = old_width * new_height * getComponents();
+ U8* temp_buffer = new U8[ temp_data_size ];
+ for( S32 col = 0; col < old_width; col++ )
+ {
+ copyLineScaled( getData() + (getComponents() * col), temp_buffer + (getComponents() * col), old_height, new_height, old_width, old_width );
+ }
+
+ deleteData();
+
+ U8* new_buffer = allocateDataSize(new_width, new_height, getComponents());
+
+ // Horizontal
+ for( S32 row = 0; row < new_height; row++ )
+ {
+ copyLineScaled( temp_buffer + (getComponents() * old_width * row), new_buffer + (getComponents() * new_width * row), old_width, new_width, 1, 1 );
+ }
+
+ // Clean up
+ delete[] temp_buffer;
+ }
+ else
+ {
+ // copy out existing image data
+ S32 temp_data_size = old_width * old_height * getComponents();
+ U8* temp_buffer = new U8[ temp_data_size ];
+ memcpy(temp_buffer, getData(), temp_data_size);
+
+ // allocate new image data, will delete old data
+ U8* new_buffer = allocateDataSize(new_width, new_height, getComponents());
+
+ for( S32 row = 0; row < new_height; row++ )
+ {
+ if (row < old_height)
+ {
+ memcpy(new_buffer + (new_width * row * getComponents()), temp_buffer + (old_width * row * getComponents()), getComponents() * llmin(old_width, new_width));
+ if (old_width < new_width)
+ {
+ // pad out rest of row with black
+ memset(new_buffer + (getComponents() * ((new_width * row) + old_width)), 0, getComponents() * (new_width - old_width));
+ }
+ }
+ else
+ {
+ // pad remaining rows with black
+ memset(new_buffer + (new_width * row * getComponents()), 0, new_width * getComponents());
+ }
+ }
+
+ // Clean up
+ delete[] temp_buffer;
+ }
+}
+
+void LLImageRaw::copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step )
+{
+ const S32 components = getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ const F32 ratio = F32(in_pixel_len) / out_pixel_len; // ratio of old to new
+ const F32 norm_factor = 1.f / ratio;
+
+ S32 goff = components >= 2 ? 1 : 0;
+ S32 boff = components >= 3 ? 2 : 0;
+ for( S32 x = 0; x < out_pixel_len; x++ )
+ {
+ // Sample input pixels in range from sample0 to sample1.
+ // Avoid floating point accumulation error... don't just add ratio each time. JC
+ const F32 sample0 = x * ratio;
+ const F32 sample1 = (x+1) * ratio;
+ const S32 index0 = llfloor(sample0); // left integer (floor)
+ const S32 index1 = llfloor(sample1); // right integer (floor)
+ const F32 fract0 = 1.f - (sample0 - F32(index0)); // spill over on left
+ const F32 fract1 = sample1 - F32(index1); // spill-over on right
+
+ if( index0 == index1 )
+ {
+ // Interval is embedded in one input pixel
+ S32 t0 = x * out_pixel_step * components;
+ S32 t1 = index0 * in_pixel_step * components;
+ U8* outp = out + t0;
+ U8* inp = in + t1;
+ for (S32 i = 0; i < components; ++i)
+ {
+ *outp = *inp;
+ ++outp;
+ ++inp;
+ }
+ }
+ else
+ {
+ // Left straddle
+ S32 t1 = index0 * in_pixel_step * components;
+ F32 r = in[t1 + 0] * fract0;
+ F32 g = in[t1 + goff] * fract0;
+ F32 b = in[t1 + boff] * fract0;
+ F32 a = 0;
+ if( components == 4)
+ {
+ a = in[t1 + 3] * fract0;
+ }
+
+ // Central interval
+ if (components < 4)
+ {
+ for( S32 u = index0 + 1; u < index1; u++ )
+ {
+ S32 t2 = u * in_pixel_step * components;
+ r += in[t2 + 0];
+ g += in[t2 + goff];
+ b += in[t2 + boff];
+ }
+ }
+ else
+ {
+ for( S32 u = index0 + 1; u < index1; u++ )
+ {
+ S32 t2 = u * in_pixel_step * components;
+ r += in[t2 + 0];
+ g += in[t2 + 1];
+ b += in[t2 + 2];
+ a += in[t2 + 3];
+ }
+ }
+
+ // right straddle
+ // Watch out for reading off of end of input array.
+ if( fract1 && index1 < in_pixel_len )
+ {
+ S32 t3 = index1 * in_pixel_step * components;
+ if (components < 4)
+ {
+ U8 in0 = in[t3 + 0];
+ U8 in1 = in[t3 + goff];
+ U8 in2 = in[t3 + boff];
+ r += in0 * fract1;
+ g += in1 * fract1;
+ b += in2 * fract1;
+ }
+ else
+ {
+ U8 in0 = in[t3 + 0];
+ U8 in1 = in[t3 + 1];
+ U8 in2 = in[t3 + 2];
+ U8 in3 = in[t3 + 3];
+ r += in0 * fract1;
+ g += in1 * fract1;
+ b += in2 * fract1;
+ a += in3 * fract1;
+ }
+ }
+
+ r *= norm_factor;
+ g *= norm_factor;
+ b *= norm_factor;
+ a *= norm_factor; // skip conditional
+
+ S32 t4 = x * out_pixel_step * components;
+ out[t4 + 0] = U8(llround(r));
+ if (components >= 2)
+ out[t4 + 1] = U8(llround(g));
+ if (components >= 3)
+ out[t4 + 2] = U8(llround(b));
+ if( components == 4)
+ out[t4 + 3] = U8(llround(a));
+ }
+ }
+}
+
+void LLImageRaw::compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len )
+{
+ llassert( getComponents() == 3 );
+
+ const S32 IN_COMPONENTS = 4;
+ const S32 OUT_COMPONENTS = 3;
+
+ const F32 ratio = F32(in_pixel_len) / out_pixel_len; // ratio of old to new
+ const F32 norm_factor = 1.f / ratio;
+
+ for( S32 x = 0; x < out_pixel_len; x++ )
+ {
+ // Sample input pixels in range from sample0 to sample1.
+ // Avoid floating point accumulation error... don't just add ratio each time. JC
+ const F32 sample0 = x * ratio;
+ const F32 sample1 = (x+1) * ratio;
+ const S32 index0 = S32(sample0); // left integer (floor)
+ const S32 index1 = S32(sample1); // right integer (floor)
+ const F32 fract0 = 1.f - (sample0 - F32(index0)); // spill over on left
+ const F32 fract1 = sample1 - F32(index1); // spill-over on right
+
+ U8 in_scaled_r;
+ U8 in_scaled_g;
+ U8 in_scaled_b;
+ U8 in_scaled_a;
+
+ if( index0 == index1 )
+ {
+ // Interval is embedded in one input pixel
+ S32 t1 = index0 * IN_COMPONENTS;
+ in_scaled_r = in[t1 + 0];
+ in_scaled_g = in[t1 + 0];
+ in_scaled_b = in[t1 + 0];
+ in_scaled_a = in[t1 + 0];
+ }
+ else
+ {
+ // Left straddle
+ S32 t1 = index0 * IN_COMPONENTS;
+ F32 r = in[t1 + 0] * fract0;
+ F32 g = in[t1 + 1] * fract0;
+ F32 b = in[t1 + 2] * fract0;
+ F32 a = in[t1 + 3] * fract0;
+
+ // Central interval
+ for( S32 u = index0 + 1; u < index1; u++ )
+ {
+ S32 t2 = u * IN_COMPONENTS;
+ r += in[t2 + 0];
+ g += in[t2 + 1];
+ b += in[t2 + 2];
+ a += in[t2 + 3];
+ }
+
+ // right straddle
+ // Watch out for reading off of end of input array.
+ if( fract1 && index1 < in_pixel_len )
+ {
+ S32 t3 = index1 * IN_COMPONENTS;
+ r += in[t3 + 0] * fract1;
+ g += in[t3 + 1] * fract1;
+ b += in[t3 + 2] * fract1;
+ a += in[t3 + 3] * fract1;
+ }
+
+ r *= norm_factor;
+ g *= norm_factor;
+ b *= norm_factor;
+ a *= norm_factor;
+
+ in_scaled_r = U8(llround(r));
+ in_scaled_g = U8(llround(g));
+ in_scaled_b = U8(llround(b));
+ in_scaled_a = U8(llround(a));
+ }
+
+ if( in_scaled_a )
+ {
+ if( 255 == in_scaled_a )
+ {
+ out[0] = in_scaled_r;
+ out[1] = in_scaled_g;
+ out[2] = in_scaled_b;
+ }
+ else
+ {
+ U8 transparency = 255 - in_scaled_a;
+ out[0] = fastFractionalMult( out[0], transparency ) + fastFractionalMult( in_scaled_r, in_scaled_a );
+ out[1] = fastFractionalMult( out[1], transparency ) + fastFractionalMult( in_scaled_g, in_scaled_a );
+ out[2] = fastFractionalMult( out[2], transparency ) + fastFractionalMult( in_scaled_b, in_scaled_a );
+ }
+ }
+ out += OUT_COMPONENTS;
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+static struct
+{
+ const char* exten;
+ S8 codec;
+}
+file_extensions[] =
+{
+ { "bmp", IMG_CODEC_BMP },
+ { "tga", IMG_CODEC_TGA },
+ { "j2c", IMG_CODEC_J2C },
+ { "jp2", IMG_CODEC_J2C },
+ { "texture", IMG_CODEC_J2C },
+ { "jpg", IMG_CODEC_JPEG },
+ { "jpeg", IMG_CODEC_JPEG },
+ { "mip", IMG_CODEC_DXT },
+ { "dxt", IMG_CODEC_DXT }
+};
+#define NUM_FILE_EXTENSIONS sizeof(file_extensions)/sizeof(file_extensions[0])
+
+static LLString find_file(LLString &name, S8 *codec)
+{
+ LLString tname;
+ for (int i=0; i<(int)(NUM_FILE_EXTENSIONS); i++)
+ {
+ tname = name + "." + LLString(file_extensions[i].exten);
+ llifstream ifs(tname.c_str(), llifstream::binary);
+ if (ifs.is_open())
+ {
+ ifs.close();
+ if (codec)
+ *codec = file_extensions[i].codec;
+ return LLString(file_extensions[i].exten);
+ }
+ }
+ return LLString("");
+}
+
+static S8 get_codec(const LLString& exten)
+{
+ for (int i=0; i<(int)(NUM_FILE_EXTENSIONS); i++)
+ {
+ if (exten == file_extensions[i].exten)
+ return file_extensions[i].codec;
+ }
+ return IMG_CODEC_INVALID;
+}
+
+bool LLImageRaw::createFromFile(const LLString &filename, bool j2c_lowest_mip_only)
+{
+ LLString name = filename;
+ size_t dotidx = name.rfind('.');
+ S8 codec = IMG_CODEC_INVALID;
+ LLString exten;
+
+ deleteData(); // delete any existing data
+
+ if (dotidx != LLString::npos)
+ {
+ exten = name.substr(dotidx+1);
+ LLString::toLower(exten);
+ codec = get_codec(exten);
+ }
+ else
+ {
+ exten = find_file(name, &codec);
+ name = name + "." + exten;
+ }
+ if (codec == IMG_CODEC_INVALID)
+ {
+ return false; // format not recognized
+ }
+
+ llifstream ifs(name.c_str(), llifstream::binary);
+ if (!ifs.is_open())
+ {
+ // SJB: changed from llinfos to lldebugs to reduce spam
+ lldebugs << "Unable to open image file: " << name << llendl;
+ return false;
+ }
+
+ ifs.seekg (0, std::ios::end);
+ int length = ifs.tellg();
+ if (j2c_lowest_mip_only && length > 2048)
+ {
+ length = 2048;
+ }
+ ifs.seekg (0, std::ios::beg);
+
+ if (!length)
+ {
+ llinfos << "Zero length file file: " << name << llendl;
+ return false;
+ }
+
+ LLPointer<LLImageFormatted> image;
+ switch(codec)
+ {
+ //case IMG_CODEC_RGB:
+ case IMG_CODEC_BMP:
+ image = new LLImageBMP();
+ break;
+ case IMG_CODEC_TGA:
+ image = new LLImageTGA();
+ break;
+#if JPEG_SUPPORT
+ case IMG_CODEC_JPEG:
+ image = new LLImageJPEG();
+ break;
+#endif
+ case IMG_CODEC_J2C:
+ image = new LLImageJ2C();
+ break;
+ case IMG_CODEC_DXT:
+ image = new LLImageDXT();
+ break;
+ default:
+ return false;
+ }
+ llassert(image.notNull());
+
+ U8 *buffer = image->allocateData(length);
+ ifs.read ((char*)buffer, length);
+ ifs.close();
+
+ image->updateData();
+
+ if (j2c_lowest_mip_only && codec == IMG_CODEC_J2C)
+ {
+ S32 width = image->getWidth();
+ S32 height = image->getHeight();
+ S32 discard_level = 0;
+ while (width > 1 && height > 1 && discard_level < MAX_DISCARD_LEVEL)
+ {
+ width >>= 1;
+ height >>= 1;
+ discard_level++;
+ }
+ ((LLImageJ2C *)((LLImageFormatted*)image))->setDiscardLevel(discard_level);
+ }
+
+ BOOL success = image->decode(this, 100000.0f);
+ image = NULL; // deletes image
+
+ if (!success)
+ {
+ deleteData();
+ llwarns << "Unable to decode image" << name << llendl;
+ return false;
+ }
+
+ return true;
+}
+
+//---------------------------------------------------------------------------
+// LLImageFormatted
+//---------------------------------------------------------------------------
+
+//static
+S32 LLImageFormatted::sGlobalFormattedMemory = 0;
+
+//static
+LLWorkerThread* LLImageFormatted::sWorkerThread = NULL;
+
+//static
+void LLImageFormatted::initClass(bool threaded, bool run_always)
+{
+ sWorkerThread = new LLWorkerThread(threaded, run_always);
+}
+
+//static
+void LLImageFormatted::cleanupClass()
+{
+ delete sWorkerThread;
+ sWorkerThread = NULL;
+}
+
+
+LLImageFormatted::LLImageFormatted(S8 codec)
+ : LLImageBase(), LLWorkerClass(sWorkerThread, "ImageFormatted"),
+ mCodec(codec),
+ mDecoding(0),
+ mDecoded(0),
+ mDiscardLevel(0)
+{
+ mMemType = LLMemType::MTYPE_IMAGEFORMATTED;
+}
+
+// virtual
+LLImageFormatted::~LLImageFormatted()
+{
+ // NOTE: ~LLimageBase() call to deleteData() calls LLImageBase::deleteData()
+ // NOT LLImageFormatted::deleteData()
+ deleteData();
+ releaseDecodedData();
+}
+
+//----------------------------------------------------------------------------
+
+//virtual
+void LLImageFormatted::startWork(S32 param)
+{
+ if (mDecoding) llerrs << "WTF?" << llendl;
+}
+
+bool LLImageFormatted::doWork(S32 param)
+{
+ if (!(isWorking())) llerrs << "WTF?" << llendl;
+ llassert(mDecodedImage.notNull());
+ if (param == 0)
+ {
+ // Decode primary channels
+ mDecoded = decode(mDecodedImage, .001f); // 1ms
+ }
+ else
+ {
+ // Decode aux channel
+ mDecoded = decode(mDecodedImage, .001f, param, param); // 1ms
+ }
+ if (mDecoded)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void LLImageFormatted::endWork(S32 param, bool aborted)
+{
+ if (mDecoding) llerrs << "WTF?" << llendl;
+ if (!mDecoded) llerrs << "WTF?" << llendl;
+}
+
+//----------------------------------------------------------------------------
+
+// static
+LLImageFormatted* LLImageFormatted::createFromExtension(const LLString& instring)
+{
+ LLString exten;
+ size_t dotidx = instring.rfind('.');
+ if (dotidx != LLString::npos)
+ {
+ exten = instring.substr(dotidx+1);
+ }
+ else
+ {
+ exten = instring;
+ }
+ S8 codec = get_codec(exten);
+ LLPointer<LLImageFormatted> image;
+ switch(codec)
+ {
+ case IMG_CODEC_BMP:
+ image = new LLImageBMP();
+ break;
+ case IMG_CODEC_TGA:
+ image = new LLImageTGA();
+ break;
+#if JPEG_SUPPORT
+ case IMG_CODEC_JPEG:
+ image = new LLImageJPEG();
+ break;
+#endif
+ case IMG_CODEC_J2C:
+ image = new LLImageJ2C();
+ break;
+ case IMG_CODEC_DXT:
+ image = new LLImageDXT();
+ break;
+ default:
+ break;
+ }
+ return image;
+}
+//----------------------------------------------------------------------------
+
+// virtual
+void LLImageFormatted::dump()
+{
+ LLImageBase::dump();
+
+ llinfos << "LLImageFormatted"
+ << " mDecoding " << mDecoding
+ << " mCodec " << S32(mCodec)
+ << " mDecoded " << mDecoded
+ << llendl;
+}
+
+//----------------------------------------------------------------------------
+
+void LLImageFormatted::readHeader(U8* data, S32 size)
+{
+ if (size <= 0)
+ {
+ size = calcHeaderSize();
+ }
+ copyData(data, size); // calls updateData()
+}
+
+S32 LLImageFormatted::calcDataSize(S32 discard_level)
+{
+ if (discard_level < 0)
+ {
+ discard_level = mDiscardLevel;
+ }
+ S32 w = getWidth() >> discard_level;
+ S32 h = getHeight() >> discard_level;
+ w = llmax(w, 1);
+ h = llmax(h, 1);
+ return w * h * getComponents();
+}
+
+S32 LLImageFormatted::calcDiscardLevelBytes(S32 bytes)
+{
+ llassert(bytes >= 0);
+ S32 discard_level = 0;
+ while (1)
+ {
+ S32 bytes_needed = calcDataSize(discard_level); // virtual
+ if (bytes_needed <= bytes)
+ {
+ break;
+ }
+ discard_level++;
+ if (discard_level > MAX_IMAGE_MIP)
+ {
+ return -1;
+ }
+ }
+ return discard_level;
+}
+
+
+//----------------------------------------------------------------------------
+
+// Subclasses that can handle more than 4 channels should override this function.
+BOOL LLImageFormatted::decode(LLImageRaw* raw_image,F32 decode_time, S32 first_channel, S32 max_channel)
+{
+ llassert( (first_channel == 0) && (max_channel == 4) );
+ return decode( raw_image, decode_time ); // Loads first 4 channels by default.
+}
+
+// virtual
+BOOL LLImageFormatted::requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard, F32 decode_time)
+{
+ llassert(getData() && getDataSize());
+ // For most codecs, only mDiscardLevel data is available. (see LLImageDXT for exception)
+ if (discard >= 0 && discard != mDiscardLevel)
+ {
+ llerrs << "Request for invalid discard level" << llendl;
+ }
+ if (haveWork())
+ {
+ checkWork();
+ }
+ if (!mDecoded)
+ {
+ if (!haveWork())
+ {
+ llassert(!mDecoding);
+ mDecodedImage = new LLImageRaw(getWidth(), getHeight(), getComponents());
+ addWork(0);
+ }
+ return FALSE;
+ }
+ else
+ {
+ llassert(mDecodedImage.notNull());
+ llassert(!mDecoding);
+ raw = mDecodedImage;
+ return TRUE;
+ }
+}
+
+BOOL LLImageFormatted::requestDecodedAuxData(LLPointer<LLImageRaw>& raw, S32 channel,
+ S32 discard, F32 decode_time)
+{
+ llassert(getData() && getDataSize());
+ // For most codecs, only mDiscardLevel data is available. (see LLImageDXT for exception)
+ if (discard >= 0 && discard != mDiscardLevel)
+ {
+ llerrs << "Request for invalid discard level" << llendl;
+ }
+ if (haveWork())
+ {
+ checkWork();
+ }
+ if (!mDecoded)
+ {
+ if (!haveWork())
+ {
+ llassert(!mDecoding);
+ mDecodedImage = new LLImageRaw(getWidth(), getHeight(), 1);
+ addWork(channel);
+ }
+ return FALSE;
+ }
+ else
+ {
+ llassert(mDecodedImage.notNull());
+ llassert(!mDecoding);
+ raw = mDecodedImage;
+ return TRUE;
+ }
+}
+
+
+// virtual
+void LLImageFormatted::releaseDecodedData()
+{
+ if (mDecoded || mDecoding)
+ {
+ mDecodedImage = NULL; // deletes image
+ mDecoded = FALSE;
+ mDecoding = FALSE;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// virtual
+U8* LLImageFormatted::allocateData(S32 size)
+{
+ U8* res = LLImageBase::allocateData(size); // calls deleteData()
+ sGlobalFormattedMemory += getDataSize();
+ return res;
+}
+
+// virtual
+U8* LLImageFormatted::reallocateData(S32 size)
+{
+ sGlobalFormattedMemory -= getDataSize();
+ U8* res = LLImageBase::reallocateData(size);
+ sGlobalFormattedMemory += getDataSize();
+ return res;
+}
+
+// virtual
+void LLImageFormatted::deleteData()
+{
+ sGlobalFormattedMemory -= getDataSize();
+ LLImageBase::deleteData();
+}
+
+//----------------------------------------------------------------------------
+
+// virtual
+void LLImageFormatted::sanityCheck()
+{
+ LLImageBase::sanityCheck();
+
+ if (mCodec >= IMG_CODEC_EOF)
+ {
+ llerrs << "Failed LLImageFormatted::sanityCheck "
+ << "decoding " << S32(mDecoding)
+ << "decoded " << S32(mDecoded)
+ << "codec " << S32(mCodec)
+ << llendl;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+BOOL LLImageFormatted::copyData(U8 *data, S32 size)
+{
+ if (data && data != getData())
+ {
+ deleteData();
+ allocateData(size);
+ memcpy(getData(), data, size);
+ }
+ updateData(); // virtual
+
+ return TRUE;
+}
+
+BOOL LLImageFormatted::appendData(U8 *data, S32 size)
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ S32 old_size = getDataSize();
+ U8* old_data = getData();
+ S32 new_size = old_size + size;
+ U8* new_data = new U8[new_size];
+ // resize the image
+ setDataAndSize(new_data, new_size);
+ // copy the old data and delete it
+ memcpy(new_data, old_data, old_size);
+ delete old_data;
+ // if we have new data, copy it and call updateData()
+ if (data)
+ {
+ memcpy(new_data + old_size, data, size);
+ updateData(); // virtual
+ }
+ return TRUE;
+}
+
+BOOL LLImageFormatted::setData(U8 *data, S32 size)
+{
+ if (data && data != getData())
+ {
+ deleteData();
+ setDataAndSize(data, size); // Access private LLImageBase members
+ sGlobalFormattedMemory += getDataSize();
+ }
+ return updateData(); // virtual
+}
+
+//----------------------------------------------------------------------------
+
+BOOL LLImageFormatted::load(const LLString &filename)
+{
+ resetLastError();
+
+ S32 file_size = 0;
+ apr_file_t* apr_file = ll_apr_file_open(filename, LL_APR_RB, &file_size);
+ if (!apr_file)
+ {
+ setLastError("Unable to open file for reading", filename);
+ return FALSE;
+ }
+ if (file_size == 0)
+ {
+ setLastError("File is empty",filename);
+ apr_file_close(apr_file);
+ return FALSE;
+ }
+
+ BOOL res;
+ U8 *data = allocateData(file_size);
+ apr_size_t bytes_read = file_size;
+ apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read
+ if (s != APR_SUCCESS || (S32) bytes_read != file_size)
+ {
+ deleteData();
+ setLastError("Unable to read entire file",filename);
+ res = FALSE;
+ }
+ else
+ {
+ res = updateData();
+ }
+ apr_file_close(apr_file);
+
+ return res;
+}
+
+BOOL LLImageFormatted::save(const LLString &filename)
+{
+ resetLastError();
+
+ apr_file_t* apr_file = ll_apr_file_open(filename, LL_APR_WB);
+ if (!apr_file)
+ {
+ setLastError("Unable to open file for reading", filename);
+ return FALSE;
+ }
+
+ ll_apr_file_write(apr_file, getData(), getDataSize());
+ apr_file_close(apr_file);
+
+ return TRUE;
+}
+
+// BOOL LLImageFormatted::save(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
+// Depricated to remove VFS dependency.
+// Use:
+// LLVFile::writeFile(image->getData(), image->getDataSize(), vfs, uuid, type);
+
+//----------------------------------------------------------------------------
+
+S8 LLImageFormatted::getCodec() const
+{
+ return mCodec;
+}
+
+//============================================================================
+
+//----------------------------------------------------------------------------
+
+static void avg4_colors4(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst)
+{
+ dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2);
+ dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2);
+ dst[2] = (U8)(((U32)(a[2]) + b[2] + c[2] + d[2])>>2);
+ dst[3] = (U8)(((U32)(a[3]) + b[3] + c[3] + d[3])>>2);
+}
+
+static void avg4_colors3(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst)
+{
+ dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2);
+ dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2);
+ dst[2] = (U8)(((U32)(a[2]) + b[2] + c[2] + d[2])>>2);
+}
+
+static void avg4_colors2(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst)
+{
+ dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2);
+ dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2);
+}
+
+//static
+void LLImageBase::generateMip(const U8* indata, U8* mipdata, S32 width, S32 height, S32 nchannels)
+{
+ llassert(width > 0 && height > 0);
+ U8* data = mipdata;
+ S32 in_width = width*2;
+ for (S32 h=0; h<height; h++)
+ {
+ for (S32 w=0; w<width; w++)
+ {
+ switch(nchannels)
+ {
+ case 4:
+ avg4_colors4(indata, indata+4, indata+4*in_width, indata+4*in_width+4, data);
+ break;
+ case 3:
+ avg4_colors3(indata, indata+3, indata+3*in_width, indata+3*in_width+3, data);
+ break;
+ case 2:
+ avg4_colors2(indata, indata+2, indata+2*in_width, indata+2*in_width+2, data);
+ break;
+ case 1:
+ *(U8*)data = (U8)(((U32)(indata[0]) + indata[1] + indata[in_width] + indata[in_width+1])>>2);
+ break;
+ default:
+ llerrs << "generateMmip called with bad num channels" << llendl;
+ }
+ indata += nchannels*2;
+ data += nchannels;
+ }
+ indata += nchannels*in_width; // skip odd lines
+ }
+}
+
+
+//============================================================================
+
+//static
+F32 LLImageBase::calc_download_priority(F32 virtual_size, F32 visible_pixels, S32 bytes_sent)
+{
+ F32 w_priority;
+
+ F32 bytes_weight = 1.f;
+ if (!bytes_sent)
+ {
+ bytes_weight = 20.f;
+ }
+ else if (bytes_sent < 1000)
+ {
+ bytes_weight = 1.f;
+ }
+ else if (bytes_sent < 2000)
+ {
+ bytes_weight = 1.f/1.5f;
+ }
+ else if (bytes_sent < 4000)
+ {
+ bytes_weight = 1.f/3.f;
+ }
+ else if (bytes_sent < 8000)
+ {
+ bytes_weight = 1.f/6.f;
+ }
+ else if (bytes_sent < 16000)
+ {
+ bytes_weight = 1.f/12.f;
+ }
+ else if (bytes_sent < 32000)
+ {
+ bytes_weight = 1.f/20.f;
+ }
+ else if (bytes_sent < 64000)
+ {
+ bytes_weight = 1.f/32.f;
+ }
+ else
+ {
+ bytes_weight = 1.f/64.f;
+ }
+ bytes_weight *= bytes_weight;
+
+
+ //llinfos << "VS: " << virtual_size << llendl;
+ F32 virtual_size_factor = virtual_size / (10.f*10.f);
+
+ // The goal is for weighted priority to be <= 0 when we've reached a point where
+ // we've sent enough data.
+ //llinfos << "BytesSent: " << bytes_sent << llendl;
+ //llinfos << "BytesWeight: " << bytes_weight << llendl;
+ //llinfos << "PreLog: " << bytes_weight * virtual_size_factor << llendl;
+ w_priority = (F32)log10(bytes_weight * virtual_size_factor);
+
+ //llinfos << "PreScale: " << w_priority << llendl;
+
+ // We don't want to affect how MANY bytes we send based on the visible pixels, but the order
+ // in which they're sent. We post-multiply so we don't change the zero point.
+ if (w_priority > 0.f)
+ {
+ F32 pixel_weight = (F32)log10(visible_pixels + 1)*3.0f;
+ w_priority *= pixel_weight;
+ }
+
+ return w_priority;
+}