Beatmup
rendering_programs.cpp
Go to the documentation of this file.
1 /*
2  Beatmup image and signal processing library
3  Copyright (C) 2019, lnstadrum
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "rendering_programs.h"
20 #include "gpu/pipeline.h"
21 #include "../gpu/bgl.h"
22 
23 
24 const char
31 
32 
34  IMAGE = 0,
37 };
38 
39 
40 static const char
42  attribute vec2 inVertex;
43  attribute vec2 inTexCoord;
44  uniform mat3 modelview;
45  uniform bool flipVertically;
46  varying vec2 texCoord;
47  void main()
48  {
49  gl_Position = vec4(modelview * vec3(inVertex, 1), 1);
50  gl_Position.x = gl_Position.x * 2.0 - 1.0;
51  if (flipVertically)
52  gl_Position.y = gl_Position.y * 2.0 - 1.0;
53  else
54  gl_Position.y = 1.0 - gl_Position.y * 2.0;
55  texCoord = inTexCoord;
56  }
57  ),
58 
59 
61  attribute vec2 inVertex;
62  attribute vec2 inTexCoord;
63  uniform mat3 modelview; // model plane in pixels -> output in pixels
64  uniform bool flipVertically;
65  uniform mat3 maskMapping;
66  uniform mat3 invImgMapping;
67  varying vec2 texCoord;
68  varying vec2 maskCoord;
69  void main()
70  {
71  gl_Position = vec4(modelview * maskMapping * vec3(inVertex, 1), 1);
72  gl_Position.x = gl_Position.x * 2.0 - 1.0;
73  if (flipVertically)
74  gl_Position.y = gl_Position.y * 2.0 - 1.0;
75  else
76  gl_Position.y = 1.0 - gl_Position.y * 2.0;
77  texCoord = (invImgMapping * vec3(inVertex, 1)).xy; // image texture coordinates
78  maskCoord = inTexCoord;
79  }
80  ),
81 
82 
84  uniform beatmupSampler image;
85  uniform mediump vec4 modulationColor;
86  varying mediump vec2 texCoord;
87  void main() {
88  gl_FragColor = beatmupTexture(image, texCoord.xy).rgba * modulationColor;
89  }
90  ),
91 
92 
94  uniform beatmupSampler image;
95  uniform highp sampler2D mask;
96  uniform highp sampler2D maskLookup;
97  uniform highp float blockSize;
98  uniform highp float pixOffset;
99  uniform mediump vec4 modulationColor;
100  uniform mediump vec4 bgColor;
101  varying mediump vec2 texCoord;
102  varying highp vec2 maskCoord;
103  )
104 #ifdef BEATMUP_OPENGLVERSION_GLES20
106  void main() {
107  highp float o = mod(maskCoord.x, blockSize);
108  highp float a = 0.0;
109  if (texCoord.x >= 0.0 && texCoord.y >= 0.0 && texCoord.x < 1.0 && texCoord.y < 1.0)
110  a = texture2D(
111  maskLookup,
112  vec2(texture2D(mask, vec2(maskCoord.x - o + pixOffset, maskCoord.y)).a, o / blockSize + 0.03125)
113  ).a;
114  gl_FragColor = mix(bgColor, beatmupTexture(image, texCoord.xy).rgba, a) * modulationColor;
115  }
116  ),
117 #else
119  void main() {
120  highp float o = mod(maskCoord.x, blockSize);
121  highp float a = 0.0;
122  if (texCoord.x >= 0.0 && texCoord.y >= 0.0 && texCoord.x < 1.0 && texCoord.y < 1.0)
123  a = texture2D(
124  maskLookup,
125  vec2(texture2D(mask, vec2(maskCoord.x - o + pixOffset, maskCoord.y)).r, o / blockSize + 0.03125)
126  ).a;
127  gl_FragColor = mix(bgColor, beatmupTexture(image, texCoord.xy).rgba, a) * modulationColor;
128  }
129  ),
130 #endif
131 
132 
134  uniform beatmupSampler image;
135  uniform highp sampler2D mask;
136  uniform mediump vec4 modulationColor;
137  uniform mediump vec4 bgColor;
138  varying mediump vec2 texCoord;
139  varying highp vec2 maskCoord;
140  )
141 #ifdef BEATMUP_OPENGLVERSION_GLES20
143  void main() {
144  highp float a = 0.0;
145  if (texCoord.x >= 0.0 && texCoord.y >= 0.0 && texCoord.x < 1.0 && texCoord.y < 1.0)
146  a = texture2D(mask, maskCoord).a;
147  gl_FragColor = mix(bgColor, beatmupTexture(image, texCoord.xy).rgba, a) * modulationColor;
148  }
149  ),
150 #else
152  void main() {
153  highp float a = 0.0;
154  if (texCoord.x >= 0.0 && texCoord.y >= 0.0 && texCoord.x < 1.0 && texCoord.y < 1.0)
155  a = texture2D(mask, maskCoord).r;
156  gl_FragColor = mix(bgColor, beatmupTexture(image, texCoord.xy).rgba, a) * modulationColor;
157  }
158  ),
159 #endif
160 
161 
163  uniform beatmupSampler image;
164  varying mediump vec2 texCoord;
165  varying mediump vec2 maskCoord;
166  uniform highp vec2 borderProfile;
167  uniform highp float slope;
168  uniform highp float border;
169  uniform highp float cornerRadius;
170  uniform mediump vec4 modulationColor;
171  uniform mediump vec4 bgColor;
172 
173  void main() {
174  highp vec2 cornerCoords = vec2(cornerRadius - min(maskCoord.x, 1.0 - maskCoord.x) * borderProfile.x, cornerRadius - min(maskCoord.y, 1.0 - maskCoord.y) * borderProfile.y);
175  highp float r;
176  if (cornerRadius > 0.0 && cornerCoords.x > 0.0 && cornerCoords.y > 0.0)
177  r = length(cornerCoords / cornerRadius) * cornerRadius;
178  else
179  r = max(cornerCoords.x, cornerCoords.y);
180  if (texCoord.x < 0.0 || texCoord.y < 0.0 || texCoord.x >= 1.0 || texCoord.y >= 1.0)
181  gl_FragColor = bgColor;
182  else
183  gl_FragColor = beatmupTexture(image, texCoord.xy).rgba;
184  gl_FragColor = gl_FragColor * clamp((cornerRadius - border - r) / (slope + 0.00098), 0.0, 1.0) * modulationColor;
185  }
186  );
187 
188 
189 using namespace Beatmup;
190 using namespace GL;
191 
192 
194 private:
195  GLuint hMaskLookups[3]; //!< texture containing mask values for 1, 2 and 4 bpp
197 
198 public:
199  Backend() : maskLookupsSet(false) {}
200  ~Backend() {}
201 
202 
204  glActiveTexture(GL_TEXTURE0 + TextureUnits::MASK_LOOKUP);
205 
206  if (!maskLookupsSet) {
207  // masked blending lookup textures initialization
208  unsigned char mask1[8][256], mask2[4][256], mask4[2][256];
209  for (int v = 0; v < 256; ++v) {
210  for (int o = 0; o < 8; ++o)
211  mask1[o][v] = ((v >> o) % 2) * 255;
212  for (int o = 0; o < 4; ++o)
213  mask2[o][v] = (int)((v >> (2 * o)) % 4) * 255 / 3;
214  for (int o = 0; o < 2; ++o)
215  mask4[o][v] = (int)((v >> (4 * o)) % 16) * 255 / 15;
216  }
217  glGenTextures(3, hMaskLookups);
218  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
219  glBindTexture(GL_TEXTURE_2D, hMaskLookups[0]);
220  glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 256, 8, 0, GL_ALPHA, GL_UNSIGNED_BYTE, mask1);
221  glBindTexture(GL_TEXTURE_2D, hMaskLookups[1]);
222  glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 256, 4, 0, GL_ALPHA, GL_UNSIGNED_BYTE, mask2);
223  glBindTexture(GL_TEXTURE_2D, hMaskLookups[2]);
224  glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 256, 2, 0, GL_ALPHA, GL_UNSIGNED_BYTE, mask4);
225  maskLookupsSet = true;
226  }
227 
228  switch (format) {
229  case BinaryMask:
230  glBindTexture(GL_TEXTURE_2D, hMaskLookups[0]);
231  break;
232  case QuaternaryMask:
233  glBindTexture(GL_TEXTURE_2D, hMaskLookups[1]);
234  break;
235  case HexMask:
236  glBindTexture(GL_TEXTURE_2D, hMaskLookups[2]);
237  break;
238  default:
239  throw GLException("Mask bitmap pixel format is not supported");
240  }
241  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
242  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
243  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
244  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
245  }
246 };
247 
248 
250  backend(new Backend()), currentGlProgram(nullptr), defaultVertexShader(*gpu, VERTEX_SHADER_BLEND, Extensions::BEATMUP_DIALECT)
251 {}
252 
253 
255  delete backend;
256 }
257 
258 
260  auto& map = programs;
261  auto it = map.find(operation);
262  if (it != map.end())
263  return it->second;
264 
265  // maps shader types to internally handled shaders
266  std::string fragmentCode;
268  switch (operation) {
270  ext = ext + Extensions::EXTERNAL_TEXTURE;
271  case Operation::BLEND:
272  fragmentCode = FRAGMENT_SHADER_BLEND;
273  break;
274 
276  ext = ext + Extensions::EXTERNAL_TEXTURE;
278  fragmentCode = FRAGMENT_SHADER_BLENDMASK;
279  break;
280 
282  ext = ext + Extensions::EXTERNAL_TEXTURE;
284  fragmentCode = FRAGMENT_SHADER_BLENDMASK_8BIT;
285  break;
286 
288  ext = ext + Extensions::EXTERNAL_TEXTURE;
290  fragmentCode = FRAGMENT_SHADER_BLENDSHAPE;
291  break;
292 
293  default:
294  Insanity::insanity("Invalid rendering operation");
295  }
296 
297  // instantiate shaders
298  const bool useDefaultVertexShader = operation == Operation::BLEND || operation == Operation::BLEND_EXT;
299  VertexShader* vertexShader = useDefaultVertexShader ? &defaultVertexShader : new VertexShader(*gpu, VERTEX_SHADER_BLENDMASK, ext);
300  FragmentShader fragmentShader(*gpu, fragmentCode, ext);
301 
302  // link program
303  Program& glProgram = programs.emplace(
304  std::piecewise_construct,
305  std::forward_as_tuple(operation),
306  std::forward_as_tuple(*gpu, *vertexShader, fragmentShader)
307  ).first->second;
308  if (!useDefaultVertexShader)
309  delete vertexShader;
310  return glProgram;
311 }
312 
313 
315  Program& glProgram = getProgram(gpu, operation);
316  currentProgram = operation;
317  currentGlProgram = &glProgram;
318  glProgram.enable(*gpu);
320  glProgram.setInteger("image", TextureUnits::IMAGE);
321  switch (operation) {
324  glProgram.setInteger("maskLookup", TextureUnits::MASK_LOOKUP);
327  glProgram.setInteger("mask", TextureUnits::MASK);
328  default: break;
329  }
330  maskSetUp = false;
331 }
332 
333 
335  if (!currentGlProgram)
336  RuntimeError("No current program");
337  return *currentGlProgram;
338 }
339 
340 
342  Program& program = getCurrentProgram();
344  if (mask.getBitsPerPixel() < 8) {
346  program.setFloat("blockSize", 8.0f / mask.getBitsPerPixel() / mask.getWidth());
347  program.setFloat("pixOffset", 0.5f / mask.getWidth());
348  }
349  maskSetUp = true;
350 }
351 
352 
353 void RenderingPrograms::blend(bool onScreen) {
354 #ifdef BEATMUP_DEBUG
356  DebugAssertion::check(maskSetUp, "Mask was not set up in masked blending");
357 #endif
358 
359  getCurrentProgram().setInteger(VERTICAL_FLIP_ID, onScreen ? 0 : 1);
360  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
361 
362 #ifdef BEATMUP_DEBUG
363  GLException::check("blending");
364 #endif
365 }
366 
367 
369  // choose program
370  switch (content.getTextureFormat()) {
371  case TextureHandler::TextureFormat::OES_Ext:
373  break;
374  default:
376  break;
377  }
378 
379  // setting texture coords, bitmap size and updating buffer data in GPU
381  (float)(output ? output->getWidth() : gpu->getDisplayResolution().getWidth() ) / content.getWidth(),
382  (float)(output ? output->getHeight() : gpu->getDisplayResolution().getHeight()) / content.getHeight()
383  ));
384 
386  currentGlProgram->setVector4("modulationColor", 1.0f, 1.0f, 1.0f, 1.0f);
388  blend(output == nullptr);
389 }
390 
391 
393  return defaultVertexShader;
394 }
A very basic class for any image.
virtual const PixelFormat getPixelFormat() const =0
Pixel format of the bitmap.
const unsigned char getBitsPerPixel() const
Returns number of bits per pixel stored in each bitmap.
static const AffineMapping IDENTITY
Definition: geometry.h:717
static const CustomRectangle UNIT_SQUARE
Definition: geometry.h:322
void setInteger(const std::string &name, const int value, bool safe=false)
Assigns a value to a specific integer variable in the program.
Definition: program.cpp:308
void enable(const GraphicPipeline &gpu)
Definition: program.cpp:250
void setFloat(const std::string &name, const float value, bool safe=false)
Assigns a value to a specific floating point variable in the program.
Definition: program.cpp:347
void setVector4(const std::string &name, const float x, const float y, const float z, const float w)
Definition: program.cpp:378
void setMatrix3(const std::string &name, const Matrix2 &mat, const Point &pos)
Definition: program.cpp:402
GLSL fragment shader.
Definition: program.h:107
GPU exception.
Definition: bgl.h:56
static void check(const std::string &info)
Definition: bgl.h:62
Regular OpenGL program.
Definition: program.h:229
static const char * TEXTURE_COORD_ATTRIB_NAME
texture coordinate attribute name in vertex shaders
void paveBackground(GraphicPipeline *gpu, TextureHandler &content, GL::TextureHandler *output)
Fills background with a repeated texture taking 1 pixel of this texture per 1 pixel of the output.
Program & getProgram(const GraphicPipeline *gpu, Operation program)
std::map< Operation, RenderingProgram > programs
void enableProgram(GraphicPipeline *gpu, Operation program)
Select and enable a common program.
static const char * TEXTURE_COORDINATES_ID
Texture coordinates shader variable name in vertex shader.
Operation
Standard rendering operations.
@ MASKED_BLEND_EXT
blending an image through a pixelwise mask (texture extension)
@ MASKED_8BIT_BLEND_EXT
blending an image through a 8 bit pixelwise mask (texture extension)
@ BLEND_EXT
default blending using a texture extension
@ MASKED_8BIT_BLEND
blending an image through a 8 bit pixelwise mask
@ MASKED_BLEND
blending an image through a pixelwise mask
@ SHAPED_BLEND_EXT
shaping an image (texture extension)
@ BLEND
default blending of a single image
static const char * VERTEX_COORD_ATTRIB_NAME
vertex coordinate attribute name in vertex shaders
static const char * VERTICAL_FLIP_ID
Vertical flipping variable name in vertex shader.
const VertexShader & getDefaultVertexShader(const GraphicPipeline *gpu) const
static const char * DECLARE_TEXTURE_COORDINATES_IN_FRAG
Declaring texture coordinates in fragment shader.
static const char * MODELVIEW_MATRIX_ID
Modelview matrix (mapping input geometry to output) shader variable name in vertex shader.
void blend(bool onScreen)
Performs the blending operation.
RenderingPrograms(GraphicPipeline *gpu)
void bindMask(GraphicPipeline *gpu, AbstractBitmap &mask)
Binds a mask to a masked rendering program.
virtual const int getHeight() const =0
Height of the texture in pixels.
virtual const int getWidth() const =0
Width of the texture in pixels.
GLSL vertex shader.
Definition: program.h:93
Internal low-level GPU control API.
Definition: pipeline.h:33
const ImageResolution & getDisplayResolution() const
Definition: pipeline.cpp:916
void setTextureCoordinates(const Rectangle &coords)
Specifies texture coordinates for the next rendering pass.
Definition: pipeline.cpp:966
void bind(GL::TextureHandler &texture, size_t texUnit, const TextureParam param)
Definition: pipeline.cpp:881
unsigned int getWidth() const
unsigned int getHeight() const
static void insanity(const char *message)
Definition: exception.h:136
GLuint hMaskLookups[3]
texture containing mask values for 1, 2 and 4 bpp
Extensions
Supported OpenGL estensions.
Definition: program.h:63
@ BEATMUP_DIALECT
pseudo-extension enabling Beatmup GLSL dialect
Definition: program.h:65
@ EXTERNAL_TEXTURE
GL_OES_EGL_image_external_essl3 if available or GL_OES_EGL_image_external.
Definition: program.h:66
@ QuaternaryMask
2 bits per pixel
@ BinaryMask
1 bit per pixel
@ HexMask
4 bits per pixel
CustomRectangle< float > Rectangle
Definition: geometry.h:627
@ INTERP_NEAREST
nearest neighbor pixel interpolation
@ REPEAT
wrapping the texture by repeating instead of clamping to edge
CustomPoint< numeric > min(const CustomPoint< numeric > &a, const CustomPoint< numeric > &b)
Definition: geometry.h:724
CustomPoint< numeric > max(const CustomPoint< numeric > &a, const CustomPoint< numeric > &b)
Definition: geometry.h:728
#define BEATMUP_SHADER_CODE(...)
Definition: program.h:26
static const char * VERTEX_SHADER_BLEND
static const char * VERTEX_SHADER_BLENDMASK
static const char * FRAGMENT_SHADER_BLENDMASK
static const char * FRAGMENT_SHADER_BLENDSHAPE
@ MASK_LOOKUP
static const char * FRAGMENT_SHADER_BLENDMASK_8BIT
static const char * FRAGMENT_SHADER_BLEND
JNIEnv jobject jint format
jobject jlong jint jint jint r
jobject jlong jint jint jint jint jint jint a
std::string content
jlong jintArray xy
JNIEnv jlong jfloat jfloat jfloat v