textureAPI.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. 
  2. // zlib open source license
  3. //
  4. // Copyright (c) 2025 David Forsgren Piuva
  5. //
  6. // This software is provided 'as-is', without any express or implied
  7. // warranty. In no event will the authors be held liable for any damages
  8. // arising from the use of this software.
  9. //
  10. // Permission is granted to anyone to use this software for any purpose,
  11. // including commercial applications, and to alter it and redistribute it
  12. // freely, subject to the following restrictions:
  13. //
  14. // 1. The origin of this software must not be misrepresented; you must not
  15. // claim that you wrote the original software. If you use this software
  16. // in a product, an acknowledgment in the product documentation would be
  17. // appreciated but is not required.
  18. //
  19. // 2. Altered source versions must be plainly marked as such, and must not be
  20. // misrepresented as being the original software.
  21. //
  22. // 3. This notice may not be removed or altered from any source
  23. // distribution.
  24. #include "textureAPI.h"
  25. #include "imageAPI.h"
  26. #include "filterAPI.h"
  27. namespace dsr {
  28. static int findLog2Size(uint32_t size) {
  29. static const uint32_t maxLog2Size = 15; // 32768 pixels
  30. for (uint32_t log2Size = 0; log2Size < maxLog2Size; log2Size++) {
  31. if ((uint32_t(1u) << log2Size) >= size) {
  32. // Found a size that is large enough.
  33. return log2Size;
  34. }
  35. }
  36. // Reached the upper limit.
  37. return maxLog2Size;
  38. }
  39. // TODO: Optimize using addition and SafePointer.
  40. static void downsample(const TextureRgbaU8 &texture, uint32_t targetLevel) {
  41. uint32_t sourceLevel = targetLevel - 1;
  42. uint32_t targetWidth = texture_getWidth(texture, targetLevel);
  43. uint32_t targetHeight = texture_getHeight(texture, targetLevel);
  44. for (uint32_t y = 0; y < targetHeight; y++) {
  45. for (uint32_t x = 0; x < targetWidth; x++) {
  46. uint32_t upperLeft = texture_readPixel(texture, x * 2 , y * 2 , sourceLevel);
  47. uint32_t upperRight = texture_readPixel(texture, x * 2 + 1, y * 2 , sourceLevel);
  48. uint32_t lowerLeft = texture_readPixel(texture, x * 2 , y * 2 + 1, sourceLevel);
  49. uint32_t lowerRight = texture_readPixel(texture, x * 2 + 1, y * 2 + 1, sourceLevel);
  50. uint32_t mixedColor = packOrder_packBytes(
  51. (packOrder_getRed (upperLeft) + packOrder_getRed (upperRight) + packOrder_getRed (lowerLeft) + packOrder_getRed (lowerRight)) / 4,
  52. (packOrder_getGreen(upperLeft) + packOrder_getGreen(upperRight) + packOrder_getGreen(lowerLeft) + packOrder_getGreen(lowerRight)) / 4,
  53. (packOrder_getBlue (upperLeft) + packOrder_getBlue (upperRight) + packOrder_getBlue (lowerLeft) + packOrder_getBlue (lowerRight)) / 4,
  54. (packOrder_getAlpha(upperLeft) + packOrder_getAlpha(upperRight) + packOrder_getAlpha(lowerLeft) + packOrder_getAlpha(lowerRight)) / 4
  55. );
  56. texture_writePixel(texture, x, y, targetLevel, mixedColor);
  57. }
  58. }
  59. }
  60. TextureRgbaU8 texture_create_RgbaU8(int32_t width, int32_t height, int32_t resolutions) {
  61. if (resolutions < 1) {
  62. throwError(U"Tried to create a texture without any resolutions stored, which would be empty!\n");
  63. return TextureRgbaU8();
  64. } else if (width < 1 || height < 1) {
  65. throwError(U"Tried to create a texture of ", width, U" x ", height, U" pixels, which would be empty!\n");
  66. return TextureRgbaU8();
  67. } else if (width > 32768 || height > 32768) {
  68. throwError(U"Tried to create a texture of ", width, U" x ", height, U" pixels, which exceeds the maximum texture dimensions of 32768 x 32768 pixels!\n");
  69. return TextureRgbaU8();
  70. } else {
  71. return TextureRgbaU8(findLog2Size(width), findLog2Size(height), resolutions - 1);
  72. }
  73. }
  74. static uint64_t testCounter = 0;
  75. void texture_generatePyramid(const TextureRgbaU8& texture) {
  76. uint32_t mipLevelCount = texture_getMipLevelCount(texture);
  77. for (uint32_t targetLevel = 1; targetLevel < mipLevelCount; targetLevel++) {
  78. downsample(texture, targetLevel);
  79. }
  80. }
  81. TextureRgbaU8 texture_create_RgbaU8(const ImageRgbaU8& image, int32_t resolutions) {
  82. if (!image_exists(image)) {
  83. // An empty image returns an empty pyramid.
  84. return TextureRgbaU8();
  85. } else {
  86. // Allocate a pyramid image.
  87. TextureRgbaU8 result = texture_create_RgbaU8(image_getWidth(image), image_getHeight(image), resolutions);
  88. uint32_t width = texture_getMaxWidth(result);
  89. uint32_t height = texture_getMaxHeight(result);
  90. // Create an image of the same size as the largest resolution.
  91. OrderedImageRgbaU8 resized = filter_resize(image, Sampler::Linear, width, height);
  92. testCounter++;
  93. // Copy from the resized image to the highest resolution in the pyramid.
  94. for (uint32_t y = 0; y < height; y++) {
  95. SafePointer<uint32_t> source = image_getSafePointer(resized, y);
  96. SafePointer<uint32_t> target = texture_getSafePointer(result, 0u, y);
  97. safeMemoryCopy(target, source, width * sizeof(uint32_t));
  98. }
  99. texture_generatePyramid(result);
  100. return result;
  101. }
  102. }
  103. ImageRgbaU8 texture_getMipLevelImage(const TextureRgbaU8& texture, int32_t mipLevel) {
  104. if (!texture_exists(texture)) {
  105. throwError(U"Can not get a mip level as an image from a texture that does not exist!\n");
  106. return ImageRgbaU8();
  107. } else if (mipLevel < 0 || mipLevel > texture_getSmallestMipLevel(texture)) {
  108. throwError(U"Can not get a non-existing mip level at index ", mipLevel, U" from a texture with layers 0..", texture_getSmallestMipLevel(texture), U"!\n");
  109. throwError(U"");
  110. return ImageRgbaU8();
  111. } else {
  112. return ImageRgbaU8(texture.impl_buffer, texture_getPixelOffsetToLayer<false, uint32_t, uint32_t>(texture, mipLevel), texture_getWidth(texture, mipLevel), texture_getHeight(texture, mipLevel), texture_getWidth(texture, mipLevel), PackOrderIndex::RGBA);
  113. }
  114. }
  115. }