| // Copyright 2016 The SwiftShader Authors. All Rights Reserved. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //    http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #include "FrameBuffer.hpp" | 
 |  | 
 | #include "Renderer/Surface.hpp" | 
 | #include "Reactor/Reactor.hpp" | 
 | #include "Common/Timer.hpp" | 
 | #include "Common/Debug.hpp" | 
 |  | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include <time.h> | 
 |  | 
 | #define ASYNCHRONOUS_BLIT false   // FIXME: Currently leads to rare race conditions | 
 |  | 
 | namespace sw | 
 | { | 
 | 	extern bool forceWindowed; | 
 |  | 
 | 	FrameBuffer::Cursor FrameBuffer::cursor = {}; | 
 | 	bool FrameBuffer::topLeftOrigin = false; | 
 |  | 
 | 	FrameBuffer::FrameBuffer(int width, int height, bool fullscreen, bool topLeftOrigin) | 
 | 	{ | 
 | 		this->topLeftOrigin = topLeftOrigin; | 
 |  | 
 | 		framebuffer = nullptr; | 
 |  | 
 | 		this->width = width; | 
 | 		this->height = height; | 
 | 		format = FORMAT_X8R8G8B8; | 
 | 		stride = 0; | 
 |  | 
 | 		windowed = !fullscreen || forceWindowed; | 
 |  | 
 | 		blitFunction = nullptr; | 
 | 		blitRoutine = nullptr; | 
 | 		blitState = {}; | 
 |  | 
 | 		if(ASYNCHRONOUS_BLIT) | 
 | 		{ | 
 | 			terminate = false; | 
 | 			FrameBuffer *parameters = this; | 
 | 			blitThread = new Thread(threadFunction, ¶meters); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	FrameBuffer::~FrameBuffer() | 
 | 	{ | 
 | 		if(ASYNCHRONOUS_BLIT) | 
 | 		{ | 
 | 			terminate = true; | 
 | 			blitEvent.signal(); | 
 | 			blitThread->join(); | 
 | 			delete blitThread; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	void FrameBuffer::setCursorImage(sw::Surface *cursorImage) | 
 | 	{ | 
 | 		if(cursorImage) | 
 | 		{ | 
 | 			cursor.image = cursorImage->lockExternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC); | 
 | 			cursorImage->unlockExternal(); | 
 |  | 
 | 			cursor.width = cursorImage->getWidth(); | 
 | 			cursor.height = cursorImage->getHeight(); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			cursor.width = 0; | 
 | 			cursor.height = 0; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	void FrameBuffer::setCursorOrigin(int x0, int y0) | 
 | 	{ | 
 | 		cursor.hotspotX = x0; | 
 | 		cursor.hotspotY = y0; | 
 | 	} | 
 |  | 
 | 	void FrameBuffer::setCursorPosition(int x, int y) | 
 | 	{ | 
 | 		cursor.positionX = x; | 
 | 		cursor.positionY = y; | 
 | 	} | 
 |  | 
 | 	void FrameBuffer::copy(sw::Surface *source) | 
 | 	{ | 
 | 		if(!source) | 
 | 		{ | 
 | 			return; | 
 | 		} | 
 |  | 
 | 		if(!lock()) | 
 | 		{ | 
 | 			return; | 
 | 		} | 
 |  | 
 | 		int sourceStride = source->getInternalPitchB(); | 
 |  | 
 | 		updateState = {}; | 
 | 		updateState.width = width; | 
 | 		updateState.height = height; | 
 | 		updateState.destFormat = format; | 
 | 		updateState.destStride = stride; | 
 | 		updateState.sourceFormat = source->getInternalFormat(); | 
 | 		updateState.sourceStride = topLeftOrigin ? sourceStride : -sourceStride; | 
 | 		updateState.cursorWidth = cursor.width; | 
 | 		updateState.cursorHeight = cursor.height; | 
 |  | 
 | 		renderbuffer = source->lockInternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC); | 
 |  | 
 | 		if(!topLeftOrigin) | 
 | 		{ | 
 | 			renderbuffer = (byte*)renderbuffer + (height - 1) * sourceStride; | 
 | 		} | 
 |  | 
 | 		cursor.x = cursor.positionX - cursor.hotspotX; | 
 | 		cursor.y = cursor.positionY - cursor.hotspotY; | 
 |  | 
 | 		if(ASYNCHRONOUS_BLIT) | 
 | 		{ | 
 | 			blitEvent.signal(); | 
 | 			syncEvent.wait(); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			copyLocked(); | 
 | 		} | 
 |  | 
 | 		source->unlockInternal(); | 
 | 		unlock(); | 
 |  | 
 | 		profiler.nextFrame();   // Assumes every copy() is a full frame | 
 | 	} | 
 |  | 
 | 	void FrameBuffer::copyLocked() | 
 | 	{ | 
 | 		if(memcmp(&blitState, &updateState, sizeof(BlitState)) != 0) | 
 | 		{ | 
 | 			blitState = updateState; | 
 | 			blitRoutine = copyRoutine(blitState); | 
 | 			blitFunction = (void(*)(void*, void*, Cursor*))blitRoutine->getEntry(); | 
 | 		} | 
 |  | 
 | 		blitFunction(framebuffer, renderbuffer, &cursor); | 
 | 	} | 
 |  | 
 | 	std::shared_ptr<Routine> FrameBuffer::copyRoutine(const BlitState &state) | 
 | 	{ | 
 | 		const int width = state.width; | 
 | 		const int height = state.height; | 
 | 		const int dBytes = Surface::bytes(state.destFormat); | 
 | 		const int dStride = state.destStride; | 
 | 		const int sBytes = Surface::bytes(state.sourceFormat); | 
 | 		const int sStride = state.sourceStride; | 
 |  | 
 | 		Function<Void(Pointer<Byte>, Pointer<Byte>, Pointer<Byte>)> function; | 
 | 		{ | 
 | 			Pointer<Byte> dst(function.Arg<0>()); | 
 | 			Pointer<Byte> src(function.Arg<1>()); | 
 | 			Pointer<Byte> cursor(function.Arg<2>()); | 
 |  | 
 | 			For(Int y = 0, y < height, y++) | 
 | 			{ | 
 | 				Pointer<Byte> d = dst + y * dStride; | 
 | 				Pointer<Byte> s = src + y * sStride; | 
 |  | 
 | 				Int x0 = 0; | 
 |  | 
 | 				switch(state.destFormat) | 
 | 				{ | 
 | 				case FORMAT_X8R8G8B8: | 
 | 				case FORMAT_A8R8G8B8: | 
 | 					{ | 
 | 						Int x = x0; | 
 |  | 
 | 						switch(state.sourceFormat) | 
 | 						{ | 
 | 						case FORMAT_X8R8G8B8: | 
 | 						case FORMAT_A8R8G8B8: | 
 | 							For(, x < width - 3, x += 4) | 
 | 							{ | 
 | 								*Pointer<Int4>(d, 1) = *Pointer<Int4>(s, sStride % 16 ? 1 : 16); | 
 |  | 
 | 								s += 4 * sBytes; | 
 | 								d += 4 * dBytes; | 
 | 							} | 
 | 							break; | 
 | 						case FORMAT_X8B8G8R8: | 
 | 						case FORMAT_A8B8G8R8: | 
 | 							For(, x < width - 3, x += 4) | 
 | 							{ | 
 | 								Int4 bgra = *Pointer<Int4>(s, sStride % 16 ? 1 : 16); | 
 |  | 
 | 								*Pointer<Int4>(d, 1) = ((bgra & Int4(0x00FF0000)) >> 16) | | 
 | 								                       ((bgra & Int4(0x000000FF)) << 16) | | 
 | 								                       (bgra & Int4(0xFF00FF00)); | 
 |  | 
 | 								s += 4 * sBytes; | 
 | 								d += 4 * dBytes; | 
 | 							} | 
 | 							break; | 
 | 						case FORMAT_A16B16G16R16: | 
 | 							For(, x < width - 1, x += 2) | 
 | 							{ | 
 | 								Short4 c0 = As<UShort4>(Swizzle(*Pointer<Short4>(s + 0), 0x2103)) >> 8; | 
 | 								Short4 c1 = As<UShort4>(Swizzle(*Pointer<Short4>(s + 8), 0x2103)) >> 8; | 
 |  | 
 | 								*Pointer<Int2>(d) = As<Int2>(PackUnsigned(c0, c1)); | 
 |  | 
 | 								s += 2 * sBytes; | 
 | 								d += 2 * dBytes; | 
 | 							} | 
 | 							break; | 
 | 						case FORMAT_R5G6B5: | 
 | 							For(, x < width - 3, x += 4) | 
 | 							{ | 
 | 								Int4 rgb = Int4(*Pointer<Short4>(s)); | 
 |  | 
 | 								*Pointer<Int4>(d) = (((rgb & Int4(0xF800)) << 8) | ((rgb & Int4(0xE01F)) << 3)) | | 
 | 								                    (((rgb & Int4(0x07E0)) << 5) | ((rgb & Int4(0x0600)) >> 1)) | | 
 | 								                    (((rgb & Int4(0x001C)) >> 2) | Int4(0xFF000000)); | 
 |  | 
 | 								s += 4 * sBytes; | 
 | 								d += 4 * dBytes; | 
 | 							} | 
 | 							break; | 
 | 						default: | 
 | 							ASSERT(false); | 
 | 							break; | 
 | 						} | 
 |  | 
 | 						For(, x < width, x++) | 
 | 						{ | 
 | 							switch(state.sourceFormat) | 
 | 							{ | 
 | 							case FORMAT_X8R8G8B8: | 
 | 							case FORMAT_A8R8G8B8: | 
 | 								*Pointer<Int>(d) = *Pointer<Int>(s); | 
 | 								break; | 
 | 							case FORMAT_X8B8G8R8: | 
 | 							case FORMAT_A8B8G8R8: | 
 | 								{ | 
 | 									Int rgba = *Pointer<Int>(s); | 
 |  | 
 | 									*Pointer<Int>(d) = ((rgba & Int(0x00FF0000)) >> 16) | | 
 | 									                   ((rgba & Int(0x000000FF)) << 16) | | 
 | 									                   (rgba & Int(0xFF00FF00)); | 
 | 								} | 
 | 								break; | 
 | 							case FORMAT_A16B16G16R16: | 
 | 								{ | 
 | 									Short4 c = As<UShort4>(Swizzle(*Pointer<Short4>(s), 0x2103)) >> 8; | 
 |  | 
 | 									*Pointer<Int>(d) = Int(As<Int2>(PackUnsigned(c, c))); | 
 | 								} | 
 | 								break; | 
 | 							case FORMAT_R5G6B5: | 
 | 								{ | 
 | 									Int rgb = Int(*Pointer<Short>(s)); | 
 |  | 
 | 									*Pointer<Int>(d) = 0xFF000000 | | 
 | 									                   ((rgb & 0xF800) << 8) | ((rgb & 0xE01F) << 3) | | 
 | 								                       ((rgb & 0x07E0) << 5) | ((rgb & 0x0600) >> 1) | | 
 | 								                       ((rgb & 0x001C) >> 2); | 
 | 								} | 
 | 								break; | 
 | 							default: | 
 | 								ASSERT(false); | 
 | 								break; | 
 | 							} | 
 |  | 
 | 							s += sBytes; | 
 | 							d += dBytes; | 
 | 						} | 
 | 					} | 
 | 					break; | 
 | 				case FORMAT_X8B8G8R8: | 
 | 				case FORMAT_A8B8G8R8: | 
 | 				case FORMAT_SRGB8_X8: | 
 | 				case FORMAT_SRGB8_A8: | 
 | 					{ | 
 | 						Int x = x0; | 
 |  | 
 | 						switch(state.sourceFormat) | 
 | 						{ | 
 | 						case FORMAT_X8B8G8R8: | 
 | 						case FORMAT_A8B8G8R8: | 
 | 							For(, x < width - 3, x += 4) | 
 | 							{ | 
 | 								*Pointer<Int4>(d, 1) = *Pointer<Int4>(s, sStride % 16 ? 1 : 16); | 
 |  | 
 | 								s += 4 * sBytes; | 
 | 								d += 4 * dBytes; | 
 | 							} | 
 | 							break; | 
 | 						case FORMAT_X8R8G8B8: | 
 | 						case FORMAT_A8R8G8B8: | 
 | 							For(, x < width - 3, x += 4) | 
 | 							{ | 
 | 								Int4 bgra = *Pointer<Int4>(s, sStride % 16 ? 1 : 16); | 
 |  | 
 | 								*Pointer<Int4>(d, 1) = ((bgra & Int4(0x00FF0000)) >> 16) | | 
 | 								                       ((bgra & Int4(0x000000FF)) << 16) | | 
 | 								                       (bgra & Int4(0xFF00FF00)); | 
 |  | 
 | 								s += 4 * sBytes; | 
 | 								d += 4 * dBytes; | 
 | 							} | 
 | 							break; | 
 | 						case FORMAT_A16B16G16R16: | 
 | 							For(, x < width - 1, x += 2) | 
 | 							{ | 
 | 								Short4 c0 = *Pointer<UShort4>(s + 0) >> 8; | 
 | 								Short4 c1 = *Pointer<UShort4>(s + 8) >> 8; | 
 |  | 
 | 								*Pointer<Int2>(d) = As<Int2>(PackUnsigned(c0, c1)); | 
 |  | 
 | 								s += 2 * sBytes; | 
 | 								d += 2 * dBytes; | 
 | 							} | 
 | 							break; | 
 | 						case FORMAT_R5G6B5: | 
 | 							For(, x < width - 3, x += 4) | 
 | 							{ | 
 | 								Int4 rgb = Int4(*Pointer<Short4>(s)); | 
 |  | 
 | 								*Pointer<Int4>(d) = Int4(0xFF000000) | | 
 |                                                     (((rgb & Int4(0x001F)) << 19) | ((rgb & Int4(0x001C)) << 14)) | | 
 | 								                    (((rgb & Int4(0x07E0)) << 5) | ((rgb & Int4(0x0600)) >> 1)) | | 
 | 								                    (((rgb & Int4(0xF800)) >> 8) | ((rgb & Int4(0xE000)) >> 13)); | 
 |  | 
 | 								s += 4 * sBytes; | 
 | 								d += 4 * dBytes; | 
 | 							} | 
 | 							break; | 
 | 						default: | 
 | 							ASSERT(false); | 
 | 							break; | 
 | 						} | 
 |  | 
 | 						For(, x < width, x++) | 
 | 						{ | 
 | 							switch(state.sourceFormat) | 
 | 							{ | 
 | 							case FORMAT_X8B8G8R8: | 
 | 							case FORMAT_A8B8G8R8: | 
 | 								*Pointer<Int>(d) = *Pointer<Int>(s); | 
 | 								break; | 
 | 							case FORMAT_X8R8G8B8: | 
 | 							case FORMAT_A8R8G8B8: | 
 | 								{ | 
 | 									Int bgra = *Pointer<Int>(s); | 
 | 									*Pointer<Int>(d) = ((bgra & Int(0x00FF0000)) >> 16) | | 
 | 									                   ((bgra & Int(0x000000FF)) << 16) | | 
 | 									                   (bgra & Int(0xFF00FF00)); | 
 | 								} | 
 | 								break; | 
 | 							case FORMAT_A16B16G16R16: | 
 | 								{ | 
 | 									Short4 c = *Pointer<UShort4>(s) >> 8; | 
 |  | 
 | 									*Pointer<Int>(d) = Int(As<Int2>(PackUnsigned(c, c))); | 
 | 								} | 
 | 								break; | 
 | 							case FORMAT_R5G6B5: | 
 | 								{ | 
 | 									Int rgb = Int(*Pointer<Short>(s)); | 
 |  | 
 | 									*Pointer<Int>(d) = 0xFF000000 | | 
 | 									                   ((rgb & 0x001F) << 19) | ((rgb & 0x001C) << 14) | | 
 | 								                       ((rgb & 0x07E0) << 5) | ((rgb & 0x0600) >> 1) | | 
 | 								                       ((rgb & 0xF800) >> 8) | ((rgb & 0xE000) >> 13); | 
 | 								} | 
 | 								break; | 
 | 							default: | 
 | 								ASSERT(false); | 
 | 								break; | 
 | 							} | 
 |  | 
 | 							s += sBytes; | 
 | 							d += dBytes; | 
 | 						} | 
 | 					} | 
 | 					break; | 
 | 				case FORMAT_R8G8B8: | 
 | 					{ | 
 | 						For(Int x = x0, x < width, x++) | 
 | 						{ | 
 | 							switch(state.sourceFormat) | 
 | 							{ | 
 | 							case FORMAT_X8R8G8B8: | 
 | 							case FORMAT_A8R8G8B8: | 
 | 								*Pointer<Byte>(d + 0) = *Pointer<Byte>(s + 0); | 
 | 								*Pointer<Byte>(d + 1) = *Pointer<Byte>(s + 1); | 
 | 								*Pointer<Byte>(d + 2) = *Pointer<Byte>(s + 2); | 
 | 								break; | 
 | 							case FORMAT_X8B8G8R8: | 
 | 							case FORMAT_A8B8G8R8: | 
 | 								*Pointer<Byte>(d + 0) = *Pointer<Byte>(s + 2); | 
 | 								*Pointer<Byte>(d + 1) = *Pointer<Byte>(s + 1); | 
 | 								*Pointer<Byte>(d + 2) = *Pointer<Byte>(s + 0); | 
 | 								break; | 
 | 							case FORMAT_A16B16G16R16: | 
 | 								*Pointer<Byte>(d + 0) = *Pointer<Byte>(s + 5); | 
 | 								*Pointer<Byte>(d + 1) = *Pointer<Byte>(s + 3); | 
 | 								*Pointer<Byte>(d + 2) = *Pointer<Byte>(s + 1); | 
 | 								break; | 
 | 							case FORMAT_R5G6B5: | 
 | 								{ | 
 | 									Int rgb = Int(*Pointer<Short>(s)); | 
 |  | 
 | 									*Pointer<Byte>(d + 0) = Byte(((rgb & 0x001F) << 3) | ((rgb & 0x001C) >> 2)); | 
 | 									*Pointer<Byte>(d + 1) = Byte(((rgb & 0x07E0) << 5) | ((rgb & 0x0600) >> 1)); | 
 | 									*Pointer<Byte>(d + 2) = Byte(((rgb & 0xF800) << 8) | ((rgb & 0xE000) << 3)); | 
 | 								} | 
 | 								break; | 
 | 							default: | 
 | 								ASSERT(false); | 
 | 								break; | 
 | 							} | 
 |  | 
 | 							s += sBytes; | 
 | 							d += dBytes; | 
 | 						} | 
 | 					} | 
 | 					break; | 
 | 				case FORMAT_R5G6B5: | 
 | 					{ | 
 | 						For(Int x = x0, x < width, x++) | 
 | 						{ | 
 | 							switch(state.sourceFormat) | 
 | 							{ | 
 | 							case FORMAT_X8R8G8B8: | 
 | 							case FORMAT_A8R8G8B8: | 
 | 								{ | 
 | 									Int c = *Pointer<Int>(s); | 
 |  | 
 | 									*Pointer<Short>(d) = Short((c & 0x00F80000) >> 8 | | 
 | 									                           (c & 0x0000FC00) >> 5 | | 
 | 									                           (c & 0x000000F8) >> 3); | 
 | 								} | 
 | 								break; | 
 | 							case FORMAT_X8B8G8R8: | 
 | 							case FORMAT_A8B8G8R8: | 
 | 								{ | 
 | 									Int c = *Pointer<Int>(s); | 
 |  | 
 | 									*Pointer<Short>(d) = Short((c & 0x00F80000) >> 19 | | 
 | 									                           (c & 0x0000FC00) >> 5 | | 
 | 									                           (c & 0x000000F8) << 8); | 
 | 								} | 
 | 								break; | 
 | 							case FORMAT_A16B16G16R16: | 
 | 								{ | 
 | 									Short4 cc = *Pointer<UShort4>(s) >> 8; | 
 | 									Int c = Int(As<Int2>(PackUnsigned(cc, cc))); | 
 |  | 
 | 									*Pointer<Short>(d) = Short((c & 0x00F80000) >> 19 | | 
 | 									                           (c & 0x0000FC00) >> 5 | | 
 | 									                           (c & 0x000000F8) << 8); | 
 | 								} | 
 | 								break; | 
 | 							case FORMAT_R5G6B5: | 
 | 								*Pointer<Short>(d) = *Pointer<Short>(s); | 
 | 								break; | 
 | 							default: | 
 | 								ASSERT(false); | 
 | 								break; | 
 | 							} | 
 |  | 
 | 							s += sBytes; | 
 | 							d += dBytes; | 
 | 						} | 
 | 					} | 
 | 					break; | 
 | 				default: | 
 | 					ASSERT(false); | 
 | 					break; | 
 | 				} | 
 | 			} | 
 |  | 
 | 			if(state.cursorWidth > 0 && state.cursorHeight > 0) | 
 | 			{ | 
 | 				Int x0 = *Pointer<Int>(cursor + OFFSET(Cursor,x)); | 
 | 				Int y0 = *Pointer<Int>(cursor + OFFSET(Cursor,y)); | 
 |  | 
 | 				For(Int y1 = 0, y1 < state.cursorHeight, y1++) | 
 | 				{ | 
 | 					Int y = y0 + y1; | 
 |  | 
 | 					If(y >= 0 && y < height) | 
 | 					{ | 
 | 						Pointer<Byte> d = dst + y * dStride + x0 * dBytes; | 
 | 						Pointer<Byte> s = src + y * sStride + x0 * sBytes; | 
 | 						Pointer<Byte> c = *Pointer<Pointer<Byte>>(cursor + OFFSET(Cursor,image)) + y1 * state.cursorWidth * 4; | 
 |  | 
 | 						For(Int x1 = 0, x1 < state.cursorWidth, x1++) | 
 | 						{ | 
 | 							Int x = x0 + x1; | 
 |  | 
 | 							If(x >= 0 && x < width) | 
 | 							{ | 
 | 								blend(state, d, s, c); | 
 | 							} | 
 |  | 
 | 							c += 4; | 
 | 							s += sBytes; | 
 | 							d += dBytes; | 
 | 						} | 
 | 					} | 
 | 				} | 
 | 			} | 
 | 		} | 
 |  | 
 | 		return function("FrameBuffer"); | 
 | 	} | 
 |  | 
 | 	void FrameBuffer::blend(const BlitState &state, const Pointer<Byte> &d, const Pointer<Byte> &s, const Pointer<Byte> &c) | 
 | 	{ | 
 | 		Short4 c1; | 
 | 		Short4 c2; | 
 |  | 
 | 		c1 = Unpack(*Pointer<Byte4>(c)); | 
 |  | 
 | 		switch(state.sourceFormat) | 
 | 		{ | 
 | 		case FORMAT_X8R8G8B8: | 
 | 		case FORMAT_A8R8G8B8: | 
 | 			c2 = Unpack(*Pointer<Byte4>(s)); | 
 | 			break; | 
 | 		case FORMAT_X8B8G8R8: | 
 | 		case FORMAT_A8B8G8R8: | 
 | 			c2 = Swizzle(Unpack(*Pointer<Byte4>(s)), 0x2103); | 
 | 			break; | 
 | 		case FORMAT_A16B16G16R16: | 
 | 			c2 = Swizzle(*Pointer<Short4>(s), 0x2103); | 
 | 			break; | 
 | 		case FORMAT_R5G6B5: | 
 | 			{ | 
 | 				Int rgb(*Pointer<Short>(s)); | 
 | 				rgb = 0xFF000000 | | 
 | 				      ((rgb & 0xF800) << 8) | ((rgb & 0xE01F) << 3) | | 
 | 				      ((rgb & 0x07E0) << 5) | ((rgb & 0x0600) >> 1) | | 
 | 				      ((rgb & 0x001C) >> 2); | 
 | 				c2 = Unpack(As<Byte4>(rgb)); | 
 | 			} | 
 | 			break; | 
 | 		default: | 
 | 			ASSERT(false); | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		c1 = As<Short4>(As<UShort4>(c1) >> 9); | 
 | 		c2 = As<Short4>(As<UShort4>(c2) >> 9); | 
 |  | 
 | 		Short4 alpha = Swizzle(c1, 0x3333) & Short4(0xFFFFu, 0xFFFFu, 0xFFFFu, 0x0000); | 
 |  | 
 | 		c1 = (c1 - c2) * alpha; | 
 | 		c1 = c1 >> 7; | 
 | 		c1 = c1 + c2; | 
 | 		c1 = c1 + c1; | 
 |  | 
 | 		switch(state.destFormat) | 
 | 		{ | 
 | 		case FORMAT_X8R8G8B8: | 
 | 		case FORMAT_A8R8G8B8: | 
 | 			*Pointer<Byte4>(d) = Byte4(PackUnsigned(c1, c1)); | 
 | 			break; | 
 | 		case FORMAT_X8B8G8R8: | 
 | 		case FORMAT_A8B8G8R8: | 
 | 		case FORMAT_SRGB8_X8: | 
 | 		case FORMAT_SRGB8_A8: | 
 | 			{ | 
 | 				c1 = Swizzle(c1, 0x2103); | 
 |  | 
 | 				*Pointer<Byte4>(d) = Byte4(PackUnsigned(c1, c1)); | 
 | 			} | 
 | 			break; | 
 | 		case FORMAT_R8G8B8: | 
 | 			{ | 
 | 				Int c = Int(As<Int2>(PackUnsigned(c1, c1))); | 
 |  | 
 | 				*Pointer<Byte>(d + 0) = Byte(c >> 0); | 
 | 				*Pointer<Byte>(d + 1) = Byte(c >> 8); | 
 | 				*Pointer<Byte>(d + 2) = Byte(c >> 16); | 
 | 			} | 
 | 			break; | 
 | 		case FORMAT_R5G6B5: | 
 | 			{ | 
 | 				Int c = Int(As<Int2>(PackUnsigned(c1, c1))); | 
 |  | 
 | 				*Pointer<Short>(d) = Short((c & 0x00F80000) >> 8 | | 
 | 				                           (c & 0x0000FC00) >> 5 | | 
 | 				                           (c & 0x000000F8) >> 3); | 
 | 			} | 
 | 			break; | 
 | 		default: | 
 | 			ASSERT(false); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	void FrameBuffer::threadFunction(void *parameters) | 
 | 	{ | 
 | 		FrameBuffer *frameBuffer = *static_cast<FrameBuffer**>(parameters); | 
 |  | 
 | 		while(!frameBuffer->terminate) | 
 | 		{ | 
 | 			frameBuffer->blitEvent.wait(); | 
 |  | 
 | 			if(!frameBuffer->terminate) | 
 | 			{ | 
 | 				frameBuffer->copyLocked(); | 
 |  | 
 | 				frameBuffer->syncEvent.signal(); | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } |