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); } }