Clone of Bael'Zharon's Respite @ https://github.com/boardwalk/bzr

Image.cpp 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*
  2. * Bael'Zharon's Respite
  3. * Copyright (C) 2014 Daniel Skorupski
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. */
  18. #include "Image.h"
  19. #include "resource/Palette.h"
  20. #include <algorithm>
  21. int bitsPerPixel(PixelFormat format)
  22. {
  23. switch(format)
  24. {
  25. case PixelFormat::kR8G8B8:
  26. return 24;
  27. case PixelFormat::kA8R8G8B8:
  28. return 32;
  29. case PixelFormat::kR5G6B5:
  30. return 16;
  31. case PixelFormat::kA4R4G4B4:
  32. return 16;
  33. case PixelFormat::kA8:
  34. return 8;
  35. case PixelFormat::kP8:
  36. return 8;
  37. case PixelFormat::kIndex16:
  38. return 16;
  39. case PixelFormat::kCustomLscapeR8G8B8:
  40. return 24;
  41. case PixelFormat::kCustomLscapeAlpha:
  42. return 8;
  43. case PixelFormat::kDXT1:
  44. return 4;
  45. case PixelFormat::kDXT3:
  46. return 8;
  47. case PixelFormat::kDXT5:
  48. return 8;
  49. default:
  50. break;
  51. }
  52. throw runtime_error("Invalid format");
  53. }
  54. bool isPaletted(PixelFormat format)
  55. {
  56. return format == PixelFormat::kP8 || format == PixelFormat::kIndex16;
  57. }
  58. bool isCompressed(PixelFormat format)
  59. {
  60. return format == PixelFormat::kDXT1 || format == PixelFormat::kDXT3 || format == PixelFormat::kDXT5;
  61. }
  62. bool hasAlpha(PixelFormat format)
  63. {
  64. return format == PixelFormat::kA8R8G8B8 || format == PixelFormat::kP8 || format == PixelFormat::kIndex16 ||
  65. format == PixelFormat::kCustomLscapeAlpha || format == PixelFormat::kDXT3 || format == PixelFormat::kDXT5;
  66. }
  67. Image::Image() : format_(PixelFormat::kUnknown), width_(0), height_(0), hasAlpha_(false)
  68. {}
  69. void Image::init(PixelFormat newFormat, int newWidth, int newHeight, const void* newData)
  70. {
  71. format_ = newFormat;
  72. width_ = newWidth;
  73. height_ = newHeight;
  74. if(newData == nullptr)
  75. {
  76. data_.clear();
  77. data_.resize(width_ * height_ * bitsPerPixel(format_) / 8);
  78. }
  79. else
  80. {
  81. data_.assign((const uint8_t*)newData, (const uint8_t*)newData + width_ * height_ * bitsPerPixel(format_) / 8);
  82. }
  83. updateHasAlpha();
  84. }
  85. template<class T>
  86. void Image::applyPalette(const Palette& palette)
  87. {
  88. vector<uint8_t> newData(width_ * height_ * 4);
  89. const T* input = reinterpret_cast<const T*>(data_.data());
  90. const T* inputEnd = input + width_ * height_;
  91. uint8_t* output = newData.data();
  92. while(input < inputEnd)
  93. {
  94. T paletteIndex = *input & (palette.colors.size() - 1);
  95. Palette::Color color = palette.colors[paletteIndex];
  96. *output++ = color.blue;
  97. *output++ = color.green;
  98. *output++ = color.red;
  99. *output++ = color.alpha;
  100. input++;
  101. }
  102. data_ = move(newData);
  103. format_ = PixelFormat::kA8R8G8B8;
  104. updateHasAlpha();
  105. }
  106. void Image::applyPalette(const Palette& palette)
  107. {
  108. if(format_ == PixelFormat::kP8)
  109. {
  110. applyPalette<uint8_t>(palette);
  111. }
  112. else if(format_ == PixelFormat::kIndex16)
  113. {
  114. applyPalette<uint16_t>(palette);
  115. }
  116. else
  117. {
  118. throw runtime_error("Cannot apply palette to this format");
  119. }
  120. }
  121. void Image::scale(int newWidth, int newHeight)
  122. {
  123. if(newWidth == width_ && newHeight == height_)
  124. {
  125. return;
  126. }
  127. if(isCompressed(format_))
  128. {
  129. throw runtime_error("Cannot scale compressed image");
  130. }
  131. int nchannels = bitsPerPixel(format_) / 8;
  132. vector<uint8_t> newData(newWidth * newHeight * nchannels);
  133. for(int dstY = 0; dstY < newHeight; dstY++)
  134. {
  135. for(int dstX = 0; dstX < newWidth; dstX++)
  136. {
  137. fp_t srcFX = static_cast<fp_t>(dstX) / static_cast<fp_t>(newWidth) * static_cast<fp_t>(width_);
  138. fp_t srcFY = static_cast<fp_t>(dstY) / static_cast<fp_t>(newHeight) * static_cast<fp_t>(height_);
  139. int srcX = static_cast<int>(srcFX);
  140. int srcY = static_cast<int>(srcFY);
  141. fp_t xDiff = srcFX - srcX;
  142. fp_t yDiff = srcFY - srcY;
  143. fp_t xOpposite = fp_t(1.0) - xDiff;
  144. fp_t yOpposite = fp_t(1.0) - yDiff;
  145. #define SRCPX(x, y, cn) static_cast<fp_t>(data_[(min(x, width_ - 1) + min(y, height_ - 1) * width_) * nchannels + cn])
  146. #define DSTPX(x, y, cn) newData[((x) + (y) * newWidth) * nchannels + cn]
  147. for(int c = 0; c < nchannels; c++)
  148. {
  149. DSTPX(dstX, dstY, c) = static_cast<uint8_t>(
  150. (SRCPX(srcX, srcY, c) * xOpposite + SRCPX(srcX + 1, srcY, c) * xDiff) * yOpposite +
  151. (SRCPX(srcX, srcY + 1, c) * xOpposite + SRCPX(srcX + 1, srcY + 1, c) * xDiff) * yDiff);
  152. }
  153. #undef SRCPX
  154. #undef DSTPX
  155. }
  156. }
  157. data_ = move(newData);
  158. width_ = newWidth;
  159. height_ = newHeight;
  160. }
  161. void Image::flipVertical()
  162. {
  163. if(isCompressed(format_))
  164. {
  165. throw runtime_error("Cannot flip compressed image");
  166. }
  167. int stride = width_ * bitsPerPixel(format_) / 8;
  168. vector<uint8_t> rowBuf(stride);
  169. for(int y = 0; y < height_ / 2; y++)
  170. {
  171. memcpy(rowBuf.data(), data_.data() + y * stride, stride);
  172. memcpy(data_.data() + stride * y, data_.data() + (height_ - y - 1) * stride, stride);
  173. memcpy(data_.data() + (height_ - y - 1) * stride, rowBuf.data(), stride);
  174. }
  175. }
  176. void Image::fill(int value)
  177. {
  178. memset(data_.data(), value, data_.size());
  179. updateHasAlpha();
  180. }
  181. PixelFormat Image::format() const
  182. {
  183. return format_;
  184. }
  185. int Image::width() const
  186. {
  187. return width_;
  188. }
  189. int Image::height() const
  190. {
  191. return height_;
  192. }
  193. size_t Image::size() const
  194. {
  195. return data_.size();
  196. }
  197. const uint8_t* Image::data() const
  198. {
  199. return data_.data();
  200. }
  201. bool Image::hasAlpha() const
  202. {
  203. return hasAlpha_;
  204. }
  205. void Image::updateHasAlpha()
  206. {
  207. hasAlpha_ = false;
  208. uint8_t* input = data_.data();
  209. uint8_t* inputEnd = data_.data() + data_.size();
  210. if(format_ == PixelFormat::kA8R8G8B8)
  211. {
  212. while(input < inputEnd)
  213. {
  214. if(input[3] != 0xFF)
  215. {
  216. hasAlpha_ = true;
  217. return;
  218. }
  219. input += 4;
  220. }
  221. }
  222. else if(format_ == PixelFormat::kDXT1)
  223. {
  224. while(input < inputEnd)
  225. {
  226. uint16_t c0 = *reinterpret_cast<const uint16_t*>(input);
  227. uint16_t c1 = *reinterpret_cast<const uint16_t*>(input + 2);
  228. uint32_t ctab = *reinterpret_cast<const uint32_t*>(input + 4);
  229. if(c0 <= c1)
  230. {
  231. while(ctab)
  232. {
  233. if((ctab & 0x3) == 0x3)
  234. {
  235. hasAlpha_ = true;
  236. return;
  237. }
  238. ctab >>= 2;
  239. }
  240. }
  241. input += 8;
  242. }
  243. }
  244. else if(format_ == PixelFormat::kCustomLscapeAlpha || format_ == PixelFormat::kDXT3 || format_ == PixelFormat::kDXT5)
  245. {
  246. // There's no reason to use these formats unless you have alpha
  247. // So let's just assume it's they do
  248. hasAlpha_ = true;
  249. }
  250. }