示例#1
0
void GUIRenderer::UpdateDrawCallCache(DrawCalls& Calls, const CStr& SpriteName, const CRect& Size, int CellID, std::map<CStr, CGUISprite*>& Sprites)
{
	// This is called only when something has changed (like the size of the
	// sprite), so it doesn't need to be particularly efficient.

	// Clean up the old data
	Calls.clear();

	// If this object has zero size, there's nothing to render. (This happens
	// with e.g. tooltips that have zero size before they're first drawn, so
	// it isn't necessarily an error.)
	if (Size.left == Size.right && Size.top == Size.bottom)
		return;


	std::map<CStr, CGUISprite*>::iterator it(Sprites.find(SpriteName));
	if (it == Sprites.end())
	{
		/*
		 * Sprite not found. Check whether this a special sprite,
		 * and if so create a new sprite:
		 * "stretched:filename.ext" - stretched image
		 * "stretched:grayscale:filename.ext" - stretched grayscale image.
		 * "cropped:0.5, 0.25"    - stretch this ratio (x,y) of the top left of the image
		 * "color:r g b a"        - solid color
		 *     > "textureAsMask"  - when using color, use the (optional) texture alpha channel as mask.
		 * These can be combined, but they must be separated by a ":"
		 * so you can have a white overlay over an stretched grayscale image with:
		 * "grayscale:color:255 255 255 100:stretched:filename.ext"
		 */
		// Check that this can be a special sprite.
		if (SpriteName.ReverseFind(":") == -1 && SpriteName.Find("color(") == -1)
		{
			LOGERROR("Trying to use a sprite that doesn't exist (\"%s\").", SpriteName.c_str());
			return;
		}
		CGUISprite* Sprite = new CGUISprite;
		VfsPath TextureName = VfsPath("art/textures/ui") / wstring_from_utf8(SpriteName.AfterLast(":"));
		if (SpriteName.Find("stretched:") != -1)
		{
			// TODO: Should check (nicely) that this is a valid file?
			SGUIImage* Image = new SGUIImage;

			Image->m_TextureName = TextureName;
			// Allow grayscale images for disabled portraits
			if (SpriteName.Find("grayscale:") != -1)
			{
				Image->m_Effects = new SGUIImageEffects;
				Image->m_Effects->m_Greyscale = true;
			}

			CClientArea ca(CRect(0, 0, 0, 0), CRect(0, 0, 100, 100));
			Image->m_Size = ca;
			Image->m_TextureSize = ca;

			Sprite->AddImage(Image);

			Sprites[SpriteName] = Sprite;
		}
		else if (SpriteName.Find("cropped:") != -1)
		{
			// TODO: Should check (nicely) that this is a valid file?
			SGUIImage* Image = new SGUIImage;

			CStr info = SpriteName.AfterLast("cropped:").BeforeFirst(":");
			double xRatio = info.BeforeFirst(",").ToDouble();
			double yRatio = info.AfterLast(",").ToDouble();

			Image->m_TextureName = TextureName;

			CClientArea ca(CRect(0, 0, 0, 0), CRect(0, 0, 100, 100));
			CClientArea cb(CRect(0, 0, 0, 0), CRect(0, 0, 100/xRatio, 100/yRatio));
			Image->m_Size = ca;
			Image->m_TextureSize = cb;

			Sprite->AddImage(Image);

			Sprites[SpriteName] = Sprite;
		}
		if (SpriteName.Find("color:") != -1)
		{
			CStrW value = wstring_from_utf8(SpriteName.AfterLast("color:").BeforeFirst(":"));
			CColor color;

			// Check color is valid
			if (!GUI<CColor>::ParseString(value, color))
			{
				LOGERROR("GUI: Error parsing sprite 'color' (\"%s\")", utf8_from_wstring(value));
				return;
			}

			SGUIImage* Image = new SGUIImage;

			// If we are using a mask, this is an effect.
			// Otherwise we can fallback to the "back color" attribute
			// TODO: we are assuming there is a filename here.
			if (SpriteName.Find("textureAsMask:") != -1)
			{
				Image->m_TextureName = TextureName;
				Image->m_Effects = new SGUIImageEffects;
				Image->m_Effects->m_SolidColor = color;
			}
			else
				Image->m_BackColor = color;

			CClientArea ca(CRect(0, 0, 0, 0), CRect(0, 0, 100, 100));
			Image->m_Size = ca;
			Image->m_TextureSize = ca;

			Sprite->AddImage(Image);

			Sprites[SpriteName] = Sprite;
		}
		it = Sprites.find(SpriteName);

		// Otherwise, just complain and give up:
		if (it == Sprites.end())
		{
			SAFE_DELETE(Sprite);
			LOGERROR("Trying to use a sprite that doesn't exist (\"%s\").", SpriteName.c_str());
			return;
		}
	}

	Calls.reserve(it->second->m_Images.size());

	// Iterate through all the sprite's images, loading the texture and
	// calculating the texture coordinates
	std::vector<SGUIImage*>::const_iterator cit;
	for (cit = it->second->m_Images.begin(); cit != it->second->m_Images.end(); ++cit)
	{
		SDrawCall Call(*cit); // pointers are safe since we never modify sprites/images after startup

		CRect ObjectSize = (*cit)->m_Size.GetClientArea(Size);

		if (ObjectSize.GetWidth() == 0.0 || ObjectSize.GetHeight() == 0.0)
		{
			// Zero sized object. Don't report as an error, since it's common for e.g. hitpoint bars.
			continue; // i.e. don't continue with this image
		}

		Call.m_Vertices = ObjectSize;
		if ((*cit)->m_RoundCoordinates)
		{
			// Round the vertex coordinates to integers, to avoid ugly filtering artifacts
			Call.m_Vertices.left = (int)(Call.m_Vertices.left + 0.5f);
			Call.m_Vertices.right = (int)(Call.m_Vertices.right + 0.5f);
			Call.m_Vertices.top = (int)(Call.m_Vertices.top + 0.5f);
			Call.m_Vertices.bottom = (int)(Call.m_Vertices.bottom + 0.5f);
		}

		if (!(*cit)->m_TextureName.empty())
		{
			CTextureProperties textureProps(g_L10n.LocalizePath((*cit)->m_TextureName));
			textureProps.SetWrap((*cit)->m_WrapMode);
			CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
			texture->Prefetch();
			Call.m_HasTexture = true;
			Call.m_Texture = texture;

			Call.m_EnableBlending = false; // will be overridden if the texture has an alpha channel

			Call.m_ObjectSize = ObjectSize;
			Call.m_CellID = CellID;
		}
		else
		{
			Call.m_HasTexture = false;
			// Enable blending if it's transparent (allowing a little error in the calculations)
			Call.m_EnableBlending = !(fabs((*cit)->m_BackColor.a - 1.0f) < 0.0000001f);
		}

		Call.m_BackColor = (*cit)->m_BackColor;
		Call.m_BorderColor = (*cit)->m_Border ? (*cit)->m_BorderColor : CColor();
		Call.m_DeltaZ = (*cit)->m_DeltaZ;

		if (!Call.m_HasTexture)
		{
			Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid);
		}
		else if ((*cit)->m_Effects)
		{
			if ((*cit)->m_Effects->m_AddColor != CColor())
			{
				Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect(str_gui_add);
				Call.m_ShaderColorParameter = (*cit)->m_Effects->m_AddColor;
				// Always enable blending if something's being subtracted from
				// the alpha channel
				if ((*cit)->m_Effects->m_AddColor.a < 0.f)
					Call.m_EnableBlending = true;
			}
			else if ((*cit)->m_Effects->m_Greyscale)
			{
				Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect(str_gui_grayscale);
			}
			else if ((*cit)->m_Effects->m_SolidColor != CColor())
			{
				Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid_mask);
				Call.m_ShaderColorParameter = (*cit)->m_Effects->m_SolidColor;
				Call.m_EnableBlending = !(fabs((*cit)->m_Effects->m_SolidColor.a - 1.0f) < 0.0000001f);
			}
			else /* Slight confusion - why no effects? */
			{
				Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect(str_gui_basic);
			}
		}
		else
		{
			Call.m_Shader = g_Renderer.GetShaderManager().LoadEffect(str_gui_basic);
		}

		Calls.push_back(Call);
	}
}