4 #include <unordered_map>
23 using std::unordered_map;
40 vector<string> TextureReader::extensions = {
"png"};
41 Mutex TextureReader::textureCacheMutex (
"texturecache-mutex");
42 unordered_map<string, Texture*> TextureReader::textureCache;
44 const vector<string>& TextureReader::getTextureExtensions() {
53 auto canonicalFilePath = FileSystem::getInstance()->getCanonicalURI(pathName, fileName);
54 auto canonicalPathName = FileSystem::getInstance()->getPathName(canonicalFilePath);
55 auto canonicalFileName = FileSystem::getInstance()->getFileName(canonicalFilePath);
58 if (useCache ==
true) {
60 auto textureCacheIt =
textureCache.find(idPrefix + canonicalFilePath);
62 texture = textureCacheIt->second;
67 if (texture ==
nullptr) {
70 if (StringTools::endsWith(StringTools::toLowerCase(canonicalFileName),
".png") ==
true) {
74 FileSystem::getInstance()->getContent(pathName, fileName, data);
77 if (texture !=
nullptr && useCache ==
true) {
82 Console::println(
"TextureReader::read(): Could not load texture: " + canonicalPathName +
"/" + canonicalFileName +
": " + (exception.what()));
95 Texture*
TextureReader::read2(
const string& texturePathName,
const string& textureFileName,
const string& transparencyTexturePathName,
const string& transparencyTextureFileName,
bool useCache,
bool powerOfTwo,
const string& idPrefix) {
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";
103 if (useCache ==
true) {
107 auto texture = textureCacheIt->second;
108 texture->acquireReference();
116 if (texture ==
nullptr) {
123 if (transparencyTexture ==
nullptr) {
124 Console::println(
"TextureReader::read2(): transparency texture: failed: " + texturePathName +
"/" + textureFileName +
";" + transparencyTexturePathName +
"/" + transparencyTextureFileName);
125 if (useCache ==
true) {
129 return texture.release();
133 Console::println(
"TextureReader::read2(): texture does not match transparency texture dimension: " + texturePathName +
"/" + textureFileName +
";" + transparencyTexturePathName +
"/" + transparencyTextureFileName);
134 if (useCache ==
true) {
138 return texture.release();
143 auto textureByteBuffer =
ByteBuffer(textureWidth * textureHeight * 4);
145 auto transparencyTextureBytesPerPixel = transparencyTexture->getRGBDepthBitsPerPixel() / 8;
146 auto transparencyTextureTextureData = transparencyTexture->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));
160 auto textureWithTransparency =
166 idPrefix + texture->
getId() +
"/transparency",
183 textureWithTransparency->acquireReference();
184 if (useCache ==
true) {
188 return textureWithTransparency.release();
192 auto textureXIncrement = (float)textureWidth / (
float)width;
193 auto textureXPixelRest = 0.0f;
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));
202 textureXPixelRest-= (int)textureXPixelRest;
203 textureXPixelRest+= textureXIncrement - (int)textureXIncrement;
207 while (textureX < textureWidth) {
208 for (
auto bytePerPixel = 0; bytePerPixel < bytesPerPixel; bytePerPixel++) {
209 pixelByteBufferScaled.
put(pixelByteBuffer.
get((y * width * bytesPerPixel) + (x * bytesPerPixel) + bytePerPixel));
227 auto textureWidthRotated = -1;
228 auto textureHeightRotated = -1;
230 auto rotationsMatrix = Matrix3x3::rotateAroundPoint(
Vector2(textureWidth / 2.0f, textureHeight / 2.0f), rotation);
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);
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(
267 x - (textureWidthRotated - textureWidth) / 2.0f,
268 y - (textureHeightRotated - textureHeight) / 2.0f
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;
283 rotatedTextureByteBuffer.put(red);
284 rotatedTextureByteBuffer.put(green);
285 rotatedTextureByteBuffer.put(blue);
286 if (textureBytesPerPixel == 4) rotatedTextureByteBuffer.put(alpha);
290 auto rotatedTexture =
296 texture->
getId() + idSuffix +
":tmp",
306 textureHeightRotated,
308 textureHeightRotated,
310 rotatedTextureByteBuffer
313 rotatedTexture->acquireReference();
315 return smooth(rotatedTexture.get(), idSuffix);
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);
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;
343 scaledTextureByteBuffer.put(red);
344 scaledTextureByteBuffer.put(green);
345 scaledTextureByteBuffer.put(blue);
346 if (textureBytesPerPixel == 4) scaledTextureByteBuffer.put(alpha);
356 texture->
getId() + idSuffix,
370 scaledTextureByteBuffer
373 scaledTexture->acquireReference();
375 if (textureWidthScaled < textureWidth)
return scaledTexture.release();
377 return smooth(scaledTexture.get(), idSuffix);
390 auto filteredTextureByteBuffer =
ByteBuffer(textureWidth * textureHeight * textureBytesPerPixel);
391 for (
auto y = 0; y < textureHeight; y++) {
392 for (
auto x = 0; x < textureWidth; x++) {
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;
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;
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));
427 auto filteredTexture =
433 texture->
getId() + idSuffix,
447 filteredTextureByteBuffer
450 filteredTexture->acquireReference();
451 return filteredTexture.release();
static TextureFormat getBC7FormatByPixelBitsPerPixel(int bpp)
Return BC7 RGB/A texture format by bits per pixel.
uint16_t getWidth() const
static TextureFormat getRGBFormatByPixelBitsPerPixel(int bpp)
Return RGB/A texture format by bits per pixel.
bool isBC7TextureFormat() const
ByteBuffer getRGBTextureData()
uint8_t getRGBDepthBitsPerPixel() const
const string & getId() const
uint16_t getTextureHeight() const
static TextureFormat getPNGFormatByPixelBitsPerPixel(int bpp)
Return PNG RGB/A texture format by bits per pixel.
uint16_t getTextureWidth() const
uint16_t getHeight() const
bool isPNGTextureFormat() const
PNG texture reader class.
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.
Vector2 class representing vector2 mathematical structure and operations with x, y components.
File system singleton class.
void unlock()
Unlocks this mutex.
void lock()
Locks the mutex, additionally mutex locks will block until other locks have been unlocked.
uint8_t get(int64_t position) const
Buffer * put(uint8_t value)
Put value into buffer.
virtual void releaseReference()
Releases a reference, thus decrementing the counter and delete it if reference counter is zero.
virtual void acquireReference()
Acquires a reference, incrementing the counter.
std::exception Exception
Exception base class.