DdsFormat.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*
  2. * DdsFormat.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "DdsFormat.h"
  12. #include <SDL_surface.h>
  13. #include <squish.h>
  14. #include "../../../lib/filesystem/CInputStream.h"
  15. void DdsFormat::touchLRU(CacheEntry & e, const std::string & key)
  16. {
  17. lruList.erase(e.lruIt);
  18. lruList.push_front(key);
  19. e.lruIt = lruList.begin();
  20. }
  21. void DdsFormat::evictIfNeeded()
  22. {
  23. while (currentDDSMemory > DDS_CACHE_MEMORY_CAP && !lruList.empty())
  24. {
  25. const std::string &oldKey = lruList.back();
  26. auto it = ddsCache.find(oldKey);
  27. if (it != ddsCache.end())
  28. {
  29. currentDDSMemory -= it->second.data.memSize;
  30. lruList.pop_back();
  31. ddsCache.erase(it);
  32. }
  33. else
  34. {
  35. lruList.pop_back();
  36. }
  37. }
  38. }
  39. void DdsFormat::insertIntoCache(const std::string & key, const CachedDDS & cd)
  40. {
  41. auto it = ddsCache.find(key);
  42. if (it != ddsCache.end())
  43. {
  44. currentDDSMemory -= it->second.data.memSize;
  45. lruList.erase(it->second.lruIt);
  46. ddsCache.erase(it);
  47. }
  48. lruList.push_front(key);
  49. CacheEntry entry;
  50. entry.data = cd;
  51. entry.lruIt = lruList.begin();
  52. ddsCache.emplace(key, entry);
  53. currentDDSMemory += cd.memSize;
  54. evictIfNeeded();
  55. }
  56. SDL_Surface * DdsFormat::load(CInputStream * stream, const std::string & cacheName, const Rect * rect)
  57. {
  58. const std::string key = cacheName;
  59. std::shared_ptr<std::vector<uint8_t>> rgba;
  60. uint32_t w = 0;
  61. uint32_t h = 0;
  62. // ---------- Check Cache First ----------
  63. {
  64. std::lock_guard lock(cacheMutex);
  65. auto it = ddsCache.find(key);
  66. if (it != ddsCache.end())
  67. {
  68. touchLRU(it->second, key);
  69. w = it->second.data.w;
  70. h = it->second.data.h;
  71. rgba = it->second.data.rgba;
  72. // Continue to rectangle extraction
  73. }
  74. }
  75. // Only decode DDS if cache miss
  76. if (!rgba)
  77. {
  78. uint32_t magic = 0;
  79. stream->read(reinterpret_cast<ui8*>(&magic), 4);
  80. if (magic != FOURCC('D','D','S',' '))
  81. return nullptr;
  82. DDSHeader hdr{};
  83. stream->read(reinterpret_cast<ui8*>(&hdr), sizeof(hdr));
  84. w = hdr.width;
  85. h = hdr.height;
  86. uint32_t fourcc = hdr.pixel_format.fourCC;
  87. int squishFlags = 0;
  88. if (fourcc == FOURCC('D','X','T','1'))
  89. squishFlags = squish::kDxt1;
  90. else if (fourcc == FOURCC('D','X','T','5'))
  91. squishFlags = squish::kDxt5;
  92. else
  93. return nullptr;
  94. int blockBytes = (fourcc == FOURCC('D','X','T','1')) ? 8 : 16;
  95. int blocks = ((w + 3) / 4) * ((h + 3) / 4);
  96. int compressedSize = blocks * blockBytes;
  97. std::vector<uint8_t> comp(compressedSize);
  98. stream->read(comp.data(), compressedSize);
  99. rgba = std::make_shared<std::vector<uint8_t>>(w * h * 4);
  100. squish::DecompressImage(rgba->data(), w, h, comp.data(), squishFlags);
  101. // Insert decoded DDS into cache
  102. {
  103. std::lock_guard<std::mutex> lock(cacheMutex);
  104. CachedDDS cd;
  105. cd.w = w;
  106. cd.h = h;
  107. cd.rgba = rgba;
  108. cd.memSize = w * h * 4;
  109. insertIntoCache(key, cd);
  110. }
  111. }
  112. // ---------- Rectangle extraction ----------
  113. int rx = 0;
  114. int ry = 0;
  115. int rw = static_cast<int>(w);
  116. int rh = static_cast<int>(h);
  117. if (rect)
  118. {
  119. rx = std::max(0, rect->x);
  120. ry = std::max(0, rect->y);
  121. rw = std::min(rect->w, static_cast<int>(w) - rx);
  122. rh = std::min(rect->h, static_cast<int>(h) - ry);
  123. if (rw <= 0 || rh <= 0)
  124. return nullptr;
  125. }
  126. SDL_Surface* surf = SDL_CreateRGBSurfaceWithFormat(0, rw, rh, 32, SDL_PIXELFORMAT_RGBA32);
  127. uint8_t* dst = static_cast<uint8_t*>(surf->pixels);
  128. for (int y = 0; y < rh; ++y)
  129. {
  130. const uint8_t* srcLine = rgba->data() + ((ry + y) * w + rx) * 4;
  131. std::copy_n(srcLine, rw * 4, dst + y * surf->pitch);
  132. }
  133. return surf;
  134. }