Index: ps/trunk/source/gui/SettingTypes/MouseEventMask.cpp =================================================================== --- ps/trunk/source/gui/SettingTypes/MouseEventMask.cpp (revision 25737) +++ ps/trunk/source/gui/SettingTypes/MouseEventMask.cpp (revision 25738) @@ -1,170 +1,179 @@ /* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "MouseEventMask.h" #include "gui/CGUISetting.h" #include "lib/tex/tex.h" #include "maths/Rect.h" #include "maths/Vector2D.h" #include "ps/Filesystem.h" +#include "ps/CacheLoader.h" #include "ps/CLogger.h" #include "ps/CStr.h" #include "scriptinterface/ScriptConversions.h" class IGUIObject; class IGUISetting; namespace { const std::string MOUSE_EVENT_MASK = "mouse_event_mask"; } class CGUIMouseEventMask::Impl { public: virtual ~Impl() = default; virtual bool IsMouseOver(const CVector2D& mousePos, const CRect& objectSize) const = 0; }; CGUIMouseEventMask::CGUIMouseEventMask(IGUIObject* owner) : IGUISetting(MOUSE_EVENT_MASK, owner) { } CGUIMouseEventMask::~CGUIMouseEventMask() { } void CGUIMouseEventMask::ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue Value) { Script::ToJSVal(rq, Value, m_Spec); } bool CGUIMouseEventMask::DoFromJSVal(const ScriptRequest& rq, JS::HandleValue value) { CStrW spec; if (!Script::FromJSVal(rq, value, spec)) return false; return DoFromString(spec); } CStr CGUIMouseEventMask::GetName() const { return MOUSE_EVENT_MASK; } bool CGUIMouseEventMask::IsMouseOver(const CVector2D& mousePos, const CRect& objectSize) const { if (m_Impl) return m_Impl->IsMouseOver(mousePos, objectSize); return false; } class CGUIMouseEventMaskTexture final : public CGUIMouseEventMask::Impl { public: static constexpr std::string_view identifier = "texture:"; static constexpr size_t specOffset = identifier.size(); static std::unique_ptr Create(const std::string& spec) { std::shared_ptr shapeFile; + CCacheLoader loader(g_VFS, L".dds"); + VfsPath sourcePath = VfsPath("art") / L"textures" / L"ui" / spec.substr(specOffset); + VfsPath archivePath = loader.ArchiveCachePath(sourcePath); + Status status; size_t size; - if (g_VFS->LoadFile(VfsPath("art") / L"textures" / L"ui" / spec.substr(specOffset), shapeFile, size) != INFO::OK) + if (loader.CanUseArchiveCache(sourcePath, archivePath)) + status = g_VFS->LoadFile(archivePath, shapeFile, size); + else + status = g_VFS->LoadFile(sourcePath, shapeFile, size); + if (status != INFO::OK) { LOGWARNING("Mouse event mask texture not found ('%s')", spec); return nullptr; } Tex tex; if (tex.decode(shapeFile, size) != INFO::OK) { LOGERROR("Could not decode mouse event mask texture '%s'", spec); return nullptr; } // TODO > would be nice to downscale, maybe. if (tex.m_Width == 0 || tex.m_Height == 0) { LOGERROR("Mouse event mask texture must have a non-null size ('%s')", spec); return nullptr; } auto mask = std::make_unique(); mask->m_Width = static_cast(tex.m_Width); mask->m_Height = static_cast(tex.m_Height); mask->m_Data.reserve(mask->m_Width * mask->m_Height); for (u8* ptr = tex.get_data(); ptr < tex.get_data() + tex.m_DataSize; ptr += tex.m_Bpp/8) { if (tex.m_Bpp == 32) mask->m_Data.push_back(*(ptr + 3) > 0); else mask->m_Data.push_back(*ptr > 0); } return mask; } virtual bool IsMouseOver(const CVector2D& mousePos, const CRect& objectSize) const override { if (m_Data.empty()) return false; CVector2D delta = mousePos - objectSize.TopLeft(); int x = floor(delta.X * m_Width / objectSize.GetWidth()); int y = floor(delta.Y * m_Height / objectSize.GetHeight()); if (x < 0 || y < 0 || x >= m_Width || y >= m_Height) return false; return m_Data[x + y * m_Width]; } private: // This uses the bool specialization on purpose for the 'compression' effect. std::vector m_Data; u16 m_Width; u16 m_Height; }; bool CGUIMouseEventMask::DoFromString(const CStrW& Value) { std::string spec = Value.ToUTF8(); if (spec == m_Spec) return true; // Empty spec - reset the mask and return. if (spec.empty()) { m_Impl.reset(); return true; } if (spec.find(CGUIMouseEventMaskTexture::identifier) != std::string::npos) { std::unique_ptr newImpl = CGUIMouseEventMaskTexture::Create(spec); if (newImpl) { m_Impl = std::move(newImpl); m_Spec = spec; return true; } else LOGERROR("Could not create shape for: %s", spec); } else LOGWARNING("Unknown clickable shape: %s", spec); return false; }