/* * DdsFormat.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #include "StdInc.h" #include "DdsFormat.h" #include #include #include "../../../lib/filesystem/CInputStream.h" void DdsFormat::touchLRU(CacheEntry & e, const std::string & key) { lruList.erase(e.lruIt); lruList.push_front(key); e.lruIt = lruList.begin(); } void DdsFormat::evictIfNeeded() { while (currentDDSMemory > DDS_CACHE_MEMORY_CAP && !lruList.empty()) { const std::string &oldKey = lruList.back(); auto it = ddsCache.find(oldKey); if (it != ddsCache.end()) { currentDDSMemory -= it->second.data.memSize; lruList.pop_back(); ddsCache.erase(it); } else { lruList.pop_back(); } } } void DdsFormat::insertIntoCache(const std::string & key, const CachedDDS & cd) { auto it = ddsCache.find(key); if (it != ddsCache.end()) { currentDDSMemory -= it->second.data.memSize; lruList.erase(it->second.lruIt); ddsCache.erase(it); } lruList.push_front(key); CacheEntry entry; entry.data = cd; entry.lruIt = lruList.begin(); ddsCache.emplace(key, entry); currentDDSMemory += cd.memSize; evictIfNeeded(); } SDL_Surface * DdsFormat::load(CInputStream * stream, const std::string & cacheName, const Rect * rect) { const std::string key = cacheName; std::shared_ptr> rgba; uint32_t w = 0; uint32_t h = 0; // ---------- Check Cache First ---------- { std::lock_guard lock(cacheMutex); auto it = ddsCache.find(key); if (it != ddsCache.end()) { touchLRU(it->second, key); w = it->second.data.w; h = it->second.data.h; rgba = it->second.data.rgba; // Continue to rectangle extraction } } // Only decode DDS if cache miss if (!rgba) { uint32_t magic = 0; stream->read(reinterpret_cast(&magic), 4); if (magic != FOURCC('D','D','S',' ')) return nullptr; DDSHeader hdr{}; stream->read(reinterpret_cast(&hdr), sizeof(hdr)); w = hdr.width; h = hdr.height; uint32_t fourcc = hdr.pixel_format.fourCC; int squishFlags = 0; if (fourcc == FOURCC('D','X','T','1')) squishFlags = squish::kDxt1; else if (fourcc == FOURCC('D','X','T','5')) squishFlags = squish::kDxt5; else return nullptr; int blockBytes = (fourcc == FOURCC('D','X','T','1')) ? 8 : 16; int blocks = ((w + 3) / 4) * ((h + 3) / 4); int compressedSize = blocks * blockBytes; std::vector comp(compressedSize); stream->read(comp.data(), compressedSize); rgba = std::make_shared>(w * h * 4); squish::DecompressImage(rgba->data(), w, h, comp.data(), squishFlags); // Insert decoded DDS into cache { std::lock_guard lock(cacheMutex); CachedDDS cd; cd.w = w; cd.h = h; cd.rgba = rgba; cd.memSize = w * h * 4; insertIntoCache(key, cd); } } // ---------- Rectangle extraction ---------- int rx = 0; int ry = 0; int rw = static_cast(w); int rh = static_cast(h); if (rect) { rx = std::max(0, rect->x); ry = std::max(0, rect->y); rw = std::min(rect->w, static_cast(w) - rx); rh = std::min(rect->h, static_cast(h) - ry); if (rw <= 0 || rh <= 0) return nullptr; } SDL_Surface* surf = SDL_CreateRGBSurfaceWithFormat(0, rw, rh, 32, SDL_PIXELFORMAT_RGBA32); uint8_t* dst = static_cast(surf->pixels); for (int y = 0; y < rh; ++y) { const uint8_t* srcLine = rgba->data() + ((ry + y) * w + rx) * 4; std::copy_n(srcLine, rw * 4, dst + y * surf->pitch); } return surf; }