ImageOperations.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // Copyright 2019 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "InputCommon/ImageOperations.h"
  4. #include <algorithm>
  5. #include <cmath>
  6. #include <limits>
  7. #include <stack>
  8. #include <string>
  9. #include <vector>
  10. #include "Common/FileUtil.h"
  11. #include "Common/IOFile.h"
  12. #include "Common/Image.h"
  13. namespace InputCommon
  14. {
  15. namespace
  16. {
  17. Pixel SampleNearest(const ImagePixelData& src, double u, double v)
  18. {
  19. const u32 x = std::clamp(static_cast<u32>(u * src.width), 0u, src.width - 1);
  20. const u32 y = std::clamp(static_cast<u32>(v * src.height), 0u, src.height - 1);
  21. return src.pixels[x + y * src.width];
  22. }
  23. } // namespace
  24. void CopyImageRegion(const ImagePixelData& src, ImagePixelData& dst, const Rect& src_region,
  25. const Rect& dst_region)
  26. {
  27. if (src_region.GetWidth() != dst_region.GetWidth() ||
  28. src_region.GetHeight() != dst_region.GetHeight())
  29. {
  30. return;
  31. }
  32. for (u32 x = 0; x < dst_region.GetWidth(); x++)
  33. {
  34. for (u32 y = 0; y < dst_region.GetHeight(); y++)
  35. {
  36. dst.pixels[(y + dst_region.top) * dst.width + x + dst_region.left] =
  37. src.pixels[(y + src_region.top) * src.width + x + src_region.left];
  38. }
  39. }
  40. }
  41. std::optional<ImagePixelData> LoadImage(const std::string& path)
  42. {
  43. File::IOFile file;
  44. file.Open(path, "rb");
  45. std::vector<u8> buffer(file.GetSize());
  46. file.ReadBytes(buffer.data(), file.GetSize());
  47. ImagePixelData image;
  48. std::vector<u8> data;
  49. if (!Common::LoadPNG(buffer, &data, &image.width, &image.height))
  50. return std::nullopt;
  51. image.pixels.resize(image.width * image.height);
  52. for (u32 x = 0; x < image.width; x++)
  53. {
  54. for (u32 y = 0; y < image.height; y++)
  55. {
  56. const u32 index = y * image.width + x;
  57. const auto pixel =
  58. Pixel{data[index * 4], data[index * 4 + 1], data[index * 4 + 2], data[index * 4 + 3]};
  59. image.pixels[index] = pixel;
  60. }
  61. }
  62. return image;
  63. }
  64. bool WriteImage(const std::string& path, const ImagePixelData& image)
  65. {
  66. std::vector<u8> buffer;
  67. buffer.reserve(image.width * image.height * 4);
  68. for (u32 y = 0; y < image.height; ++y)
  69. {
  70. for (u32 x = 0; x < image.width; ++x)
  71. {
  72. const auto index = x + y * image.width;
  73. const auto pixel = image.pixels[index];
  74. buffer.push_back(pixel.r);
  75. buffer.push_back(pixel.g);
  76. buffer.push_back(pixel.b);
  77. buffer.push_back(pixel.a);
  78. }
  79. }
  80. return Common::SavePNG(path, buffer.data(), Common::ImageByteFormat::RGBA, image.width,
  81. image.height, image.width * 4);
  82. }
  83. ImagePixelData Resize(ResizeMode mode, const ImagePixelData& src, u32 new_width, u32 new_height)
  84. {
  85. ImagePixelData result(new_width, new_height);
  86. for (u32 x = 0; x < new_width; x++)
  87. {
  88. const double u = x / static_cast<double>(new_width - 1);
  89. for (u32 y = 0; y < new_height; y++)
  90. {
  91. const double v = y / static_cast<double>(new_height - 1);
  92. switch (mode)
  93. {
  94. case ResizeMode::Nearest:
  95. result.pixels[y * new_width + x] = SampleNearest(src, u, v);
  96. break;
  97. }
  98. }
  99. }
  100. return result;
  101. }
  102. ImagePixelData ResizeKeepAspectRatio(ResizeMode mode, const ImagePixelData& src, u32 new_width,
  103. u32 new_height, const Pixel& background_color)
  104. {
  105. ImagePixelData result(new_width, new_height, background_color);
  106. const double corrected_height = new_width * (src.height / static_cast<double>(src.width));
  107. const double corrected_width = new_height * (src.width / static_cast<double>(src.height));
  108. // initially no borders
  109. u32 top = 0;
  110. u32 left = 0;
  111. ImagePixelData resized;
  112. if (corrected_height <= new_height)
  113. {
  114. // Handle vertical padding
  115. const int diff = new_height - std::trunc(corrected_height);
  116. top = diff / 2;
  117. if (diff % 2 != 0)
  118. {
  119. // If the difference is odd, we need to have one side be slightly larger
  120. top += 1;
  121. }
  122. resized = Resize(mode, src, new_width, corrected_height);
  123. }
  124. else
  125. {
  126. // Handle horizontal padding
  127. const int diff = new_width - std::trunc(corrected_width);
  128. left = diff / 2;
  129. if (diff % 2 != 0)
  130. {
  131. // If the difference is odd, we need to have one side be slightly larger
  132. left += 1;
  133. }
  134. resized = Resize(mode, src, corrected_width, new_height);
  135. }
  136. CopyImageRegion(resized, result, Rect{0, 0, resized.width, resized.height},
  137. Rect{left, top, left + resized.width, top + resized.height});
  138. return result;
  139. }
  140. } // namespace InputCommon