Visual Computing Library  devel
Loading...
Searching...
No Matches
canvas.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2025 *
6 * Visual Computing Lab *
7 * ISTI - Italian National Research Council *
8 * *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the Mozilla Public License Version 2.0 as published *
13 * by the Mozilla Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * Mozilla Public License Version 2.0 *
20 * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. *
21 ****************************************************************************/
22
23#ifndef VCL_BGFX_CANVAS_H
24#define VCL_BGFX_CANVAS_H
25
26#include <vclib/base.h>
27#include <vclib/bgfx/context.h>
28#include <vclib/bgfx/read_framebuffer_request.h>
29#include <vclib/bgfx/system/native_window_handle.h>
30#include <vclib/io/image.h>
31#include <vclib/render/concepts/render_app.h>
32#include <vclib/render/input.h>
33#include <vclib/render/read_buffer_types.h>
34
35#include <optional>
36
37namespace vcl {
38
42// TODO: write documentation here
43template<typename DerivedRenderApp>
45{
46 using ReadFramebufferRequest = detail::ReadFramebufferRequest;
47
48protected:
49 using FloatData = ReadBufferTypes::FloatData;
50 using ByteData = ReadBufferTypes::ByteData;
51 using ReadData = ReadBufferTypes::ReadData;
52
53public:
54 using CallbackReadBuffer = ReadBufferTypes::CallbackReadBuffer;
55
56private:
57 void* mWinId = nullptr;
58
59 // frame buffer for drawing the canvas
60 // BGFX_INVALID_HANDLE represents the default frame buffer of the window
61 bgfx::ViewId mViewId = BGFX_INVALID_VIEW;
62 bgfx::FrameBufferHandle mFbh = BGFX_INVALID_HANDLE;
63
64 // size of the canvas
65 Point2<uint> mSize = {0, 0};
66
67 vcl::Color mDefaultClearColor = vcl::Color::Black;
68
69 // current frame
70 uint32_t mCurrFrame = 0;
71
72 // offscreen readback request
73 std::optional<ReadFramebufferRequest> mReadRequest = std::nullopt;
74
75public:
77 void* winId,
78 uint width,
79 uint height,
80 void* displayId = nullptr) : mWinId(winId)
81 {
82 static_assert(
84 "The DerivedRenderApp must satisfy the RenderAppConcept.");
85
86 // on screen framebuffer
87 mViewId = Context::instance(mWinId, displayId).requestViewId();
88
89 // (re)create the framebuffers
90 onResize(width, height);
91 }
92
94 {
95 // deallocate the framebuffers
96 if (bgfx::isValid(mFbh))
97 bgfx::destroy(mFbh);
98
99 // release the view id
100 auto& ctx = Context::instance();
101 if (ctx.isValidViewId(mViewId))
102 ctx.releaseViewId(mViewId);
103 }
104
105 Point2<uint> size() const { return mSize; }
106
107 bgfx::ViewId viewId() const { return mViewId; }
108
109 bgfx::FrameBufferHandle frameBuffer() const { return mFbh; }
110
111 void setDefaultClearColor(const Color& color)
112 {
113 mDefaultClearColor = color;
114 bgfx::setViewClear(
115 mViewId,
117 color.rgba());
118 }
119
127 bool screenshot(const std::string& filename, uint multiplier = 1)
128 {
130 }
131
139 void onInit() {}
140
147 void onResize(uint width, uint height)
148 {
149 mSize = {width, height};
150
151 // create window backbuffer
152 if (bgfx::isValid(mFbh))
153 bgfx::destroy(mFbh);
154
155 auto& ctx = Context::instance();
156 mFbh = ctx.createFramebufferAndInitView(
157 mWinId, mViewId, width, height, true, mDefaultClearColor.rgba());
158 // the canvas framebuffer is non valid for the default window
159 assert(ctx.isDefaultWindow(mWinId) == !bgfx::isValid(mFbh));
160 }
161
166 void onPaint()
167 {
168 bgfx::setViewFrameBuffer(mViewId, mFbh);
169 bgfx::touch(mViewId);
170
171 // ask the derived frame to draw all the drawer objects:
172 DerivedRenderApp::CNV::draw(derived());
173 DerivedRenderApp::CNV::postDraw(derived());
174
175 const bool newReadRequested =
176 (mReadRequest != std::nullopt && !mReadRequest->isSubmitted());
177
178 if (newReadRequested) {
179 // draw offscreen frame
180 offscreenFrame();
181 mCurrFrame = bgfx::frame();
182 // submit the calls for blitting the offscreen depth buffer
183 if (mReadRequest->submit()) {
184 // solicit new frame
185 derived()->update();
186 }
187 }
188 else {
189 mCurrFrame = bgfx::frame();
190 }
191
192 if (mReadRequest != std::nullopt) {
193 // read depth data if available
194 const bool done = mReadRequest->performRead(mCurrFrame);
195 if (done)
196 mReadRequest = std::nullopt;
197 // solicit new frame
198 derived()->update();
199 }
200
201 // this is required only when using Qt in macOS
202#if defined(__APPLE__)
203 bgfx::frame();
204#endif // __APPLE__
205 }
206
216 const Point2i& point,
217 CallbackReadBuffer callback = nullptr)
218 {
219 if (!Context::instance().supportsReadback() // feature unsupported
220 || mReadRequest != std::nullopt // read already requested
221 || point.x() < 0 || point.y() < 0 // point out of bounds
222 || point.x() >= mSize.x() || point.y() >= mSize.y()) {
223 return false;
224 }
225
226 mReadRequest.emplace(point, mSize, callback);
227 return true;
228 }
229
239 bool onScreenshot(const std::string& filename, uint multiplier = 1)
240 {
241 if (!Context::instance().supportsReadback() // feature unsupported
242 || mReadRequest != std::nullopt) { // read already requested
243 return false;
244 }
245
246 // get size
247 auto size = mSize * multiplier;
248
249 // color data callback
250 CallbackReadBuffer callback = [=](const ReadData& data) {
251 assert(
252 std::holds_alternative<ReadFramebufferRequest::ByteData>(data));
253 const auto& d = std::get<ReadFramebufferRequest::ByteData>(data);
254
255 // save rgb image data into file using stb depending on file
256 // TODO: maybe useful to save it asynchronously
257 try {
258 vcl::saveImageData(filename, size.x(), size.y(), d.data());
259 }
260 catch (const std::exception& e) {
261 std::cerr << "Error saving image: " << e.what() << std::endl;
262 }
263 };
264
265 mReadRequest.emplace(size, callback, mDefaultClearColor);
266 return true;
267 }
268
280 const Point2i& point,
281 CallbackReadBuffer callback = nullptr)
282 {
283 if (!Context::instance().supportsReadback() // feature unsupported
284 || mReadRequest != std::nullopt // read already requested
285 || point.x() < 0 || point.y() < 0 // point out of bounds
286 || point.x() >= mSize.x() || point.y() >= mSize.y()) {
287 return false;
288 }
289
290 mReadRequest.emplace(point, mSize, true, callback);
291 return true;
292 }
293
294private:
295 // draw offscreen frame
296 void offscreenFrame()
297 {
298 assert(mReadRequest != std::nullopt && !mReadRequest->isSubmitted());
299
300 // render offscren
301 bgfx::setViewFrameBuffer(
302 mReadRequest->viewId(), mReadRequest->frameBuffer());
303 bgfx::touch(mReadRequest->viewId());
304
305 // render changing the view
306 auto tmpId = mViewId;
307 mViewId = mReadRequest->viewId();
308 switch (mReadRequest->type()) {
309 case ReadFramebufferRequest::Type::COLOR:
310 case ReadFramebufferRequest::Type::DEPTH:
311 DerivedRenderApp::CNV::drawContent(derived());
312 break;
313 case ReadFramebufferRequest::Type::ID:
314 DerivedRenderApp::CNV::drawId(derived());
315 break;
316 default: assert(false && "unsupported readback type"); break;
317 }
318 mViewId = tmpId;
319 }
320
321 auto* derived() { return static_cast<DerivedRenderApp*>(this); }
322
323 const auto* derived() const
324 {
325 return static_cast<const DerivedRenderApp*>(this);
326 }
327};
328
329} // namespace vcl
330
331#endif // VCL_BGFX_CANVAS_H
A class representing a box in N-dimensional space.
Definition box.h:46
The Canvas class describes a canvas on which bgfx can draw.
Definition canvas.h:45
bool screenshot(const std::string &filename, uint multiplier=1)
Request a screenshot of the canvas. The screenshot will be saved asynchronously.
Definition canvas.h:127
void onInit()
Automatically called by the DerivedRenderApp when the window initializes. Initialization is requires ...
Definition canvas.h:139
bool onReadDepth(const Point2i &point, CallbackReadBuffer callback=nullptr)
Automatically called by the DerivedRenderApp when a drawer asks to read the depth buffer at a specifi...
Definition canvas.h:215
void onPaint()
Automatically called by the DerivedRenderApp when the window asks to repaint.
Definition canvas.h:166
bool onScreenshot(const std::string &filename, uint multiplier=1)
Automatically called by the DerivedRenderApp when a drawer asks for a screenshot. Also called by the ...
Definition canvas.h:239
void onResize(uint width, uint height)
Automatically called by the DerivedRenderApp when the window is resized.
Definition canvas.h:147
bool onReadId(const Point2i &point, CallbackReadBuffer callback=nullptr)
Automatically called by the DerivedRenderApp when a drawer asks to read the ID at a specific point.
Definition canvas.h:279
The Color class represents a 32 bit color.
Definition color.h:48
static Context & instance(void *windowHandle=nullptr, void *displayHandle=nullptr)
Return the context instance.
Definition context.cpp:371
The Point class represents an N-dimensional point containing N scalar values.
Definition point.h:55
ScalarType & x()
Returns a reference to the x-component of the Point object.
Definition point.h:128
ScalarType & y()
Returns a reference to the y-component of the Point object.
Definition point.h:150
Definition render_app.h:31