GLImage.cc 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #include "GLImage.hh"
  2. #include "GLContext.hh"
  3. #include "SDLSurfacePtr.hh"
  4. #include "MSXException.hh"
  5. #include "Math.hh"
  6. #include "PNG.hh"
  7. #include "build-info.hh"
  8. #include <cstdlib>
  9. #include <SDL.h>
  10. using std::string;
  11. using namespace gl;
  12. namespace openmsx {
  13. static gl::Texture loadTexture(
  14. SDLSurfacePtr surface, ivec2& size)
  15. {
  16. size = ivec2(surface->w, surface->h);
  17. // Make a copy to convert to the correct pixel format.
  18. // TODO instead directly load the image in the correct format.
  19. SDLSurfacePtr image2(size[0], size[1], 32,
  20. OPENMSX_BIGENDIAN ? 0xFF000000 : 0x000000FF,
  21. OPENMSX_BIGENDIAN ? 0x00FF0000 : 0x0000FF00,
  22. OPENMSX_BIGENDIAN ? 0x0000FF00 : 0x00FF0000,
  23. OPENMSX_BIGENDIAN ? 0x000000FF : 0xFF000000);
  24. SDL_Rect area;
  25. area.x = 0;
  26. area.y = 0;
  27. area.w = size[0];
  28. area.h = size[1];
  29. SDL_SetSurfaceBlendMode(surface.get(), SDL_BLENDMODE_NONE);
  30. SDL_BlitSurface(surface.get(), &area, image2.get(), &area);
  31. gl::Texture texture(true); // enable interpolation
  32. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size[0], size[1], 0,
  33. GL_RGBA, GL_UNSIGNED_BYTE, image2->pixels);
  34. return texture;
  35. }
  36. static gl::Texture loadTexture(
  37. const string& filename, ivec2& size)
  38. {
  39. SDLSurfacePtr surface(PNG::load(filename, false));
  40. try {
  41. return loadTexture(std::move(surface), size);
  42. } catch (MSXException& e) {
  43. throw MSXException("Error loading image ", filename, ": ",
  44. e.getMessage());
  45. }
  46. }
  47. GLImage::GLImage(OutputSurface& /*output*/, const string& filename)
  48. : texture(loadTexture(filename, size))
  49. {
  50. }
  51. GLImage::GLImage(OutputSurface& /*output*/, const string& filename, float scalefactor)
  52. : texture(loadTexture(filename, size))
  53. {
  54. size = trunc(vec2(size) * scalefactor);
  55. checkSize(size);
  56. }
  57. GLImage::GLImage(OutputSurface& /*output*/, const string& filename, ivec2 size_)
  58. : texture(loadTexture(filename, size))
  59. {
  60. checkSize(size_);
  61. size = size_;
  62. }
  63. GLImage::GLImage(OutputSurface& /*output*/, ivec2 size_, unsigned rgba)
  64. : texture(gl::Null())
  65. {
  66. checkSize(size_);
  67. size = size_;
  68. borderSize = 0;
  69. borderR = borderG = borderB = borderA = 0; // not used, but avoid (harmless) UMR
  70. for (int i = 0; i < 4; ++i) {
  71. bgR[i] = (rgba >> 24) & 0xff;
  72. bgG[i] = (rgba >> 16) & 0xff;
  73. bgB[i] = (rgba >> 8) & 0xff;
  74. unsigned alpha = (rgba >> 0) & 0xff;
  75. bgA[i] = (alpha == 255) ? 256 : alpha;
  76. }
  77. initBuffers();
  78. }
  79. GLImage::GLImage(OutputSurface& /*output*/, ivec2 size_, const unsigned* rgba,
  80. int borderSize_, unsigned borderRGBA)
  81. : texture(gl::Null())
  82. {
  83. checkSize(size_);
  84. size = size_;
  85. borderSize = borderSize_;
  86. for (int i = 0; i < 4; ++i) {
  87. bgR[i] = (rgba[i] >> 24) & 0xff;
  88. bgG[i] = (rgba[i] >> 16) & 0xff;
  89. bgB[i] = (rgba[i] >> 8) & 0xff;
  90. unsigned alpha = (rgba[i] >> 0) & 0xff;
  91. bgA[i] = (alpha == 255) ? 256 : alpha;
  92. }
  93. borderR = (borderRGBA >> 24) & 0xff;
  94. borderG = (borderRGBA >> 16) & 0xff;
  95. borderB = (borderRGBA >> 8) & 0xff;
  96. unsigned alpha = (borderRGBA >> 0) & 0xff;
  97. borderA = (alpha == 255) ? 256 : alpha;
  98. initBuffers();
  99. }
  100. GLImage::GLImage(OutputSurface& /*output*/, SDLSurfacePtr image)
  101. : texture(loadTexture(std::move(image), size))
  102. {
  103. }
  104. void GLImage::initBuffers()
  105. {
  106. vao.bind();
  107. // border
  108. uint8_t indices[10] = { 4,0,5,1,6,2,7,3,4,0 };
  109. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementsBuffer.get());
  110. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
  111. vao.unbind();
  112. }
  113. void GLImage::draw(OutputSurface& /*output*/, ivec2 pos, uint8_t r, uint8_t g, uint8_t b, uint8_t alpha)
  114. {
  115. // 4-----------------7
  116. // | |
  117. // | 0---------3 |
  118. // | | | |
  119. // | | | |
  120. // | 1-------- 2 |
  121. // | |
  122. // 5-----------------6
  123. int bx = (size[0] > 0) ? borderSize : -borderSize;
  124. int by = (size[1] > 0) ? borderSize : -borderSize;
  125. ivec2 positions[8] = {
  126. pos + ivec2( + bx, + by), // 0
  127. pos + ivec2( + bx, size[1] - by), // 1
  128. pos + ivec2(size[0] - bx, size[1] - by), // 2
  129. pos + ivec2(size[0] - bx, + by), // 3
  130. pos + ivec2(0 , 0 ), // 4
  131. pos + ivec2(0 , size[1] ), // 5
  132. pos + ivec2(size[0] , size[1] ), // 6
  133. pos + ivec2(size[0] , 0 ), // 7
  134. };
  135. glEnable(GL_BLEND);
  136. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  137. vao.bind();
  138. glBindBuffer(GL_ARRAY_BUFFER, vbo[0].get());
  139. glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
  140. if (texture.get()) {
  141. vec2 tex[4] = {
  142. vec2(0.0f, 0.0f),
  143. vec2(0.0f, 1.0f),
  144. vec2(1.0f, 1.0f),
  145. vec2(1.0f, 0.0f),
  146. };
  147. gl::context->progTex.activate();
  148. glUniform4f(gl::context->unifTexColor,
  149. r / 255.0f, g / 255.0f, b / 255.0f, alpha / 255.0f);
  150. glUniformMatrix4fv(gl::context->unifTexMvp, 1, GL_FALSE,
  151. &gl::context->pixelMvp[0][0]);
  152. ivec2* base = nullptr;
  153. glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, base + 4);
  154. glEnableVertexAttribArray(0);
  155. glBindBuffer(GL_ARRAY_BUFFER, vbo[1].get());
  156. glBufferData(GL_ARRAY_BUFFER, sizeof(tex), tex, GL_STATIC_DRAW);
  157. glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
  158. glEnableVertexAttribArray(1);
  159. texture.bind();
  160. glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  161. glDisableVertexAttribArray(1);
  162. glDisableVertexAttribArray(0);
  163. } else {
  164. assert(r == 255);
  165. assert(g == 255);
  166. assert(b == 255);
  167. gl::context->progFill.activate();
  168. glUniformMatrix4fv(gl::context->unifFillMvp, 1, GL_FALSE,
  169. &gl::context->pixelMvp[0][0]);
  170. glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, nullptr);
  171. glEnableVertexAttribArray(0);
  172. glVertexAttrib4f(1, borderR / 255.0f, borderG / 255.0f, borderB / 255.0f,
  173. (borderA * alpha) / (255.0f * 255.0f));
  174. if ((2 * borderSize >= abs(size[0])) ||
  175. (2 * borderSize >= abs(size[1]))) {
  176. // only border
  177. glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
  178. } else {
  179. // border
  180. if (borderSize > 0) {
  181. glDrawElements(GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_BYTE, nullptr);
  182. }
  183. // interior
  184. uint8_t col[4][4] = {
  185. { bgR[0], bgG[0], bgB[0], uint8_t((bgA[0] * alpha) / 256) },
  186. { bgR[2], bgG[2], bgB[2], uint8_t((bgA[2] * alpha) / 256) },
  187. { bgR[3], bgG[3], bgB[3], uint8_t((bgA[3] * alpha) / 256) },
  188. { bgR[1], bgG[1], bgB[1], uint8_t((bgA[1] * alpha) / 256) },
  189. };
  190. glBindBuffer(GL_ARRAY_BUFFER, vbo[2].get());
  191. glBufferData(GL_ARRAY_BUFFER, sizeof(col), col, GL_STATIC_DRAW);
  192. glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, nullptr);
  193. glEnableVertexAttribArray(1);
  194. glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  195. glDisableVertexAttribArray(1);
  196. glBindBuffer(GL_ARRAY_BUFFER, 0);
  197. }
  198. glDisableVertexAttribArray(0);
  199. }
  200. vao.unbind();
  201. glDisable(GL_BLEND);
  202. }
  203. } // namespace openmsx