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/bgfx/context.h>
27#include <vclib/bgfx/read_framebuffer_request.h>
28#include <vclib/bgfx/system/native_window_handle.h>
29#include <vclib/io/image.h>
30#include <vclib/render/concepts/render_app.h>
31#include <vclib/render/input.h>
32#include <vclib/render/read_buffer_types.h>
33#include <vclib/base.h>
34
35#include <optional>
36
37namespace vcl {
38
69template<typename DerivedRenderApp>
71{
72 using ReadFramebufferRequest = detail::ReadFramebufferRequest;
73
74protected:
75 using FloatData = ReadBufferTypes::FloatData;
76 using ByteData = ReadBufferTypes::ByteData;
77 using ReadData = ReadBufferTypes::ReadData;
78
79public:
80 using CallbackReadBuffer = ReadBufferTypes::CallbackReadBuffer;
81
82private:
83 void* mWinId = nullptr;
84
85 // frame buffer for drawing the canvas
86 // BGFX_INVALID_HANDLE represents the default frame buffer of the window
87 bgfx::ViewId mViewId = BGFX_INVALID_VIEW;
88 bgfx::FrameBufferHandle mFbh = BGFX_INVALID_HANDLE;
89
90 // size of the canvas
91 Point2<uint> mSize = {0, 0};
92
93 vcl::Color mDefaultClearColor = vcl::Color::Black;
94
95 // current frame
96 uint32_t mCurrFrame = 0;
97
98 // offscreen readback request
99 std::optional<ReadFramebufferRequest> mReadRequest = std::nullopt;
100
101public:
103 void* winId,
104 uint width,
105 uint height,
106 void* displayId = nullptr) : mWinId(winId)
107 {
108 static_assert(
110 "The DerivedRenderApp must satisfy the RenderAppConcept.");
111
112 // on screen framebuffer
113 mViewId = Context::instance(mWinId, displayId).requestViewId();
114
115 // (re)create the framebuffers
116 onResize(width, height);
117 }
118
120 {
121 // deallocate the framebuffers
122 if (bgfx::isValid(mFbh))
123 bgfx::destroy(mFbh);
124
125 // release the view id
126 auto& ctx = Context::instance();
127 if (ctx.isValidViewId(mViewId))
128 ctx.releaseViewId(mViewId);
129 }
130
131 Point2<uint> size() const { return mSize; }
132
133 bgfx::ViewId viewId() const { return mViewId; }
134
135 bgfx::FrameBufferHandle frameBuffer() const { return mFbh; }
136
137 void setDefaultClearColor(const Color& color)
138 {
139 mDefaultClearColor = color;
140 bgfx::setViewClear(
141 mViewId,
143 color.rgba());
144 }
145
153 bool screenshot(const std::string& filename, uint multiplier = 1)
154 {
156 }
157
165 void onInit() {}
166
173 void onResize(uint width, uint height)
174 {
175 mSize = {width, height};
176
177 // create window backbuffer
178 if (bgfx::isValid(mFbh))
179 bgfx::destroy(mFbh);
180
181 auto& ctx = Context::instance();
182 mFbh = ctx.createFramebufferAndInitView(
183 mWinId, mViewId, width, height, true, mDefaultClearColor.rgba());
184 // the canvas framebuffer is non valid for the default window
185 assert(ctx.isDefaultWindow(mWinId) == !bgfx::isValid(mFbh));
186 }
187
192 void onPaint()
193 {
194 bgfx::setViewFrameBuffer(mViewId, mFbh);
195 bgfx::touch(mViewId);
196
197 // ask the derived frame to draw all the drawer objects:
198 DerivedRenderApp::CNV::draw(derived());
199 DerivedRenderApp::CNV::postDraw(derived());
200
201 const bool newReadRequested =
202 (mReadRequest != std::nullopt && !mReadRequest->isSubmitted());
203
204 if (newReadRequested) {
205 // draw offscreen frame
206 offscreenFrame();
207 mCurrFrame = bgfx::frame();
208 // submit the calls for blitting the offscreen depth buffer
209 if (mReadRequest->submit()) {
210 // solicit new frame
211 derived()->update();
212 }
213 }
214 else {
215 mCurrFrame = bgfx::frame();
216 }
217
218 if (mReadRequest != std::nullopt) {
219 // read depth data if available
220 const bool done = mReadRequest->performRead(mCurrFrame);
221 if (done)
222 mReadRequest = std::nullopt;
223 // solicit new frame
224 derived()->update();
225 }
226
227 // this is required only when using Qt in macOS
228#if defined(__APPLE__)
229 bgfx::frame();
230#endif // __APPLE__
231 }
232
242 const Point2i& point,
243 CallbackReadBuffer callback = nullptr)
244 {
245 if (!Context::instance().supportsReadback() // feature unsupported
246 || mReadRequest != std::nullopt // read already requested
247 || point.x() < 0 || point.y() < 0 // point out of bounds
248 || point.x() >= mSize.x() || point.y() >= mSize.y()) {
249 return false;
250 }
251
252 mReadRequest.emplace(point, mSize, callback);
253 return true;
254 }
255
265 bool onScreenshot(const std::string& filename, uint multiplier = 1)
266 {
267 if (!Context::instance().supportsReadback() // feature unsupported
268 || mReadRequest != std::nullopt) { // read already requested
269 return false;
270 }
271
272 // get size
273 auto size = mSize * multiplier;
274
275 // color data callback
276 CallbackReadBuffer callback = [=](const ReadData& data) {
277 assert(
278 std::holds_alternative<ReadFramebufferRequest::ByteData>(data));
279 const auto& d = std::get<ReadFramebufferRequest::ByteData>(data);
280
281 // save rgb image data into file using stb depending on file
282 // TODO: maybe useful to save it asynchronously
283 try {
284 vcl::saveImageData(filename, size.x(), size.y(), d.data());
285 }
286 catch (const std::exception& e) {
287 std::cerr << "Error saving image: " << e.what() << std::endl;
288 }
289 };
290
291 mReadRequest.emplace(size, callback, mDefaultClearColor);
292 return true;
293 }
294
306 const Point2i& point,
307 CallbackReadBuffer callback = nullptr)
308 {
309 if (!Context::instance().supportsReadback() // feature unsupported
310 || mReadRequest != std::nullopt // read already requested
311 || point.x() < 0 || point.y() < 0 // point out of bounds
312 || point.x() >= mSize.x() || point.y() >= mSize.y()) {
313 return false;
314 }
315
316 mReadRequest.emplace(point, mSize, true, callback);
317 return true;
318 }
319
320private:
321 // draw offscreen frame
322 void offscreenFrame()
323 {
324 assert(mReadRequest != std::nullopt && !mReadRequest->isSubmitted());
325
326 // render offscren
327 bgfx::setViewFrameBuffer(
328 mReadRequest->viewId(), mReadRequest->frameBuffer());
329 bgfx::touch(mReadRequest->viewId());
330
331 // render changing the view
332 auto tmpId = mViewId;
333 mViewId = mReadRequest->viewId();
334 switch (mReadRequest->type()) {
335 case ReadFramebufferRequest::Type::COLOR:
336 case ReadFramebufferRequest::Type::DEPTH:
337 DerivedRenderApp::CNV::drawContent(derived());
338 break;
339 case ReadFramebufferRequest::Type::ID:
340 DerivedRenderApp::CNV::drawId(derived());
341 break;
342 default: assert(false && "unsupported readback type"); break;
343 }
344 mViewId = tmpId;
345 }
346
347 auto* derived() { return static_cast<DerivedRenderApp*>(this); }
348
349 const auto* derived() const
350 {
351 return static_cast<const DerivedRenderApp*>(this);
352 }
353};
354
355} // namespace vcl
356
357#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:71
bool screenshot(const std::string &filename, uint multiplier=1)
Request a screenshot of the canvas. The screenshot will be saved asynchronously.
Definition canvas.h:153
void onInit()
Automatically called by the DerivedRenderApp when the window initializes. Initialization is requires ...
Definition canvas.h:165
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:241
void onPaint()
Automatically called by the DerivedRenderApp when the window asks to repaint.
Definition canvas.h:192
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:265
void onResize(uint width, uint height)
Automatically called by the DerivedRenderApp when the window is resized.
Definition canvas.h:173
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:305
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