textureAPI.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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. #include "drawAPI.h"
  28. namespace dsr {
  29. static int findLog2Size(uint32_t size) {
  30. static const uint32_t maxLog2Size = 15; // 32768 pixels
  31. for (uint32_t log2Size = 0; log2Size < maxLog2Size; log2Size++) {
  32. if ((uint32_t(1u) << log2Size) >= size) {
  33. // Found a size that is large enough.
  34. return log2Size;
  35. }
  36. }
  37. // Reached the upper limit.
  38. return maxLog2Size;
  39. }
  40. // TODO: Optimize using addition and SafePointer.
  41. static void downsample(const TextureRgbaU8 &texture, uint32_t targetLevel) {
  42. uint32_t sourceLevel = targetLevel - 1;
  43. uint32_t targetWidth = texture_getWidth(texture, targetLevel);
  44. uint32_t targetHeight = texture_getHeight(texture, targetLevel);
  45. for (uint32_t y = 0; y < targetHeight; y++) {
  46. for (uint32_t x = 0; x < targetWidth; x++) {
  47. uint32_t upperLeft = texture_readPixel(texture, x * 2 , y * 2 , sourceLevel);
  48. uint32_t upperRight = texture_readPixel(texture, x * 2 + 1, y * 2 , sourceLevel);
  49. uint32_t lowerLeft = texture_readPixel(texture, x * 2 , y * 2 + 1, sourceLevel);
  50. uint32_t lowerRight = texture_readPixel(texture, x * 2 + 1, y * 2 + 1, sourceLevel);
  51. uint32_t mixedColor = packOrder_packBytes(
  52. (packOrder_getRed (upperLeft) + packOrder_getRed (upperRight) + packOrder_getRed (lowerLeft) + packOrder_getRed (lowerRight)) / 4,
  53. (packOrder_getGreen(upperLeft) + packOrder_getGreen(upperRight) + packOrder_getGreen(lowerLeft) + packOrder_getGreen(lowerRight)) / 4,
  54. (packOrder_getBlue (upperLeft) + packOrder_getBlue (upperRight) + packOrder_getBlue (lowerLeft) + packOrder_getBlue (lowerRight)) / 4,
  55. (packOrder_getAlpha(upperLeft) + packOrder_getAlpha(upperRight) + packOrder_getAlpha(lowerLeft) + packOrder_getAlpha(lowerRight)) / 4
  56. );
  57. texture_writePixel(texture, x, y, targetLevel, mixedColor);
  58. }
  59. }
  60. }
  61. TextureRgbaU8 texture_create_RgbaU8(int32_t width, int32_t height, int32_t resolutions) {
  62. if (resolutions < 1) {
  63. throwError(U"Tried to create a texture without any resolutions stored, which would be empty!\n");
  64. return TextureRgbaU8();
  65. } else if (width < 1 || height < 1) {
  66. throwError(U"Tried to create a texture of ", width, U" x ", height, U" pixels, which would be empty!\n");
  67. return TextureRgbaU8();
  68. } else if (width > 32768 || height > 32768) {
  69. 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");
  70. return TextureRgbaU8();
  71. } else {
  72. return TextureRgbaU8(findLog2Size(width), findLog2Size(height), resolutions - 1);
  73. }
  74. }
  75. static uint64_t testCounter = 0;
  76. void texture_generatePyramid(const TextureRgbaU8& texture) {
  77. uint32_t mipLevelCount = texture_getMipLevelCount(texture);
  78. for (uint32_t targetLevel = 1; targetLevel < mipLevelCount; targetLevel++) {
  79. downsample(texture, targetLevel);
  80. }
  81. }
  82. TextureRgbaU8 texture_create_RgbaU8(const ImageRgbaU8& image, int32_t resolutions) {
  83. if (!image_exists(image)) {
  84. // An empty image returns an empty pyramid.
  85. return TextureRgbaU8();
  86. } else {
  87. // Allocate a pyramid image.
  88. TextureRgbaU8 result = texture_create_RgbaU8(image_getWidth(image), image_getHeight(image), resolutions);
  89. uint32_t width = texture_getMaxWidth(result);
  90. uint32_t height = texture_getMaxHeight(result);
  91. // Create an image of the same size as the largest resolution.
  92. OrderedImageRgbaU8 resized = filter_resize(image, Sampler::Linear, width, height);
  93. testCounter++;
  94. // Copy from the resized image to the highest resolution in the pyramid.
  95. for (uint32_t y = 0; y < height; y++) {
  96. SafePointer<uint32_t> source = image_getSafePointer(resized, y);
  97. SafePointer<uint32_t> target = texture_getSafePointer(result, 0u, y);
  98. safeMemoryCopy(target, source, width * sizeof(uint32_t));
  99. }
  100. texture_generatePyramid(result);
  101. return result;
  102. }
  103. }
  104. ImageRgbaU8 texture_getMipLevelImage(const TextureRgbaU8& texture, int32_t mipLevel) {
  105. if (!texture_exists(texture)) {
  106. throwError(U"Can not get a mip level as an image from a texture that does not exist!\n");
  107. return ImageRgbaU8();
  108. } else if (mipLevel < 0 || mipLevel > texture_getSmallestMipLevel(texture)) {
  109. 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");
  110. throwError(U"");
  111. return ImageRgbaU8();
  112. } else {
  113. return ImageRgbaU8(texture.impl_buffer, texture_getPixelOffsetToLayer<false, uint32_t>(texture, mipLevel), texture_getWidth(texture, mipLevel), texture_getHeight(texture, mipLevel), texture_getWidth(texture, mipLevel), PackOrderIndex::RGBA);
  114. }
  115. }
  116. }