Beatmup
scene.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 "../basic_types.h"
20 #include "../scene/scene.h"
21 #include "../exception.h"
22 #include "../color/constants.h"
23 #include <map>
24 #include <vector>
25 #include <cmath>
26 
27 using namespace Beatmup;
28 
29 
30 std::string Scene::SceneIntegrityError::getSceneLog(const Scene& scene, const std::string prefix, int recursionLeft) const {
31  std::string log = prefix + "Scene has " + std::to_string(scene.getLayerCount()) + " layers\n";
32  const int N = scene.getLayerCount();
33  for (int i = 0; i < N; i++) {
34  Layer& layer = scene.getLayer(i);
35  log += prefix + "\t" + layer.getName() + " [";
36  switch (layer.getType()) {
38  log += "bitmap";
39  BitmapLayer& casted = layer.castTo<BitmapLayer&>();
40  if (!casted.getBitmap())
41  log += ", no bitmap";
42  else
43  log += ", " + casted.getBitmap()->toString();
44  }
45  break;
46 
48  log += "masked bitmap";
50  if (!casted.getBitmap())
51  log += ", no bitmap";
52  else
53  log += ", bitmap " + casted.getBitmap()->toString();
54  if (!casted.getMask())
55  log += ", no mask";
56  else
57  log += ", mask " + casted.getMask()->toString();
58  }
59  break;
60 
62  log += "bitmap";
63  BitmapLayer& casted = layer.castTo<BitmapLayer&>();
64  if (!casted.getBitmap())
65  log += ", no content";
66  else
67  log += ", " + casted.getBitmap()->toString();
68  }
69  break;
70 
72  log += "shader";
73  break;
74 
76  log += "scene";
77  break;
78  }
79  if (layer.isPhantom())
80  log += ", phantom";
81  log += "]\n";
83  if (recursionLeft > 0)
84  log += getSceneLog(scene, prefix + "\t", recursionLeft - 1);
85  }
86  }
87  return log;
88 }
89 
90 
91 Scene::SceneIntegrityError::SceneIntegrityError(const std::string reason, const Scene& scene) :
92  Exception( (std::string(reason) + "\n" + getSceneLog(scene, "")).c_str() )
93 {}
94 
95 
96 
97 std::string generateUniqueLayerName(const Scene& scene, const char* prefix = "") {
98  int n = 1;
99  std::string candidate;
100  while (scene.getLayer((candidate = prefix + (" #" + std::to_string(n))).c_str()))
101  n++;
102  return candidate;
103 }
104 
105 
107 
108 
110  for (auto it : layers)
111  delete it;
112 }
113 
114 
116  return newLayer<BitmapLayer>(name);
117 }
118 
119 
121  return newLayer<BitmapLayer>(generateUniqueLayerName(*this, "Bitmap layer").c_str());
122 }
123 
124 
126  return newLayer<MaskedBitmapLayer>(name);
127 }
128 
129 
131  return newLayer<MaskedBitmapLayer>(generateUniqueLayerName(*this, "Masked bitmap layer").c_str());
132 }
133 
134 
136  return newLayer<ShapedBitmapLayer>(name);
137 }
138 
139 
141  return newLayer<ShapedBitmapLayer>(generateUniqueLayerName(*this, "Shaped bitmap layer").c_str());
142 }
143 
144 
146  return newLayer<ShadedBitmapLayer>(name);
147 }
148 
149 
151  return newLayer<ShadedBitmapLayer>(generateUniqueLayerName(*this, "Shaped bitmap layer").c_str());
152 }
153 
154 
156  SceneLayer* l = new SceneLayer(scene);
157  l->setName(generateUniqueLayerName(*this, "Scene layer").c_str());
158  layers.push_back(l);
159  return *l;
160 }
161 
162 
163 Scene::Layer* Scene::getLayer(const char* name) const {
164  for (auto l : layers)
165  if (l->getName() == name)
166  return l;
167  return nullptr;
168 }
169 
170 
172  return *layers[index];
173 }
174 
175 Scene::Layer* Scene::getLayer(float x, float y, unsigned int recursionDepth) const {
176  for (auto it = layers.crbegin(); it != layers.crend(); it++)
177  if (!(*it)->isPhantom()) {
178  if ((*it)->getType() == Scene::Layer::Type::SceneLayer) {
179  Layer *result = (*it)->getChild(x, y, recursionDepth + 1);
180  if (result)
181  return result;
182  } else {
183  if ((*it)->testPoint(x, y))
184  return *it;
185  }
186  }
187  return nullptr;
188 }
189 
190 
191 int Scene::getLayerIndex(const Layer& layer) const {
192  for (size_t i = 0; i < layers.size(); ++i)
193  if (layers[i] == &layer)
194  return (int)i;
195  return -1;
196 }
197 
198 
199 int Scene::getLayerCount() const {
200  return (int)layers.size();
201 }
202 
203 
205  for (auto it = layers.crbegin(); it != layers.crend(); it++) {
206  if (&layer == *it) {
208  return true;
209  }
210 
211  // scene containers are checked recursively
212  if ((*it)->getType() == Scene::Layer::Type::SceneLayer) {
213  const SceneLayer& container = (*it)->castTo<SceneLayer>();
214  if (container.getScene().resolveMapping(layer, mapping)) {
215  mapping = container.getMapping() * mapping;
216  return true;
217  }
218  }
219  }
220  return false;
221 }
222 
223 
225  if (getLayerIndex(layer) >= 0)
226  throw SceneIntegrityError(std::string("Layer ") + layer.getName() + " is already in the scene", *this);
227  layers.push_back(&layer);
228 }
229 
230 
232  Layer* l = layers[index];
233  layers.erase(layers.begin() + index);
234  return l;
235 }
236 
237 
238 Scene::Layer::Layer(Type type) : visible(true), phantom(false), type(type) {}
239 
240 bool Scene::Layer::testPoint(float x, float y) const {
241  return mapping.isPointInside(Point(x, y));
242 }
243 
244 Scene::Layer* Scene::Layer::getChild(float, float, unsigned int) const {
245  return nullptr;
246 }
247 
248 
250 
251 bool Scene::SceneLayer::testPoint(float x, float y) const {
252  return getChild(x, y) != nullptr;
253 }
254 
255 Scene::Layer* Scene::SceneLayer::getChild(float x, float y, unsigned int recursionDepth) const {
256  static const unsigned int MAX_RECURSION_DEPTH = 256;
257  if (recursionDepth >= MAX_RECURSION_DEPTH)
258  return nullptr;
259  Point p = mapping.getInverse(x, y);
260  return scene.getLayer(p.x, p.y, recursionDepth);
261 }
262 
263 
266 {}
267 
269  Layer(type), invAr(0), bitmap(nullptr), bitmapMapping(), modulation(Color::WHITE)
270 {}
271 
272 
274  if (bitmap)
275  context.lockBitmap(bitmap);
276  return bitmap;
277 }
278 
280  if (content) {
281  invAr = content->getInvAspectRatio();
282  context.getProgram().bindSampler(context.getGpu(), *content, "image", TextureParam::INTERP_LINEAR);
283  }
284  else
285  invAr = 0;
286 
287  context.getProgram().setVector4("modulationColor", modulation);
288 }
289 
290 
292  GL::TextureHandler* content = resolveContent(context);
293  if (content) {
294  // program selection
295  switch (content->getTextureFormat()) {
296  case GL::TextureHandler::TextureFormat::OES_Ext:
298  break;
299  default:
301  break;
302  }
303 
304  configure(context, content);
305 
306  AffineMapping arMapping(context.getMapping() * bitmapMapping);
307  arMapping.matrix.scale(1.0f, invAr);
309  context.blend();
310  }
311 }
312 
313 
314 bool Scene::BitmapLayer::testPoint(float x, float y) const {
315  // no bitmap - no deal (if the source is set to bitmap)
316  if (!bitmap)
317  return false;
318  return (mapping * bitmapMapping).isPointInside(x, y, 1, invAr);
319 }
320 
321 
323  BitmapLayer(type), maskMapping(), bgColor(Beatmup::Color::ZERO)
324 {}
325 
328  AffineMapping arImgMapping(bitmapMapping), arMaskMapping(maskMapping);
329  arImgMapping.matrix.scale(1.0f, invAr);
330  arMaskMapping.matrix.scale(1.0f, invAr);
331  context.getProgram().setVector4("bgColor", bgColor);
333  context.getProgram().setMatrix3("invImgMapping", arImgMapping.getInverse() * arMaskMapping);
334  context.getProgram().setMatrix3("maskMapping", arMaskMapping);
335 }
336 
337 
340 {}
341 
342 
344  GL::TextureHandler* content = resolveContent(context);
345  if (content && mask) {
346  const bool mask8bit = mask->getPixelFormat() == PixelFormat::SingleByte;
347 
348  // program selection
349  switch (content->getTextureFormat()) {
350  case GL::TextureHandler::TextureFormat::OES_Ext:
351  context.enableProgram(mask8bit ?
354  );
355  break;
356  default:
357  context.enableProgram(mask8bit ?
360  break;
361  }
362 
363  context.lockBitmap(mask);
364 
366  context.bindMask(*mask);
367  context.blend();
368  }
369 }
370 
371 
372 bool Scene::MaskedBitmapLayer::testPoint(float x, float y) const {
373  // no bitmap - no deal (if the source is set to bitmap)
374  if (!bitmap)
375  return false;
376  if (mask) {
377  RuntimeError::check(mask->isUpToDate(ProcessingTarget::CPU), "Mask is out of date on CPU.");
378  const Point p = (mapping * maskMapping).getInverse(x, y);
379  int
380  w = floorf_fast(mask->getWidth() * p.x),
381  h = floorf_fast(mask->getHeight() * p.y);
382  if (0 <= w && w < mask->getWidth() && 0 <= h && h < mask->getHeight()) {
384  bool result = mask->getPixelInt(w, h) > 0;
385  return result;
386  }
387  return false;
388  }
389  return BitmapLayer::testPoint(x, y);
390 }
391 
392 
395  borderWidth(0), slopeWidth(0), cornerRadius(0), inPixels(true)
396 {}
397 
398 
401 
402  if (content) {
403  // program selection
404  switch (content->getTextureFormat()) {
405  case GL::TextureHandler::TextureFormat::OES_Ext:
407  break;
408  default:
410  break;
411  }
412 
414 
415  // computing border profile in pixels
416  AffineMapping arMaskMapping(maskMapping);
417  arMaskMapping.matrix.scale(1.0f, invAr);
418 
419  Matrix2 mat = context.getMapping().matrix * arMaskMapping.matrix;
420  mat.prescale(1.0f, context.getOutputResolution().getInvAspectRatio());
421 
422  const float scale = inPixels ? context.getOutputWidth() : 1;
423  const Point borderProfile(scale * mat.getScalingX(), scale * mat.getScalingY());
424  context.getProgram().setVector2("borderProfile", borderProfile.x, borderProfile.y);
425  context.getProgram().setFloat("cornerRadius", cornerRadius + borderWidth);
426  context.getProgram().setFloat("slope", slopeWidth);
427  context.getProgram().setFloat("border", borderWidth);
428 
429  context.blend();
430  }
431 }
432 
433 
434 bool Scene::ShapedBitmapLayer::testPoint(float x, float y) const {
435  // no bitmap - no deal (if the source is set to bitmap)
436  if (!bitmap)
437  return false;
438  return (mapping * maskMapping).isPointInside(x, y, 1, invAr) && BitmapLayer::testPoint(x, y);
439 }
440 
441 
444  shader(nullptr)
445 {}
446 
447 
449  if (!shader)
450  return;
452 
453  if (content)
454  invAr = content->getInvAspectRatio();
455  else
456  invAr = 0;
457 
458  AffineMapping arMapping(context.getMapping());
459  if (content)
460  arMapping.matrix.scale(1.0f, invAr);
461 
462  shader->prepare(context.getGpu(), content, TextureParam::INTERP_LINEAR, nullptr, arMapping);
463  shader->setInteger(GL::RenderingPrograms::VERTICAL_FLIP_ID, context.isRenderingOnScreen() ? 0 : 1);
464  shader->process(context.getGpu());
465 }
Locks a bitmap for reading on CPU.
std::string toString() const
Retruns a string describing the bitmap.
2x3 affine mapping containing a 2x2 matrix and a 2D point
Definition: geometry.h:639
AffineMapping getInverse() const
Returns inverse mapping.
Definition: geometry.cpp:52
bool isPointInside(const Point &point) const
Tests whether a point from the output domain is inside the input axes span.
Definition: geometry.cpp:87
void scale(numeric factor)
Definition: geometry.h:399
Base class for all exceptions.
Definition: exception.h:37
void bindSampler(GraphicPipeline &gpu, GL::TextureHandler &image, const char *uniformId, TextureParam param)
Definition: program.cpp:474
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
void setVector2(const std::string &name, const float x, const float y)
Definition: program.cpp:362
@ 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 * VERTICAL_FLIP_ID
Vertical flipping variable name in vertex shader.
static const char * MODELVIEW_MATRIX_ID
Modelview matrix (mapping input geometry to output) shader variable name in vertex shader.
Stores the rendering context: current program, current mapping in the scene space,...
void blend()
Initiates the rendering operation.
const float getOutputWidth() const
void bindMask(AbstractBitmap &mask)
const AffineMapping & getMapping() const
const bool isRenderingOnScreen() const
GraphicPipeline & getGpu()
const ImageResolution & getOutputResolution() const
void enableProgram(GL::RenderingPrograms::Operation operation)
void lockBitmap(AbstractBitmap *bitmap)
static void check(const bool condition, const std::string &message)
Definition: exception.h:64
Layer having an image to render.
Definition: scene.h:158
GL::TextureHandler * resolveContent(RenderingContext &context)
Definition: scene.cpp:273
const AbstractBitmap * getBitmap() const
Definition: scene.h:181
void render(RenderingContext &context)
Definition: scene.cpp:291
void configure(RenderingContext &context, GL::TextureHandler *content)
Configures current rendering program to render this layer.
Definition: scene.cpp:279
bool testPoint(float x, float y) const
Tests if a given point falls in the layer.
Definition: scene.cpp:314
Layer containing a bitmap and a mask applied to the bitmap when rendering.
Definition: scene.h:197
void configure(RenderingContext &context, GL::TextureHandler *content)
Definition: scene.cpp:326
Abstract scene layer having name, type, geometry and some content to display.
Definition: scene.h:64
virtual bool testPoint(float x, float y) const
Tests if a given point falls in the layer.
Definition: scene.cpp:240
@ SceneLayer
layer containing a scene
@ MaskedBitmapLayer
layer displaying a bitmap with mask
@ ShadedBitmapLayer
layer displaying a bitmap through a custom fragment shader
@ BitmapLayer
layer displaying a bitmap
@ ShapedBitmapLayer
layer displaying a bitmap within a shape
void setName(const char *name)
Definition: scene.h:95
virtual Layer * getChild(float x, float y, unsigned int recursionDepth=0) const
Picks a child layer at given point, if any.
Definition: scene.cpp:244
Type getType() const
Definition: scene.h:92
const std::string & getName() const
Definition: scene.h:94
bool isPhantom() const
Returns true if the layer is ignored when searching a layer by point.
Definition: scene.h:119
AffineMapping & getMapping()
Definition: scene.h:97
LayerType & castTo() const
Definition: scene.h:132
Layer(const Layer &)=delete
disabling copying constructor
Bitmap layer using another bitmap as a mask.
Definition: scene.h:217
bool testPoint(float x, float y) const
Tests if a given point falls in the layer.
Definition: scene.cpp:372
void render(RenderingContext &context)
Definition: scene.cpp:343
const AbstractBitmap * getMask() const
Definition: scene.h:226
std::string getSceneLog(const Scene &scene, const std::string prefix, int recursionLeft=100) const
Definition: scene.cpp:30
SceneIntegrityError(const std::string reason, const Scene &scene)
Definition: scene.cpp:91
Layer containing an entire scene.
Definition: scene.h:141
SceneLayer(const Beatmup::Scene &scene)
Definition: scene.cpp:249
Layer * getChild(float x, float y, unsigned int recursionDepth=0) const
Picks a child layer at given point, if any.
Definition: scene.cpp:255
bool testPoint(float x, float y) const
Tests if a given point falls in the layer.
Definition: scene.cpp:251
const Beatmup::Scene & getScene() const
Definition: scene.h:147
Bitmap layer using a custom shader.
Definition: scene.h:268
void render(RenderingContext &context)
Definition: scene.cpp:448
Layer containing a bitmap and a parametric mask (shape)
Definition: scene.h:235
void render(RenderingContext &context)
Definition: scene.cpp:399
bool testPoint(float x, float y) const
Tests if a given point falls in the layer.
Definition: scene.cpp:434
An ordered set of layers representing a renderable content.
Definition: scene.h:37
ShadedBitmapLayer & newShadedBitmapLayer()
Definition: scene.cpp:150
bool resolveMapping(const Layer &layer, AffineMapping &mapping) const
Computes absolute mapping of a given layer with respect to the scene mapping.
Definition: scene.cpp:204
void attachLayer(Layer &layer)
Attaches an existing layer to the scene.
Definition: scene.cpp:224
MaskedBitmapLayer & newMaskedBitmapLayer()
Definition: scene.cpp:130
Layer * getLayer(const char *name) const
Retrieves a layer by its name or null if not found.
Definition: scene.cpp:163
BitmapLayer & newBitmapLayer()
Definition: scene.cpp:120
Layer * detachLayer(int index)
Detaches a layer from the scene.
Definition: scene.cpp:231
int getLayerCount() const
Returns total number of layers in the scene.
Definition: scene.cpp:199
std::vector< Layer * > layers
scene layers
Definition: scene.h:39
ShapedBitmapLayer & newShapedBitmapLayer()
Definition: scene.cpp:140
int getLayerIndex(const Layer &layer) const
Returns layer index in the scene or -1 if not found.
Definition: scene.cpp:191
SceneLayer & addScene(const Scene &scene)
Adds a subscene to the current scene.
Definition: scene.cpp:155
static const color4i ZERO
Definition: constants.h:32
static const color4i WHITE
Definition: constants.h:26
CustomPoint< float > Point
Definition: geometry.h:626
@ SingleByte
single channel of 8 bits per pixel (like grayscale), unsigned integer values
@ INTERP_LINEAR
bilinear pixel interpolation
Definition: geometry.h:721
std::string to_string(Beatmup::NNets::ActivationFunction function)
std::string generateUniqueLayerName(const Scene &scene, const char *prefix="")
Definition: scene.cpp:97
int floorf_fast(float x)
Definition: utils.hpp:26
JNIEnv jlong jint jint jint jint jfloat scale
return(jlong) new Beatmup jlong jstring name
jlong jint index
jobject jlong jint jint y
jlong h
jlong jstring jint jint jint jint w
std::string content
Beatmup::IntPoint result
jobject jlong jint x
Beatmup::InternalBitmap * bitmap
* mat
Beatmup::Scene::Layer * layer
return bitmap getWidth()
jlong jboolean inPixels
Beatmup::IntPoint p((int) x,(int) y)
return $pool getJavaReference & scene(index)
return bitmap getHeight()
jlong jboolean visible
Beatmup::AffineMapping & mapping
return layer isVisible() ? JNI_TRUE jlong jboolean phantom
int n