TDME2  1.9.200
TextureReader.cpp
Go to the documentation of this file.
2 
3 #include <string>
4 #include <unordered_map>
5 #include <vector>
6 
7 #include <tdme/tdme.h>
9 
11 #include <tdme/engine/Texture.h>
12 #include <tdme/math/Matrix3x3.h>
13 #include <tdme/math/Vector2.h>
17 #include <tdme/utilities/Console.h>
20 
21 using std::string;
22 using std::to_string;
23 using std::unordered_map;
24 using std::vector;
25 
27 
39 
40 vector<string> TextureReader::extensions = {"png"};
41 Mutex TextureReader::textureCacheMutex ("texturecache-mutex");
42 unordered_map<string, Texture*> TextureReader::textureCache;
43 
44 const vector<string>& TextureReader::getTextureExtensions() {
45  return extensions;
46 }
47 
48 Texture* TextureReader::read(const string& pathName, const string& fileName, bool useCache, bool powerOfTwo, const string& idPrefix)
49 {
50  Texture* texture = nullptr;
51 
52  // make canonical
53  auto canonicalFilePath = FileSystem::getInstance()->getCanonicalURI(pathName, fileName);
54  auto canonicalPathName = FileSystem::getInstance()->getPathName(canonicalFilePath);
55  auto canonicalFileName = FileSystem::getInstance()->getFileName(canonicalFilePath);
56 
57  // do cache look up
58  if (useCache == true) {
60  auto textureCacheIt = textureCache.find(idPrefix + canonicalFilePath);
61  if (textureCacheIt != textureCache.end()) {
62  texture = textureCacheIt->second;
63  }
64  }
65 
66  // have texture?
67  if (texture == nullptr) {
68  // nope try to load
69  try {
70  if (StringTools::endsWith(StringTools::toLowerCase(canonicalFileName), ".png") == true) {
71 
72  // create PNG input stream
73  vector<uint8_t> data;
74  FileSystem::getInstance()->getContent(pathName, fileName, data);
75 
76  texture = PNGTextureReader::read(canonicalFilePath, data, powerOfTwo, idPrefix);
77  if (texture != nullptr && useCache == true) {
78  textureCache[texture->getId()] = texture;
79  }
80  }
81  } catch (Exception& exception) {
82  Console::println("TextureReader::read(): Could not load texture: " + canonicalPathName + "/" + canonicalFileName + ": " + (exception.what()));
83  }
84  }
85  //
86  if (texture != nullptr) texture->acquireReference();
87 
88  //
89  if (useCache == true) textureCacheMutex.unlock();
90 
91  // done
92  return texture;
93 }
94 
95 Texture* TextureReader::read2(const string& texturePathName, const string& textureFileName, const string& transparencyTexturePathName, const string& transparencyTextureFileName, bool useCache, bool powerOfTwo, const string& idPrefix) {
96  // make canonical
97  auto canonicalFile = FileSystem::getInstance()->getCanonicalURI(texturePathName, textureFileName);
98  auto canonicalPathName = FileSystem::getInstance()->getPathName(canonicalFile);
99  auto canonicalFileName = FileSystem::getInstance()->getFileName(canonicalFile);
100  auto cacheId = idPrefix + canonicalPathName + "/" + canonicalFileName + "/transparency";
101 
102  // do cache look up
103  if (useCache == true) {
105  auto textureCacheIt = textureCache.find(cacheId);
106  if (textureCacheIt != textureCache.end()) {
107  auto texture = textureCacheIt->second;
108  texture->acquireReference();
110  return texture;
111  }
112  }
113 
114  // load diffuse texture
115  auto texture = unique_ptr<Texture, decltype([](Texture* texture){ texture->releaseReference(); })>(TextureReader::read(texturePathName, textureFileName, false, powerOfTwo, idPrefix));
116  if (texture == nullptr) {
117  if (useCache == true) textureCacheMutex.unlock();
118  return nullptr;
119  }
120  // additional transparency texture
121  auto transparencyTexture = unique_ptr<Texture, decltype([](Texture* texture){ texture->releaseReference(); })>(TextureReader::read(transparencyTexturePathName, transparencyTextureFileName, false, powerOfTwo, idPrefix));
122  // do we have one?
123  if (transparencyTexture == nullptr) {
124  Console::println("TextureReader::read2(): transparency texture: failed: " + texturePathName + "/" + textureFileName + ";" + transparencyTexturePathName + "/" + transparencyTextureFileName);
125  if (useCache == true) {
126  textureCache[cacheId] = texture.get();
128  }
129  return texture.release();
130  }
131  // same dimensions and supported pixel depth?
132  if (texture->getTextureWidth() != transparencyTexture->getTextureWidth() || texture->getTextureHeight() != transparencyTexture->getTextureHeight()) {
133  Console::println("TextureReader::read2(): texture does not match transparency texture dimension: " + texturePathName + "/" + textureFileName + ";" + transparencyTexturePathName + "/" + transparencyTextureFileName);
134  if (useCache == true) {
135  textureCache[cacheId] = texture.get();
137  }
138  return texture.release();
139  }
140  // yep, combine diffuse map + diffuse transparency map
141  auto textureWidth = texture->getTextureWidth();
142  auto textureHeight = texture->getTextureHeight();
143  auto textureByteBuffer = ByteBuffer(textureWidth * textureHeight * 4);
144  auto diffuseTextureBytesPerPixel = texture->getRGBDepthBitsPerPixel() / 8;
145  auto transparencyTextureBytesPerPixel = transparencyTexture->getRGBDepthBitsPerPixel() / 8;
146  auto transparencyTextureTextureData = transparencyTexture->getRGBTextureData();
147  auto textureTextureData = texture->getRGBTextureData();
148  for (auto y = 0; y < textureHeight; y++) {
149  for (auto x = 0; x < textureWidth; x++) {
150  auto transparencyTextureRed = transparencyTextureTextureData.get((y * textureWidth * transparencyTextureBytesPerPixel) + (x * transparencyTextureBytesPerPixel) + 0);
151  auto transparencyTextureGreen = transparencyTextureTextureData.get((y * textureWidth * transparencyTextureBytesPerPixel) + (x * transparencyTextureBytesPerPixel) + 1);
152  auto transparencyTextureBlue = transparencyTextureTextureData.get((y * textureWidth * transparencyTextureBytesPerPixel) + (x * transparencyTextureBytesPerPixel) + 2);
153  textureByteBuffer.put(textureTextureData.get((y * textureWidth * diffuseTextureBytesPerPixel) + (x * diffuseTextureBytesPerPixel) + 0));
154  textureByteBuffer.put(textureTextureData.get((y * textureWidth * diffuseTextureBytesPerPixel) + (x * diffuseTextureBytesPerPixel) + 1));
155  textureByteBuffer.put(textureTextureData.get((y * textureWidth * diffuseTextureBytesPerPixel) + (x * diffuseTextureBytesPerPixel) + 2));
156  textureByteBuffer.put((uint8_t)((transparencyTextureRed + transparencyTextureGreen + transparencyTextureBlue) * 0.3333f));
157  }
158  }
159  //
160  auto textureWithTransparency =
161  unique_ptr<
162  Texture,
163  decltype([](Texture* texture){ texture->releaseReference(); })
164  >(
165  new Texture(
166  idPrefix + texture->getId() + "/transparency",
168  texture->isPNGTextureFormat() == true?
170  (
171  texture->isBC7TextureFormat() == true?
174  ),
175  texture->getWidth(),
176  texture->getHeight(),
177  texture->getTextureWidth(),
178  texture->getTextureHeight(),
180  textureByteBuffer
181  )
182  );
183  textureWithTransparency->acquireReference();
184  if (useCache == true) {
185  textureCache[cacheId] = textureWithTransparency.get();
187  }
188  return textureWithTransparency.release();
189 }
190 
191 void TextureReader::scaleTextureLine(const ByteBuffer& pixelByteBuffer, ByteBuffer& pixelByteBufferScaled, int width, int textureWidth, int bytesPerPixel, int y) {
192  auto textureXIncrement = (float)textureWidth / (float)width;
193  auto textureXPixelRest = 0.0f;
194  auto textureX = 0;
195  for (auto x = 0; x < width; x++) {
196  for (auto i = 0; i < (int)textureXIncrement + (int)textureXPixelRest; i++) {
197  for (auto bytePerPixel = 0; bytePerPixel < bytesPerPixel; bytePerPixel++) {
198  pixelByteBufferScaled.put(pixelByteBuffer.get((y * width * bytesPerPixel) + (x * bytesPerPixel) + bytePerPixel));
199  }
200  textureX++;
201  }
202  textureXPixelRest-= (int)textureXPixelRest;
203  textureXPixelRest+= textureXIncrement - (int)textureXIncrement;
204  }
205  {
206  auto x = width - 1;
207  while (textureX < textureWidth) {
208  for (auto bytePerPixel = 0; bytePerPixel < bytesPerPixel; bytePerPixel++) {
209  pixelByteBufferScaled.put(pixelByteBuffer.get((y * width * bytesPerPixel) + (x * bytesPerPixel) + bytePerPixel));
210  }
211  textureX++;
212  }
213  }
214 }
215 
218  auto textureCacheIt = textureCache.find(texture->getId());
219  if (textureCacheIt != textureCache.end()) textureCache.erase(textureCacheIt);
221 }
222 
223 Texture* TextureReader::rotate(Texture* texture, float rotation, const string& idSuffix) {
224  auto textureWidth = texture->getTextureWidth();
225  auto textureHeight = texture->getTextureHeight();
226  auto textureBytesPerPixel = texture->getRGBDepthBitsPerPixel() / 8;
227  auto textureWidthRotated = -1;
228  auto textureHeightRotated = -1;
229  {
230  auto rotationsMatrix = Matrix3x3::rotateAroundPoint(Vector2(textureWidth / 2.0f, textureHeight / 2.0f), rotation);
231  Vector2 leftTop(0.0f, 0.0f);
232  Vector2 rightTop(textureWidth, 0.0f);
233  Vector2 leftBottom(0.0f, textureHeight);
234  Vector2 rightBottom(textureWidth, textureHeight);
235  auto leftTopRotated = rotationsMatrix.multiply(leftTop);
236  auto rightTopRotated = rotationsMatrix.multiply(rightTop);
237  auto leftBottomRotated = rotationsMatrix.multiply(leftBottom);
238  auto rightBottomRotated = rotationsMatrix.multiply(rightBottom);
239  auto textureWidthTransformedMin = Math::min(leftTopRotated.getX(),
240  Math::min(rightTopRotated.getX(),
241  Math::min(leftBottomRotated.getX(),
242  rightBottomRotated.getX())));
243  auto textureWidthTransformedMax = Math::max(leftTopRotated.getX(),
244  Math::max(rightTopRotated.getX(),
245  Math::max(leftBottomRotated.getX(),
246  rightBottomRotated.getX())));
247  auto textureHeightTransformedMin = Math::min(leftTopRotated.getY(),
248  Math::min(rightTopRotated.getY(),
249  Math::min(leftBottomRotated.getY(),
250  rightBottomRotated.getY())));
251  auto textureHeightTransformedMax = Math::max(leftTopRotated.getY(),
252  Math::max(rightTopRotated.getY(),
253  Math::max(leftBottomRotated.getY(),
254  rightBottomRotated.getY())));
255  textureWidthRotated = static_cast<int>(textureWidthTransformedMax
256  - textureWidthTransformedMin);
257  textureHeightRotated = static_cast<int>(textureHeightTransformedMax
258  - textureHeightTransformedMin);
259  }
260  auto textureTextureData = texture->getRGBTextureData();
261  auto rotatedTextureByteBuffer = ByteBuffer(textureWidthRotated * textureHeightRotated * textureBytesPerPixel);
262  auto rotationsMatrix = Matrix3x3::rotateAroundPoint(Vector2(textureWidth / 2.0f, textureHeight / 2.0f), rotation);
263  for (auto y = 0; y < textureHeightRotated; y++) {
264  for (auto x = 0; x < textureWidthRotated; x++) {
265  auto originalTexturePoint = rotationsMatrix.multiply(
266  Vector2(
267  x - (textureWidthRotated - textureWidth) / 2.0f,
268  y - (textureHeightRotated - textureHeight) / 2.0f
269  )
270  );
271  auto red = 0;
272  auto green = 0;
273  auto blue = 0;
274  auto alpha = 0;
275  auto originalX = static_cast<int>(originalTexturePoint.getX());
276  auto originalY = static_cast<int>(originalTexturePoint.getY());
277  if (originalX >= 0 && originalX < textureWidth && originalY >= 0 && originalY < textureHeight) {
278  red = textureTextureData.get((originalY * textureWidth * textureBytesPerPixel) + (originalX * textureBytesPerPixel) + 0);
279  green = textureTextureData.get((originalY * textureWidth * textureBytesPerPixel) + (originalX * textureBytesPerPixel) + 1);
280  blue = textureTextureData.get((originalY * textureWidth * textureBytesPerPixel) + (originalX * textureBytesPerPixel) + 2);
281  alpha = textureBytesPerPixel == 4?textureTextureData.get((originalY * textureWidth * textureBytesPerPixel) + (originalX * textureBytesPerPixel) + 3):255;
282  }
283  rotatedTextureByteBuffer.put(red);
284  rotatedTextureByteBuffer.put(green);
285  rotatedTextureByteBuffer.put(blue);
286  if (textureBytesPerPixel == 4) rotatedTextureByteBuffer.put(alpha);
287  }
288  }
289  //
290  auto rotatedTexture =
291  unique_ptr<
292  Texture,
293  decltype([](Texture* texture){ texture->releaseReference(); })
294  >(
295  new Texture(
296  texture->getId() + idSuffix + ":tmp",
297  textureBytesPerPixel == 4?Texture::TEXTUREDEPTH_RGBA:Texture::TEXTUREDEPTH_RGB,
298  texture->isPNGTextureFormat() == true?
300  (
301  texture->isBC7TextureFormat() == true?
304  ),
305  textureWidthRotated,
306  textureHeightRotated,
307  textureWidthRotated,
308  textureHeightRotated,
310  rotatedTextureByteBuffer
311  )
312  );
313  rotatedTexture->acquireReference();
314  // do smooooth
315  return smooth(rotatedTexture.get(), idSuffix);
316 }
317 
318 Texture* TextureReader::scale(Texture* texture, int width, int height, const string& idSuffix) {
319  auto textureTextureData = texture->getRGBTextureData();
320  auto textureWidth = texture->getTextureWidth();
321  auto textureHeight = texture->getTextureHeight();
322  auto textureBytesPerPixel = texture->getRGBDepthBitsPerPixel() / 8;
323  auto textureWidthScaled = width;
324  auto textureHeightScaled = height;
325  auto scaledTextureByteBuffer = ByteBuffer(textureWidthScaled * textureHeightScaled * textureBytesPerPixel);
326  auto scaleX = static_cast<float>(textureWidth) / static_cast<float>(textureWidthScaled);
327  auto scaleY = static_cast<float>(textureHeight) / static_cast<float>(textureHeightScaled);
328  for (auto y = 0; y < textureHeightScaled; y++) {
329  for (auto x = 0; x < textureWidthScaled; x++) {
330  auto originalTexturePoint = Vector2(static_cast<float>(x) * scaleX, static_cast<float>(y) * scaleY);
331  auto red = 0;
332  auto green = 0;
333  auto blue = 0;
334  auto alpha = 0;
335  auto originalX = static_cast<int>(originalTexturePoint.getX());
336  auto originalY = static_cast<int>(originalTexturePoint.getY());
337  if (originalX >= 0 && originalX < textureWidth && originalY >= 0 && originalY < textureHeight) {
338  red = textureTextureData.get((originalY * textureWidth * textureBytesPerPixel) + (originalX * textureBytesPerPixel) + 0);
339  green = textureTextureData.get((originalY * textureWidth * textureBytesPerPixel) + (originalX * textureBytesPerPixel) + 1);
340  blue = textureTextureData.get((originalY * textureWidth * textureBytesPerPixel) + (originalX * textureBytesPerPixel) + 2);
341  alpha = textureBytesPerPixel == 4?textureTextureData.get((originalY * textureWidth * textureBytesPerPixel) + (originalX * textureBytesPerPixel) + 3):255;
342  }
343  scaledTextureByteBuffer.put(red);
344  scaledTextureByteBuffer.put(green);
345  scaledTextureByteBuffer.put(blue);
346  if (textureBytesPerPixel == 4) scaledTextureByteBuffer.put(alpha);
347  }
348  }
349  //
350  auto scaledTexture =
351  unique_ptr<
352  Texture,
353  decltype([](Texture* texture){ texture->releaseReference(); })
354  >(
355  new Texture(
356  texture->getId() + idSuffix,
357  textureBytesPerPixel == 4?Texture::TEXTUREDEPTH_RGBA:Texture::TEXTUREDEPTH_RGB,
358  texture->isPNGTextureFormat() == true?
360  (
361  texture->isBC7TextureFormat() == true?
364  ),
365  textureWidthScaled,
366  textureHeightScaled,
367  textureWidthScaled,
368  textureHeightScaled,
370  scaledTextureByteBuffer
371  )
372  );
373  scaledTexture->acquireReference();
374  // no smoothing if texture got smaller
375  if (textureWidthScaled < textureWidth) return scaledTexture.release();
376  // otherwise do smoothing
377  return smooth(scaledTexture.get(), idSuffix);
378 }
379 
380 Texture* TextureReader::smooth(Texture* texture, const string& idSuffix, float adjacentSampleWeight) {
381  //
382  auto textureWidth = texture->getTextureWidth();
383  auto textureHeight = texture->getTextureHeight();
384  auto textureBytesPerPixel = texture->getRGBDepthBitsPerPixel() / 8;
385 
386  //
387  auto textureTextureData = texture->getRGBTextureData();
388 
389  //
390  auto filteredTextureByteBuffer = ByteBuffer(textureWidth * textureHeight * textureBytesPerPixel);
391  for (auto y = 0; y < textureHeight; y++) {
392  for (auto x = 0; x < textureWidth; x++) {
393  auto samples = 0.0f;
394  auto red = 0.0f;
395  auto green = 0.0f;
396  auto blue = 0.0f;
397  auto alpha = 0.0f;
398  {
399  auto pixelOffset = (y * textureWidth * textureBytesPerPixel) + (x * textureBytesPerPixel);
400  red+= textureTextureData.get(pixelOffset + 0) * 1.0f;
401  green+= textureTextureData.get(pixelOffset + 1) * 1.0f;
402  blue+= textureTextureData.get(pixelOffset + 2) * 1.0f;
403  alpha+= (textureBytesPerPixel == 4?textureTextureData.get(pixelOffset + 3):255.0f) * 1.0f;
404  samples+=1.0f;
405  }
406  for (auto _y = -1; _y <= 1; _y++) {
407  for (auto _x = -1; _x <= 1; _x++) {
408  if ((Math::abs(_x) == 1 && Math::abs(_y) == 1) == false &&
409  (x + _x >= 0 && x + _x < textureWidth && y + _y >= 0 && y + _y < textureHeight)) {
410  auto pixelOffset = ((y + _y) * textureWidth * textureBytesPerPixel) + ((x + _x) * textureBytesPerPixel);
411  red+= textureTextureData.get(pixelOffset + 0) * adjacentSampleWeight;
412  green+= textureTextureData.get(pixelOffset + 1) * adjacentSampleWeight;
413  blue+= textureTextureData.get(pixelOffset + 2) * adjacentSampleWeight;
414  alpha+= (textureBytesPerPixel == 4?textureTextureData.get(pixelOffset + 3):255.0f) * adjacentSampleWeight;
415  samples+= adjacentSampleWeight;
416  }
417  }
418  }
419  filteredTextureByteBuffer.put(static_cast<uint8_t>(red / samples));
420  filteredTextureByteBuffer.put(static_cast<uint8_t>(green / samples));
421  filteredTextureByteBuffer.put(static_cast<uint8_t>(blue / samples));
422  if (textureBytesPerPixel == 4) filteredTextureByteBuffer.put(static_cast<uint8_t>(alpha / samples));
423  }
424  }
425 
426  //
427  auto filteredTexture =
428  unique_ptr<
429  Texture,
430  decltype([](Texture* texture){ texture->releaseReference(); })
431  >(
432  new Texture(
433  texture->getId() + idSuffix,
434  textureBytesPerPixel == 4?Texture::TEXTUREDEPTH_RGBA:Texture::TEXTUREDEPTH_RGB,
435  texture->isPNGTextureFormat() == true?
437  (
438  texture->isBC7TextureFormat() == true?
441  ),
442  textureWidth, // TODO: calculate new width
443  textureHeight, // TODO: calculate new height
444  textureWidth,
445  textureHeight,
447  filteredTextureByteBuffer
448  )
449  );
450  filteredTexture->acquireReference();
451  return filteredTexture.release();
452 }
Texture entity.
Definition: Texture.h:24
static TextureFormat getBC7FormatByPixelBitsPerPixel(int bpp)
Return BC7 RGB/A texture format by bits per pixel.
Definition: Texture.h:98
uint16_t getWidth() const
Definition: Texture.h:197
static TextureFormat getRGBFormatByPixelBitsPerPixel(int bpp)
Return RGB/A texture format by bits per pixel.
Definition: Texture.h:72
bool isBC7TextureFormat() const
Definition: Texture.h:246
ByteBuffer getRGBTextureData()
Definition: Texture.h:253
uint8_t getRGBDepthBitsPerPixel() const
Definition: Texture.h:186
const string & getId() const
Definition: Texture.h:172
uint16_t getTextureHeight() const
Definition: Texture.h:211
static TextureFormat getPNGFormatByPixelBitsPerPixel(int bpp)
Return PNG RGB/A texture format by bits per pixel.
Definition: Texture.h:85
uint16_t getTextureWidth() const
Definition: Texture.h:218
uint16_t getHeight() const
Definition: Texture.h:204
bool isPNGTextureFormat() const
Definition: Texture.h:239
static bool read(const vector< uint8_t > &pngData, ByteBuffer &textureByteBuffer)
Read PNG from memory into texture byte buffer.
static Texture * scale(Texture *texture, int width, int height, const string &idSuffix=":scaled")
Scale texture.
static STATIC_DLL_IMPEXT Mutex textureCacheMutex
static void removeFromCache(Texture *texture)
Remove texture from cache.
static STATIC_DLL_IMPEXT unordered_map< string, Texture * > textureCache
static Texture * read(const string &pathName, const string &fileName, bool useCache=true, bool powerOfTwo=true, const string &idPrefix=string())
Reads a texture.
static void scaleTextureLine(const ByteBuffer &pixelByteBuffer, ByteBuffer &pixelByteBufferScaled, int width, int textureWidth, int bytesPerPixel, int y)
Scales a texture line.
static Texture * read2(const string &texturePathName, const string &textureFileName, const string &transparencyTexturePathName, const string &transparencyTextureFileName, bool useCache=true, bool powerOfTwo=true, const string &idPrefix=string())
Reads a texture with additional transparency texture.
static Texture * smooth(Texture *texture, const string &idSuffix=":smoothed", float adjacentSampleWeight=0.05f)
Smooth texture.
static Texture * rotate(Texture *texture, float rotation, const string &idSuffix=":rotated")
Rotate texture around center.
static STATIC_DLL_IMPEXT vector< string > extensions
Matrix3x3 class representing matrix3x3 mathematical structure and operations for 2d space.
Definition: Matrix3x3.h:20
Vector2 class representing vector2 mathematical structure and operations with x, y components.
Definition: Vector2.h:20
File system singleton class.
Definition: FileSystem.h:17
Mutex implementation.
Definition: Mutex.h:19
void unlock()
Unlocks this mutex.
Definition: Mutex.h:54
void lock()
Locks the mutex, additionally mutex locks will block until other locks have been unlocked.
Definition: Mutex.h:47
uint8_t get(int64_t position) const
Definition: Buffer.h:107
Buffer * put(uint8_t value)
Put value into buffer.
Definition: Buffer.h:115
Byte buffer class.
Definition: ByteBuffer.h:27
Console class.
Definition: Console.h:29
virtual void releaseReference()
Releases a reference, thus decrementing the counter and delete it if reference counter is zero.
Definition: Reference.h:38
virtual void acquireReference()
Acquires a reference, incrementing the counter.
Definition: Reference.h:31
String tools class.
Definition: StringTools.h:22
std::exception Exception
Exception base class.
Definition: Exception.h:18