Commits

Vadim Savchuk  committed dc3bd12 Merge

Manual merge from default branch

  • Participants
  • Parent commits 3e28339, 7a739cb
  • Branches product-engine

Comments (0)

Files changed (545)

 0962101bfa7df0643a6e625786025fe7f8a6dc97 2-1-beta-2
 12769e547e30067d494a6c01479a18107366ce2f beta-5
 17fc2908e9a1ef34a9f53a41a393caf5c3cac390 beta-3-5
+19547b909b404552593be5ec7c18241e062a6d65 2-1-1-beta-1
 1e2b517adc2ecb342cd3c865f2a6ccf82a3cf8d7 2-1-beta-3
 3469d90a115b900f8f250e137bbd9b684130f5d2 beta-4
 3e4b947f79d88c385e8218cbc0731cef0e42cfc4 2-1-beta-1
 4f777ffb99fefdc6497c61385c22688ff149c659 viewer-2-0-0
 52d96ad3d39be29147c5b2181b3bb46af6164f0e alpha-3
 668851b2ef0f8cf8df07a0fba429e4a6c1e70abb viewer-2-0-1
+6e3b2e13906ba8ff22d3c8490b02d518adb2c907 2-1-1-beta-2
 7f16e79826d377f5f9f5b33dc721ab56d0d7dc8f alpha-4
 7f16e79826d377f5f9f5b33dc721ab56d0d7dc8f fork to viewer-20qa
 80bc6cff515118a36108967af49d3f8105c95bc9 viewer-2-0-2-start
 b03065d018b8a2e28b7de85b293a4c992cb4c12d 2-1-release
 b8419565906e4feb434426d8d9b17dd1458e24b2 alpha-6
+bb38ff1a763738609e1b3cada6d15fa61e5e84b9 2-1-1-release
 c6969fe44e58c542bfc6f1bd6c0be2fa860929ac 2-1-beta-4
 d2382d374139850efa5bb6adfb229e3e656cfc40 howard-demo
 d40ac9dd949cba6dab1cc386da6a2027690c2519 alpha-5

File indra/cmake/LLKDU.cmake

 
 if (INSTALL_PROPRIETARY AND NOT STANDALONE)
   use_prebuilt_binary(kdu)
-  if (EXISTS ${LIBS_CLOSED_DIR}/llkdu)
-    if (WINDOWS)
-      set(KDU_LIBRARY debug kdu_cored optimized kdu_core)
-    else (WINDOWS)
-      set(KDU_LIBRARY kdu)
-    endif (WINDOWS)
-
-    set(KDU_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include)
-
-    set(LLKDU_LIBRARY llkdu)
-    set(LLKDU_STATIC_LIBRARY llkdu_static)
-    set(LLKDU_LIBRARIES ${LLKDU_LIBRARY})
-    set(LLKDU_STATIC_LIBRARIES ${LLKDU_STATIC_LIBRARY})
-  endif (EXISTS ${LIBS_CLOSED_DIR}/llkdu)
+  set(LLKDU_LIBRARY llkdu)
 endif (INSTALL_PROPRIETARY AND NOT STANDALONE)

File indra/cmake/ViewerMiscLibs.cmake

   use_prebuilt_binary(fontconfig)
 endif(NOT STANDALONE)
 
+if(VIEWER AND NOT STANDALONE)
+  if(EXISTS ${CMAKE_SOURCE_DIR}/newview/res/have_artwork_bundle.marker)
+    message(STATUS "We seem to have an artwork bundle in the tree - brilliant.")
+  else(EXISTS ${CMAKE_SOURCE_DIR}/newview/res/have_artwork_bundle.marker)
+    message(FATAL_ERROR "Didn't find an artwork bundle - this needs to be downloaded separately and unpacked into this tree.  You can probably get it from the same place you got your viewer source.  Thanks!")
+  endif(EXISTS ${CMAKE_SOURCE_DIR}/newview/res/have_artwork_bundle.marker)
+endif(VIEWER AND NOT STANDALONE)

File indra/llcommon/lldarray.h

 		{
 			U32 n = mVector.size();
 			mIndexMap[k] = n;
-			mVector.resize(n+1);
+			mVector.push_back(Type());
 			llassert(mVector.size() == mIndexMap.size());
 			return mVector[n];
 		}

File indra/llcommon/llstrider.h

 	void setStride (S32 skipBytes)	{ mSkip = (skipBytes ? skipBytes : sizeof(Object));}
 
 	void skip(const U32 index)     { mBytep += mSkip*index;}
-
+	U32 getSkip() const			   { return mSkip; }
 	Object* get()                  { return mObjectp; }
 	Object* operator->()           { return mObjectp; }
 	Object& operator *()           { return *mObjectp; }

File indra/llcommon/llversionserver.h

 const S32 LL_VERSION_MAJOR = 2;
 const S32 LL_VERSION_MINOR = 1;
 const S32 LL_VERSION_PATCH = 0;
-const S32 LL_VERSION_BUILD = 0;
+const S32 LL_VERSION_BUILD = 13828;
 
 const char * const LL_CHANNEL = "Second Life Server";
 

File indra/llcrashlogger/llcrashlogger.cpp

 
 bool LLCrashLogger::init()
 {
+	LLCurl::initClass();
+
 	// We assume that all the logs we're looking for reside on the current drive
 	gDirUtilp->initAppDirs("SecondLife");
 

File indra/llinventory/llparcel.cpp

 };
 
 // NOTE: Adding parcel categories also requires updating:
-// * floater_directory.xml category combobox
 // * floater_about_land.xml category combobox
 // * Web site "create event" tools
 // DO NOT DELETE ITEMS FROM THIS LIST WITHOUT DEEPLY UNDERSTANDING WHAT YOU'RE DOING.

File indra/llmath/llvolume.cpp

 const F32 SCULPT_MIN_AREA = 0.002f;
 const S32 SCULPT_MIN_AREA_DETAIL = 1;
 
+#define GEN_TRI_STRIP 0
+
 BOOL check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm)
 {    
 	LLVector3 test = (pt2-pt1)%(pt3-pt2);
 	mGenerateSingleFace = generate_single_face;
 
 	generate();
-	if (mParams.getSculptID().isNull())
+	if (mParams.getSculptID().isNull() && params.getSculptType() == LL_SCULPT_TYPE_NONE)
 	{
 		createVolumeFaces();
 	}
 			LLProfile::Face& face = mProfilep->mFaces[i];
 			vf.mBeginS = face.mIndex;
 			vf.mNumS = face.mCount;
+			if (vf.mNumS < 0)
+			{
+				llerrs << "Volume face corruption detected." << llendl;
+			}
+
 			vf.mBeginT = 0;
 			vf.mNumT= getPath().mPath.size();
 			vf.mID = i;
 					if (face.mFlat && vf.mNumS > 2)
 					{ //flat inner faces have to copy vert normals
 						vf.mNumS = vf.mNumS*2;
+						if (vf.mNumS < 0)
+						{
+							llerrs << "Volume face corruption detected." << llendl;
+						}
 					}
 				}
 				else
 
 	if (!partial_build)
 	{
+#if GEN_TRI_STRIP
 		mTriStrip.clear();
+#endif
 		S32 idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0};
 		for(S32 gx = 0;gx<grid_size;gx++)
 		{
 						mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]);
 					}
 					
+#if GEN_TRI_STRIP
 					if (gy == 0)
 					{
 						mTriStrip.push_back((gx+1)*(grid_size+1));
 					{
 						mTriStrip.push_back(gy+1+gx*(grid_size+1));
 					}
+#endif
 				}
 				else
 				{
 						mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]);
 					}
 
+#if GEN_TRI_STRIP
 					if (gy == 0)
 					{
 						mTriStrip.push_back(gx*(grid_size+1));
 					{
 						mTriStrip.push_back(gy+1+(gx+1)*(grid_size+1));
 					}
+#endif
 				}
 			}
 			
 		}
 
+#if GEN_TRI_STRIP
 		if (mTriStrip.size()%2 == 1)
 		{
 			mTriStrip.push_back(mTriStrip[mTriStrip.size()-1]);
 		}
+#endif
 	}
 		
 	return TRUE;
 			mIndices[3*i+v2] = i + 1;
 		}
 
+#if GEN_TRI_STRIP
 		//make tri strip
 		if (mTypeMask & OPEN_MASK)
 		{
 				mTriStrip.push_back(mTriStrip[mTriStrip.size()-1]);
 			}
 		}
+#endif
 	}
 		
 	return TRUE;
 
 void LLVolumeFace::makeTriStrip()
 {
+#if GEN_TRI_STRIP
 	for (U32 i = 0; i < mIndices.size(); i+=3)
 	{
 		U16 i0 = mIndices[i];
 	{
 		mTriStrip.push_back(mTriStrip[mTriStrip.size()-1]);
 	}
+#endif
 }
 
 void LLVolumeFace::createBinormals()
 		mHasBinormals = FALSE;
 	}
 
-
-	LLVector3& face_min = mExtents[0];
-	LLVector3& face_max = mExtents[1];
-
-	mCenter.clearVec();
-
 	S32 begin_stex = llfloor( profile[mBeginS].mV[2] );
 	S32 num_s = ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) ? mNumS/2 : mNumS;
 
 		
 			mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
 			mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
-			
-			if (cur_vertex == 0)
-			{
-				face_min = face_max = mesh[i].mPos;
-			}
-			else
-			{
-				update_min_max(face_min, face_max, mesh[i].mPos);
-			}
 
 			cur_vertex++;
 
 			mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
 			mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
 
-			update_min_max(face_min,face_max,mesh[i].mPos);
-
 			cur_vertex++;
 		}
 	}
 	
+
+	//get bounding box for this side
+	LLVector3& face_min = mExtents[0];
+	LLVector3& face_max = mExtents[1];
+	mCenter.clearVec();
+
+	face_min = face_max = mVertices[0].mPosition;
+	for (U32 i = 1; i < mVertices.size(); ++i)
+	{
+		update_min_max(face_min, face_max, mVertices[i].mPosition);
+	}
+
 	mCenter = (face_min + face_max) * 0.5f;
 
 	S32 cur_index = 0;
 
 	if (!partial_build)
 	{
+#if GEN_TRI_STRIP
 		mTriStrip.clear();
+#endif
 
 		// Now we generate the indices.
 		for (t = 0; t < (mNumT-1); t++)
 		{
+#if GEN_TRI_STRIP
 			//prepend terminating index to strip
 			mTriStrip.push_back(mNumS*t);
+#endif
 
 			for (s = 0; s < (mNumS-1); s++)
 			{	
 				mIndices[cur_index++] = s+1 + mNumS*t;			//bottom right
 				mIndices[cur_index++] = s+1 + mNumS*(t+1);		//top right
 
+#if GEN_TRI_STRIP
 				if (s == 0)
 				{
 					mTriStrip.push_back(s+mNumS*t);
 				}
 				mTriStrip.push_back(s+1+mNumS*t);
 				mTriStrip.push_back(s+1+mNumS*(t+1));
+#endif
 				
 				mEdge[cur_edge++] = (mNumS-1)*2*t+s*2+1;						//bottom left/top right neighbor face 
 				if (t < mNumT-2) {												//top right/top left neighbor face 
 				}
 				mEdge[cur_edge++] = (mNumS-1)*2*t+s*2;							//top right/bottom left neighbor face	
 			}
+#if GEN_TRI_STRIP
 			//append terminating vertex to strip
 			mTriStrip.push_back(mNumS-1+mNumS*(t+1));
+#endif
 		}
 
+#if GEN_TRI_STRIP
 		if (mTriStrip.size()%2 == 1)
 		{
 			mTriStrip.push_back(mTriStrip[mTriStrip.size()-1]);
 		}
+#endif
 	}
 
 	//generate normals 
 	for (U32 i = 0; i < mIndices.size()/3; i++) //for each triangle
 	{
-		const S32 i0 = mIndices[i*3+0];
-		const S32 i1 = mIndices[i*3+1];
-		const S32 i2 = mIndices[i*3+2];
-		const VertexData& v0 = mVertices[i0];
-		const VertexData& v1 = mVertices[i1];
-		const VertexData& v2 = mVertices[i2];
+		const U16* idx = &(mIndices[i*3]);
+			
+		VertexData* v[] = 
+		{	&mVertices[idx[0]], &mVertices[idx[1]], &mVertices[idx[2]] };
 					
 		//calculate triangle normal
-		LLVector3 norm = (v0.mPosition-v1.mPosition) % (v0.mPosition-v2.mPosition);
-
-		for (U32 j = 0; j < 3; j++) 
-		{ //add triangle normal to vertices
-			const S32 idx = mIndices[i*3+j];
-			mVertices[idx].mNormal += norm; // * (weight_sum - d[j])/weight_sum;
-		}
+		LLVector3 norm = (v[0]->mPosition-v[1]->mPosition) % (v[0]->mPosition-v[2]->mPosition);
+
+		v[0]->mNormal += norm;
+		v[1]->mNormal += norm;
+		v[2]->mNormal += norm;
 
 		//even out quad contributions
-		if ((i & 1) == 0) 
-		{
-			mVertices[i2].mNormal += norm;
-		}
-		else 
-		{
-			mVertices[i1].mNormal += norm;
-		}
+		v[i%2+1]->mNormal += norm;
 	}
 	
 	// adjust normals based on wrapping and stitching

File indra/llmessage/llhttpclient.cpp

 	lldebugs << LLURLRequest::actionAsVerb(method) << " " << url << " "
 		<< headers << llendl;
 
-    // Insert custom headers is the caller sent any
-    if (headers.isMap())
-    {
+	// Insert custom headers if the caller sent any
+	if (headers.isMap())
+	{
+		if (headers.has("Cookie"))
+		{
+			req->allowCookies();
+		}
+
         LLSD::map_const_iterator iter = headers.beginMap();
         LLSD::map_const_iterator end  = headers.endMap();
 

File indra/llmessage/llpumpio.cpp

 	pump(DEFAULT_POLL_TIMEOUT);
 }
 
-static LLFastTimer::DeclareTimer FTM_PUMP("Pump");
+static LLFastTimer::DeclareTimer FTM_PUMP_IO("Pump IO");
 
 //timeout is in microseconds
 void LLPumpIO::pump(const S32& poll_timeout)
 {
 	LLMemType m1(LLMemType::MTYPE_IO_PUMP);
-	LLFastTimer t1(FTM_PUMP);
+	LLFastTimer t1(FTM_PUMP_IO);
 	//llinfos << "LLPumpIO::pump()" << llendl;
 
 	// Run any pending runners.
 	return true;
 }
 
+static LLFastTimer::DeclareTimer FTM_PUMP_CALLBACK_CHAIN("Chain");
+
 void LLPumpIO::callback()
 {
 	LLMemType m1(LLMemType::MTYPE_IO_PUMP);
 		callbacks_t::iterator end = mCallbacks.end();
 		for(; it != end; ++it)
 		{
+			LLFastTimer t(FTM_PUMP_CALLBACK_CHAIN);
 			// it's always the first and last time for respone chains
 			(*it).mHead = (*it).mChainLinks.begin();
 			(*it).mInit = true;

File indra/llmessage/llurlrequest.cpp

     mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, proxy);
 }
 
+void LLURLRequest::allowCookies()
+{
+	mDetail->mCurlRequest->setoptString(CURLOPT_COOKIEFILE, "");
+}
+
 // virtual
 LLIOPipe::EStatus LLURLRequest::handleError(
 	LLIOPipe::EStatus status,

File indra/llmessage/llurlrequest.h

      */
 	void useProxy(const std::string& proxy);
 
+	/**
+	 * @brief Turn on cookie handling for this request with CURLOPT_COOKIEFILE.
+	 */
+	void allowCookies();
+
 public:
 	/** 
 	 * @brief Give this pipe a chance to handle a generated error

File indra/llprimitive/llprimitive.h

 
 	void setLightTexture(const LLUUID& id) { mLightTexture = id; }
 	LLUUID getLightTexture() const         { return mLightTexture; }
+	bool isLightSpotlight() const         { return mLightTexture.notNull(); }
 	void setParams(const LLVector3& params) { mParams = params; }
 	LLVector3 getParams() const			   { return mParams; }
 	

File indra/llrender/llfontfreetype.cpp

 	}
 	else
 	{
-		gi = get_if_there(mCharGlyphInfoMap, (llwchar)0, (LLFontGlyphInfo*)NULL);
-		if (gi)
+		char_glyph_info_map_t::iterator found_it = mCharGlyphInfoMap.find((llwchar)0);
+		if (found_it != mCharGlyphInfoMap.end())
 		{
-			return gi->mXAdvance;
+			return found_it->second->mXAdvance;
 		}
 	}
 

File indra/llrender/llfontfreetype.h

 #ifndef LL_LLFONTFREETYPE_H
 #define LL_LLFONTFREETYPE_H
 
-#include <map>
+#include <boost/unordered_map.hpp>
 #include "llpointer.h"
 #include "llstl.h"
 
 
 	BOOL mValid;
 
-	typedef std::map<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t;
+	typedef boost::unordered_map<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t;
 	mutable char_glyph_info_map_t mCharGlyphInfoMap; // Information about glyph location in bitmap
 
 	mutable LLPointer<LLFontBitmapCache> mFontBitmapCachep;

File indra/llrender/llfontgl.cpp

 S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, 
 					 ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const
 {
+	LLFastTimer _(FTM_RENDER_FONTS);
+
 	if(!sDisplayFont) //do not display texts
 	{
 		return wstr.length() ;
 
 	gGL.loadUIIdentity();
 	
-	gGL.translateUI(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ);
+	//gGL.translateUI(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ);
 
 	// this code snaps the text origin to a pixel grid to start with
-	F32 pixel_offset_x = llround((F32)sCurOrigin.mX) - (sCurOrigin.mX);
-	F32 pixel_offset_y = llround((F32)sCurOrigin.mY) - (sCurOrigin.mY);
-	gGL.translateUI(-pixel_offset_x, -pixel_offset_y, 0.f);
+	//F32 pixel_offset_x = llround((F32)sCurOrigin.mX) - (sCurOrigin.mX);
+	//F32 pixel_offset_y = llround((F32)sCurOrigin.mY) - (sCurOrigin.mY);
+	//gGL.translateUI(-pixel_offset_x, -pixel_offset_y, 0.f);
 
-	LLFastTimer t(FTM_RENDER_FONTS);
+	LLVector2 origin(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY));
+	// snap the text origin to a pixel grid to start with
+	origin.mV[VX] -= llround((F32)sCurOrigin.mX) - (sCurOrigin.mX);
+	origin.mV[VY] -= llround((F32)sCurOrigin.mY) - (sCurOrigin.mY);
 
-	gGL.color4fv( color.mV );
 
 	S32 chars_drawn = 0;
 	S32 i;
  	// Not guaranteed to be set correctly
 	gGL.setSceneBlendType(LLRender::BT_ALPHA);
 	
-	cur_x = ((F32)x * sScaleX);
-	cur_y = ((F32)y * sScaleY);
+	cur_x = ((F32)x * sScaleX) + origin.mV[VX];
+	cur_y = ((F32)y * sScaleY) + origin.mV[VY];
 
 	// Offset y by vertical alignment.
 	switch (valign)
 
 	const LLFontGlyphInfo* next_glyph = NULL;
 
+	const S32 GLYPH_BATCH_SIZE = 30;
+	LLVector3 vertices[GLYPH_BATCH_SIZE * 4];
+	LLVector2 uvs[GLYPH_BATCH_SIZE * 4];
+	LLColor4U colors[GLYPH_BATCH_SIZE * 4];
+
+	LLColor4U text_color(color);
+
+	S32 bitmap_num = -1;
+	S32 glyph_count = 0;
 	for (i = begin_offset; i < begin_offset + length; i++)
 	{
 		llwchar wch = wstr[i];
 			break;
 		}
 		// Per-glyph bitmap texture.
-		LLImageGL *image_gl = mFontFreetype->getFontBitmapCache()->getImageGL(fgi->mBitmapNum);
-		gGL.getTexUnit(0)->bind(image_gl);
+		S32 next_bitmap_num = fgi->mBitmapNum;
+		if (next_bitmap_num != bitmap_num)
+		{
+			bitmap_num = next_bitmap_num;
+			LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num);
+			gGL.getTexUnit(0)->bind(font_image);
+		}
 	
 		if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth))
 		{
 				    llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth,
 				    llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight);
 		
-		drawGlyph(screen_rect, uv_rect, color, style_to_add, shadow, drop_shadow_strength);
+		if (glyph_count >= GLYPH_BATCH_SIZE)
+		{
+			gGL.begin(LLRender::QUADS);
+			{
+				gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
+			}
+			gGL.end();
+
+			glyph_count = 0;
+		}
+
+		drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style_to_add, shadow, drop_shadow_strength);
 
 		chars_drawn++;
 		cur_x += fgi->mXAdvance;
 		cur_render_y = cur_y;
 	}
 
+	gGL.begin(LLRender::QUADS);
+	{
+		gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
+	}
+	gGL.end();
+
+
 	if (right_x)
 	{
-		*right_x = cur_x / sScaleX;
+		*right_x = (cur_x - origin.mV[VX]) / sScaleX;
 	}
 
+	//FIXME: add underline as glyph?
 	if (style_to_add & UNDERLINE)
 	{
 		F32 descender = mFontFreetype->getDescenderHeight();
 		gGL.pushUIMatrix();
 		renderUTF8(std::string("..."), 
 				0,
-				cur_x / sScaleX, (F32)y,
+				(cur_x - origin.mV[VX]) / sScaleX, (F32)y,
 				color,
 				LEFT, valign,
 				style_to_add,
 	return *this;
 }
 
-void LLFontGL::renderQuad(const LLRectf& screen_rect, const LLRectf& uv_rect, F32 slant_amt) const
+void LLFontGL::renderQuad(LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, F32 slant_amt) const
 {
-	gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
-	gGL.vertex2f(llfont_round_x(screen_rect.mRight), 
-				llfont_round_y(screen_rect.mTop));
+	S32 index = 0;
 
-	gGL.texCoord2f(uv_rect.mLeft, uv_rect.mTop);
-	gGL.vertex2f(llfont_round_x(screen_rect.mLeft), 
-				llfont_round_y(screen_rect.mTop));
+	vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mRight), llfont_round_y(screen_rect.mTop), 0.f);
+	uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mTop);
+	colors_out[index] = color;
+	index++;
 
-	gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
-	gGL.vertex2f(llfont_round_x(screen_rect.mLeft + slant_amt), 
-				llfont_round_y(screen_rect.mBottom));
+	vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mLeft), llfont_round_y(screen_rect.mTop), 0.f);
+	uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mTop);
+	colors_out[index] = color;
+	index++;
 
-	gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom);
-	gGL.vertex2f(llfont_round_x(screen_rect.mRight + slant_amt), 
-				llfont_round_y(screen_rect.mBottom));
+	vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mLeft), llfont_round_y(screen_rect.mBottom), 0.f);
+	uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mBottom);
+	colors_out[index] = color;
+	index++;
+
+	vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mRight), llfont_round_y(screen_rect.mBottom), 0.f);
+	uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mBottom);
+	colors_out[index] = color;
 }
 
-void LLFontGL::drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4& color, U8 style, ShadowType shadow, F32 drop_shadow_strength) const
+void LLFontGL::drawGlyph(S32& glyph_count, LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, U8 style, ShadowType shadow, F32 drop_shadow_strength) const
 {
 	F32 slant_offset;
 	slant_offset = ((style & ITALIC) ? ( -mFontFreetype->getAscenderHeight() * 0.2f) : 0.f);
 
-	gGL.begin(LLRender::QUADS);
+	//FIXME: bold and drop shadow are mutually exclusive only for convenience
+	//Allow both when we need them.
+	if (style & BOLD)
 	{
-		//FIXME: bold and drop shadow are mutually exclusive only for convenience
-		//Allow both when we need them.
-		if (style & BOLD)
+		for (S32 pass = 0; pass < 2; pass++)
 		{
-			gGL.color4fv(color.mV);
-			for (S32 pass = 0; pass < 2; pass++)
+			LLRectf screen_rect_offset = screen_rect;
+
+			screen_rect_offset.translate((F32)(pass * BOLD_OFFSET), 0.f);
+			renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_offset, uv_rect, color, slant_offset);
+			glyph_count++;
+		}
+	}
+	else if (shadow == DROP_SHADOW_SOFT)
+	{
+		LLColor4U shadow_color = LLFontGL::sShadowColor;
+		shadow_color.mV[VALPHA] = U8(color.mV[VALPHA] * drop_shadow_strength * DROP_SHADOW_SOFT_STRENGTH);
+		for (S32 pass = 0; pass < 5; pass++)
+		{
+			LLRectf screen_rect_offset = screen_rect;
+
+			switch(pass)
 			{
-				LLRectf screen_rect_offset = screen_rect;
-
-				screen_rect_offset.translate((F32)(pass * BOLD_OFFSET), 0.f);
-				renderQuad(screen_rect_offset, uv_rect, slant_offset);
+			case 0:
+				screen_rect_offset.translate(-1.f, -1.f);
+				break;
+			case 1:
+				screen_rect_offset.translate(1.f, -1.f);
+				break;
+			case 2:
+				screen_rect_offset.translate(1.f, 1.f);
+				break;
+			case 3:
+				screen_rect_offset.translate(-1.f, 1.f);
+				break;
+			case 4:
+				screen_rect_offset.translate(0, -2.f);
+				break;
 			}
+		
+			renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_offset, uv_rect, shadow_color, slant_offset);
+			glyph_count++;
 		}
-		else if (shadow == DROP_SHADOW_SOFT)
-		{
-			LLColor4 shadow_color = LLFontGL::sShadowColor;
-			shadow_color.mV[VALPHA] = color.mV[VALPHA] * drop_shadow_strength * DROP_SHADOW_SOFT_STRENGTH;
-			gGL.color4fv(shadow_color.mV);
-			for (S32 pass = 0; pass < 5; pass++)
-			{
-				LLRectf screen_rect_offset = screen_rect;
-
-				switch(pass)
-				{
-				case 0:
-					screen_rect_offset.translate(-1.f, -1.f);
-					break;
-				case 1:
-					screen_rect_offset.translate(1.f, -1.f);
-					break;
-				case 2:
-					screen_rect_offset.translate(1.f, 1.f);
-					break;
-				case 3:
-					screen_rect_offset.translate(-1.f, 1.f);
-					break;
-				case 4:
-					screen_rect_offset.translate(0, -2.f);
-					break;
-				}
-			
-				renderQuad(screen_rect_offset, uv_rect, slant_offset);
-			}
-			gGL.color4fv(color.mV);
-			renderQuad(screen_rect, uv_rect, slant_offset);
-		}
-		else if (shadow == DROP_SHADOW)
-		{
-			LLColor4 shadow_color = LLFontGL::sShadowColor;
-			shadow_color.mV[VALPHA] = color.mV[VALPHA] * drop_shadow_strength;
-			gGL.color4fv(shadow_color.mV);
-			LLRectf screen_rect_shadow = screen_rect;
-			screen_rect_shadow.translate(1.f, -1.f);
-			renderQuad(screen_rect_shadow, uv_rect, slant_offset);
-			gGL.color4fv(color.mV);
-			renderQuad(screen_rect, uv_rect, slant_offset);
-		}
-		else // normal rendering
-		{
-			gGL.color4fv(color.mV);
-			renderQuad(screen_rect, uv_rect, slant_offset);
-		}
-
+		renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset);
+		glyph_count++;
 	}
-	gGL.end();
+	else if (shadow == DROP_SHADOW)
+	{
+		LLColor4U shadow_color = LLFontGL::sShadowColor;
+		shadow_color.mV[VALPHA] = U8(color.mV[VALPHA] * drop_shadow_strength);
+		LLRectf screen_rect_shadow = screen_rect;
+		screen_rect_shadow.translate(1.f, -1.f);
+		renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_shadow, uv_rect, shadow_color, slant_offset);
+		glyph_count++;
+		renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset);
+		glyph_count++;
+	}
+	else // normal rendering
+	{
+		renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset);
+		glyph_count++;
+	}
 }

File indra/llrender/llfontgl.h

 	LLFontDescriptor mFontDescriptor;
 	LLPointer<LLFontFreetype> mFontFreetype;
 
-	void renderQuad(const LLRectf& screen_rect, const LLRectf& uv_rect, F32 slant_amt) const;
-	void drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4& color, U8 style, ShadowType shadow, F32 drop_shadow_fade) const;
+	void renderQuad(LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, F32 slant_amt) const;
+	void drawGlyph(S32& glyph_count, LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, U8 style, ShadowType shadow, F32 drop_shadow_fade) const;
 
 	// Registry holds all instantiated fonts.
 	static LLFontRegistry* sFontRegistry;

File indra/llrender/llgl.cpp

 // GL_EXT_framebuffer_blit
 PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebufferEXT = NULL;
 
+// GL_EXT_blend_func_separate
+PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT = NULL;
+
 // GL_ARB_draw_buffers
 PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB = NULL;
 
 	mHasCompressedTextures(FALSE),
 	mHasFramebufferObject(FALSE),
 	mHasFramebufferMultisample(FALSE),
+	mHasBlendFuncSeparate(FALSE),
 
 	mHasVertexBufferObject(FALSE),
 	mHasPBuffer(FALSE),
 
 	mHasSeparateSpecularColor(FALSE),
 
+	mDebugGPU(FALSE),
+
 	mDriverVersionMajor(1),
 	mDriverVersionMinor(0),
 	mDriverVersionRelease(0),
 		return false;
 	}
 	
+	setToDebugGPU();
 
 	initGLStates();
 	return true;
 }
 
+void LLGLManager::setToDebugGPU()
+{
+	//"MOBILE INTEL(R) 965 EXPRESS CHIP", 
+	if (mGLRenderer.find("INTEL") != std::string::npos && mGLRenderer.find("965") != std::string::npos)
+	{
+		mDebugGPU = TRUE ;
+	}
+
+	return ;
+}
+
 void LLGLManager::getGLInfo(LLSD& info)
 {
 	info["GLInfo"]["GLVendor"] = std::string((const char *)glGetString(GL_VENDOR));
 #else
 	mHasDrawBuffers = FALSE;
 # endif
+# if GL_EXT_blend_func_separate
+	mHasBlendFuncSeparate = TRUE;
+#else
+	mHasBlendFuncSeparate = FALSE;
+# endif
 	mHasMipMapGeneration = FALSE;
 	mHasSeparateSpecularColor = FALSE;
 	mHasAnisotropic = FALSE;
 		&& ExtensionExists("GL_EXT_packed_depth_stencil", gGLHExts.mSysExts);
 	mHasFramebufferMultisample = mHasFramebufferObject && ExtensionExists("GL_EXT_framebuffer_multisample", gGLHExts.mSysExts);
 	mHasDrawBuffers = ExtensionExists("GL_ARB_draw_buffers", gGLHExts.mSysExts);
+	mHasBlendFuncSeparate = ExtensionExists("GL_EXT_blend_func_separate", gGLHExts.mSysExts);
 	mHasTextureRectangle = ExtensionExists("GL_ARB_texture_rectangle", gGLHExts.mSysExts);
 #if !LL_DARWIN
 	mHasPointParameters = !mIsATI && ExtensionExists("GL_ARB_point_parameters", gGLHExts.mSysExts);
 		mHasFramebufferObject = FALSE;
 		mHasFramebufferMultisample = FALSE;
 		mHasDrawBuffers = FALSE;
+		mHasBlendFuncSeparate = FALSE;
 		mHasMipMapGeneration = FALSE;
 		mHasSeparateSpecularColor = FALSE;
 		mHasAnisotropic = FALSE;
 		mHasShaderObjects = FALSE;
 		mHasVertexShader = FALSE;
 		mHasFragmentShader = FALSE;
+		mHasBlendFuncSeparate = FALSE;
 		LL_WARNS("RenderInit") << "GL extension support forced to SIMPLE level via LL_GL_BASICEXT" << LL_ENDL;
 	}
 	if (getenv("LL_GL_BLACKLIST"))	/* Flawfinder: ignore */
 		if (strchr(blacklist,'r')) mHasDrawBuffers = FALSE;//S
 		if (strchr(blacklist,'s')) mHasFramebufferMultisample = FALSE;
 		if (strchr(blacklist,'t')) mHasTextureRectangle = FALSE;
-
+		if (strchr(blacklist,'u')) mHasBlendFuncSeparate = FALSE;//S
+		
 	}
 #endif // LL_LINUX || LL_SOLARIS
 	
 	{
 		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_fragment_shader" << LL_ENDL;
 	}
+	if (!mHasBlendFuncSeparate)
+	{
+		LL_INFOS("RenderInit") << "Couldn't initialize GL_EXT_blend_func_separate" << LL_ENDL;
+	}
+	if (!mHasDrawBuffers)
+	{
+		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_draw_buffers" << LL_ENDL;
+	}
 
 	// Disable certain things due to known bugs
 	if (mIsIntel && mHasMipMapGeneration)
 	{
 		glDrawBuffersARB = (PFNGLDRAWBUFFERSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDrawBuffersARB");
 	}
+	if (mHasBlendFuncSeparate)
+	{
+		glBlendFuncSeparateEXT = (PFNGLBLENDFUNCSEPARATEEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glBlendFuncSeparateEXT");
+	}
 #if (!LL_LINUX && !LL_SOLARIS) || LL_LINUX_NV_GL_HEADERS
 	// This is expected to be a static symbol on Linux GL implementations, except if we use the nvidia headers - bah
 	glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElements");
 	glGetError();
 }
 
-void assert_glerror()
+void do_assert_glerror()
 {
-	if (!gGLActive)
-	{
-		//llwarns << "GL used while not active!" << llendl;
-
-		if (gDebugSession)
-		{
-			//ll_fail("GL used while not active");
-		}
-	}
-
-	if (gNoRender || !gDebugGL) 
-	{
-		return;
-	}
-	
-	if (!gGLManager.mInited)
+	if (LL_UNLIKELY(!gGLManager.mInited))
 	{
 		LL_ERRS("RenderInit") << "GL not initialized" << LL_ENDL;
 	}
 	GLenum error;
 	error = glGetError();
 	BOOL quit = FALSE;
-	while (error)
+	while (LL_UNLIKELY(error))
 	{
 		quit = TRUE;
-#ifndef LL_LINUX // *FIX: !  This should be an error for linux as well.
 		GLubyte const * gl_error_msg = gluErrorString(error);
 		if (NULL != gl_error_msg)
 		{
 			}
 		}
 		error = glGetError();
-#endif
 	}
 
 	if (quit)
 	}
 }
 
+void assert_glerror()
+{
+	if (!gGLActive)
+	{
+		//llwarns << "GL used while not active!" << llendl;
+
+		if (gDebugSession)
+		{
+			//ll_fail("GL used while not active");
+		}
+	}
+
+	if (!gNoRender && gDebugGL) 
+	{
+		do_assert_glerror();
+	}
+}
+	
+
 void clear_glerror()
 {
 	//  Create or update texture to be used with this data 
 //
 
 // Static members
-std::map<LLGLenum, LLGLboolean> LLGLState::sStateMap;
+boost::unordered_map<LLGLenum, LLGLboolean> LLGLState::sStateMap;
 
 GLboolean LLGLDepthTest::sDepthEnabled = GL_FALSE; // OpenGL default
 GLenum LLGLDepthTest::sDepthFunc = GL_LESS; // OpenGL default
 void LLGLState::dumpStates() 
 {
 	LL_INFOS("RenderState") << "GL States:" << LL_ENDL;
-	for (std::map<LLGLenum, LLGLboolean>::iterator iter = sStateMap.begin();
+	for (boost::unordered_map<LLGLenum, LLGLboolean>::iterator iter = sStateMap.begin();
 		 iter != sStateMap.end(); ++iter)
 	{
 		LL_INFOS("RenderState") << llformat(" 0x%04x : %s",(S32)iter->first,iter->second?"TRUE":"FALSE") << LL_ENDL;
 		}
 	}
 	
-	for (std::map<LLGLenum, LLGLboolean>::iterator iter = sStateMap.begin();
+	for (boost::unordered_map<LLGLenum, LLGLboolean>::iterator iter = sStateMap.begin();
 		 iter != sStateMap.end(); ++iter)
 	{
 		LLGLenum state = iter->first;

File indra/llrender/llgl.h

 // This file contains various stuff for handling gl extensions and other gl related stuff.
 
 #include <string>
-#include <map>
+#include <boost/unordered_map.hpp>
 #include <list>
 
 #include "llerror.h"
 	BOOL mHasCompressedTextures;
 	BOOL mHasFramebufferObject;
 	BOOL mHasFramebufferMultisample;
+	BOOL mHasBlendFuncSeparate;
 	
 	// ARB Extensions
 	BOOL mHasVertexBufferObject;
 
 	// Misc extensions
 	BOOL mHasSeparateSpecularColor;
+
+	//whether this GPU is in the debug list.
+	BOOL mDebugGPU;
 	
 	S32 mDriverVersionMajor;
 	S32 mDriverVersionMinor;
 	void initExtensions();
 	void initGLStates();
 	void initGLImages();
+	void setToDebugGPU();
 };
 
 extern LLGLManager gGLManager;
 	static void checkClientArrays(const std::string& msg = "", U32 data_mask = 0x0001);
 	
 protected:
-	static std::map<LLGLenum, LLGLboolean> sStateMap;
+	static boost::unordered_map<LLGLenum, LLGLboolean> sStateMap;
 	
 public:
 	enum { CURRENT_STATE = -2 };

File indra/llrender/llglheaders.h

 
 extern PFNGLCOLORTABLEEXTPROC glColorTableEXT;
 
+//GL_EXT_blend_func_separate
+extern PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT;
+
 //GL_EXT_framebuffer_object
 extern PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT;
 extern PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT;
 # include "GL/glh_extensions.h"
 # undef __APPLE__
 
-#elif LL_LINUX 
+#elif LL_LINUX
+//----------------------------------------------------------------------------
+// LL_LINUX
+
 //----------------------------------------------------------------------------
 // Linux, MESA headers, but not necessarily assuming MESA runtime.
 // quotes so we get libraries/.../GL/ version
 # define LL_LINUX_NV_GL_HEADERS 0
 #endif // LL_LINUX && defined(WINGDIAPI)
 
+
 #if LL_LINUX_NV_GL_HEADERS
 // Missing functions when using nvidia headers:
 extern PFNGLACTIVETEXTUREARBPROC	glActiveTextureARB;
 extern PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glCompressedTexImage2DARB;
 extern PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImageARB;
 
+//GL_EXT_blend_func_separate
+extern PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT;
+
 //GL_EXT_framebuffer_object
 extern PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT;
 extern PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT;
 //GL_ARB_draw_buffers
 extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB;
 
+
 #elif LL_WINDOWS
+//----------------------------------------------------------------------------
+// LL_WINDOWS
 
 // windows gl headers depend on things like APIENTRY, so include windows.
 #define WIN32_LEAN_AND_MEAN
 extern PFNGLGETACTIVEATTRIBARBPROC glGetActiveAttribARB;
 extern PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB;
 
+//GL_EXT_blend_func_separate
+extern PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT;
+
 //GL_EXT_framebuffer_object
 extern PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT;
 extern PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT;
 //GL_ARB_draw_buffers
 extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB;
 
+
 #elif LL_DARWIN
 //----------------------------------------------------------------------------
 // LL_DARWIN
 // Note that they also must not be called on 10.3.9.  This should be taken care of by a runtime check for the existence of the GL extension.
 #include <AvailabilityMacros.h>
 
+//GL_EXT_blend_func_separate
+extern void glBlendFuncSeparateEXT(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;
+
 // GL_EXT_framebuffer_object
 extern GLboolean glIsRenderbufferEXT(GLuint renderbuffer) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;
 extern void glBindRenderbufferEXT(GLenum target, GLuint renderbuffer) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;

File indra/llrender/llimagegl.cpp

 			if (gDebugSession)
 			{
 				gFailLog << "wrong texture size and discard level!" << 
-					mWidth << " Height: " << mHeight << " Current Level: " << mCurrentDiscardLevel << std::endl;
+					mWidth << " Height: " << mHeight << " Current Level: " << (S32)mCurrentDiscardLevel << std::endl;
 			}
 			else
 			{
 				llerrs << "wrong texture size and discard level: width: " << 
-					mWidth << " Height: " << mHeight << " Current Level: " << mCurrentDiscardLevel << llendl ;
+					mWidth << " Height: " << mHeight << " Current Level: " << (S32)mCurrentDiscardLevel << llendl ;
 			}
 		}
 
 {
 	if (gGL.getTexUnit(0)->bind(this, false, true))
 	{
-		checkTexSize(true) ;
-		llcallstacks << fb_x << " : " << fb_y << " : " << x_pos << " : " << y_pos << " : " << width << " : " << height << llcallstacksendl ;
+		if(gGLManager.mDebugGPU)
+		{
+			llinfos << "Calling glCopyTexSubImage2D(...)" << llendl ;
+			checkTexSize(true) ;
+			llcallstacks << fb_x << " : " << fb_y << " : " << x_pos << " : " << y_pos << " : " << width << " : " << height << llcallstacksendl ;
+		}
 
 		glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width, height);
 		mGLTextureCreated = true;
 	}
 }
 
-void LLImageGL::analyzeAlpha(const void* data_in, S32 w, S32 h)
+void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h)
 {
 	if(!mNeedsAlphaAndPickMask)
 	{
 	}
 
 	U32 length = w * h;
-	const GLubyte* current = ((const GLubyte*) data_in) + mAlphaOffset ;
+	U32 alphatotal = 0;
 	
-	S32 sample[16];
-	memset(sample, 0, sizeof(S32)*16);
+	U32 sample[16];
+	memset(sample, 0, sizeof(U32)*16);
 
-	for (U32 i = 0; i < length; i++)
+	// generate histogram of quantized alpha.
+	// also add-in the histogram of a 2x2 box-sampled version.  The idea is
+	// this will mid-skew the data (and thus increase the chances of not
+	// being used as a mask) from high-frequency alpha maps which
+	// suffer the worst from aliasing when used as alpha masks.
+	if (w >= 2 && h >= 2)
 	{
-		++sample[*current/16];
-		current += mAlphaStride ;
+		llassert(w%2 == 0);
+		llassert(h%2 == 0);
+		const GLubyte* rowstart = ((const GLubyte*) data_in) + mAlphaOffset;
+		for (U32 y = 0; y < h; y+=2)
+		{
+			const GLubyte* current = rowstart;
+			for (U32 x = 0; x < w; x+=2)
+			{
+				const U32 s1 = current[0];
+				alphatotal += s1;
+				const U32 s2 = current[w * mAlphaStride];
+				alphatotal += s2;
+				current += mAlphaStride;
+				const U32 s3 = current[0];
+				alphatotal += s3;
+				const U32 s4 = current[w * mAlphaStride];
+				alphatotal += s4;
+				current += mAlphaStride;
+
+				++sample[s1/16];
+				++sample[s2/16];
+				++sample[s3/16];
+				++sample[s4/16];
+
+				const U32 asum = (s1+s2+s3+s4);
+				alphatotal += asum;
+				sample[asum/(16*4)] += 4;
+			}
+			
+			rowstart += 2 * w * mAlphaStride;
+		}
+		length *= 2; // we sampled everything twice, essentially
+	}
+	else
+	{
+		const GLubyte* current = ((const GLubyte*) data_in) + mAlphaOffset;
+		for (U32 i = 0; i < length; i++)
+		{
+			const U32 s1 = *current;
+			alphatotal += s1;
+			++sample[s1/16];
+			current += mAlphaStride;
+		}
+	}
+	
+	// if more than 1/16th of alpha samples are mid-range, this
+	// shouldn't be treated as a 1-bit mask
+
+	// also, if all of the alpha samples are clumped on one half
+	// of the range (but not at an absolute extreme), then consider
+	// this to be an intentional effect and don't treat as a mask.
+
+	U32 midrangetotal = 0;
+	for (U32 i = 4; i < 11; i++)
+	{
+		midrangetotal += sample[i];
+	}