11#include <spdlog/spdlog.h>
21 spdlog::debug(
"[DisplayManager::DisplayManager]: Created");
31 QScreen* primary_screen { QGuiApplication::primaryScreen() };
34 QSize viewport_default(800, 600);
38 QSize physical { primary_screen->size() * primary_screen->devicePixelRatio() };
41 screen_info.
dpr =
static_cast<float>(primary_screen->devicePixelRatio());
43 spdlog::info(
"[DisplayManager::initialize]: Auto-detected screen: {}x{}, DPR: {:.2f}",
48 spdlog::warn(
"[DisplayManager::initialize]: No screen detected, using defaults");
51 screen_info.
dpr = 1.0f;
63 if (!viewport_logical.isValid() || viewport_logical.isEmpty()) {
64 spdlog::error(
"[DisplayManager::initialize]: Invalid viewport size");
69 spdlog::error(
"[DisplayManager::initialize]: Invalid screen info");
77 m_viewport_manager->initialize(viewport_logical, screen);
83 spdlog::info(
"[DisplayManager::initialize]: Initialized successfully");
84 spdlog::info(
"[DisplayManager::initialize]: Max downsample: {}px", m_viewport_manager->maxDownsample());
85 spdlog::info(
"[DisplayManager::initialize]: Plafond: {}px", m_viewport_manager->plafond());
86 spdlog::info(
"[DisplayManager::initialize]: Quality margin: {:.2f}", m_viewport_manager->qualityMargin());
93 return m_viewport_manager && m_viewport_manager->isInitialized();
99 if (m_rendering_item == item) {
100 spdlog::trace(
"[DisplayManager::setRenderingItem]: Item is already set, skipping update");
104 spdlog::debug(
"[DisplayManager::setRenderingItem]: Setting new rendering item");
105 m_rendering_item = item;
107 if (m_rendering_item)
109 spdlog::debug(
"[DisplayManager::setRenderingItem]: Updating zoom and pan on rendering item");
110 m_rendering_item->
setZoom(m_zoom);
111 m_rendering_item->
setPan(m_pan);
113 spdlog::warn(
"[DisplayManager::setRenderingItem]: Rendering item is null");
123 if (!source_image || !source_image->isValid()) {
124 spdlog::warn(
"[DisplayManager::createDisplayImage]: Invalid source image");
128 if (!m_rendering_item) {
129 spdlog::warn(
"[DisplayManager::createDisplayImage]: No rendering item");
137 m_downsample_size = QSize(source_image->width(), source_image->height());
143 if (m_source_image_size.isValid() && m_source_image_size.width() > 0) {
144 m_display_scale =
static_cast<float>(m_downsample_size.width()) /
145 static_cast<float>(m_source_image_size.width());
148 spdlog::debug(
"[DisplayManager::createDisplayImage]: Creating display image: {}x{}, scale={:.3f}",
149 m_downsample_size.width(), m_downsample_size.height(),
156 m_rendering_item->
setImage(std::move(source_image));
168 if (!source_tile || !source_tile->isValid()) {
169 spdlog::warn(
"[DisplayManager::updateDisplayTile]: Invalid source tile");
173 if (!m_rendering_item) {
174 spdlog::warn(
"[DisplayManager::updateDisplayTile]: No rendering item");
178 spdlog::debug(
"[DisplayManager::updateDisplayTile]: Updating display {}x{}",
179 m_display_image_size.width(), m_display_image_size.height());
181 m_rendering_item->
updateTile(std::move(source_tile));
190 m_source_image_size = QSize(width, height);
192 spdlog::debug(
"[DisplayManager::setSourceImageSize]: Source image size: {}x{}", width, height);
199 spdlog::warn(
"[DisplayManager::setSourceImageSize]: Not initialized, using source size as display size");
200 m_display_image_size = m_source_image_size;
201 m_downsample_size = m_source_image_size;
202 m_display_scale = 1.0f;
209 m_display_image_size = calc.display_size;
210 m_downsample_size = calc.downsample_size;
211 m_fit_zoom = calc.fit_zoom;
214 if (m_source_image_size.width() > 0) {
215 m_display_scale =
static_cast<float>(m_downsample_size.width()) /
216 static_cast<float>(m_source_image_size.width());
219 spdlog::info(
"[DisplayManager::setSourceImageSize]: Calculated display:");
220 spdlog::info(
"[DisplayManager::setSourceImageSize]: Source: {}x{}", m_source_image_size.width(), m_source_image_size.height());
221 spdlog::info(
"[DisplayManager::setSourceImageSize]: Display: {}x{}", m_display_image_size.width(), m_display_image_size.height());
222 spdlog::info(
"[DisplayManager::setSourceImageSize]: Downsample: {}x{}", m_downsample_size.width(), m_downsample_size.height());
223 spdlog::info(
"[DisplayManager::setSourceImageSize]: Fit zoom: {:.3f}", m_fit_zoom);
224 spdlog::info(
"[DisplayManager::setSourceImageSize]: Texture memory: {} MB", calc.texture_memory_mb);
227 if (calc.needs_downsample) {
228 spdlog::debug(
"[DisplayManager::setSourceImageSize]: Requesting downsampled image: {}x{}",
229 m_downsample_size.width(), m_downsample_size.height());
251 const float clamped_zoom { std::clamp(
zoom, 0.1f, 10.0f) };
253 if (std::abs(m_zoom - clamped_zoom) < 0.001f) {
257 spdlog::debug(
"[DisplayManager::setZoom]: Zoom: {:.3f} → {:.3f}", m_zoom, clamped_zoom);
259 m_zoom = clamped_zoom;
263 if (m_rendering_item) {
264 m_rendering_item->
setZoom(m_zoom);
273 spdlog::trace(
"[DisplayManager::setPan]: Pan is already set to ({:.3f}, {:.3f}), skipping update",
pan.x(),
pan.y());
280 if (m_rendering_item) {
281 m_rendering_item->
setPan(m_pan);
293 float old_zoom { m_zoom };
294 float new_zoom { std::clamp(old_zoom * zoom_delta, 0.1f, 10.0f) };
300 const QPointF adjusted_pan { point - (point - m_pan) * (new_zoom / old_zoom) };
303 m_pan = adjusted_pan;
306 spdlog::debug(
"[DisplayManager::zoomAt]: Zoom changed from {:.6f} to {:.6f}, pan adjusted to ({:.6f}, {:.6f})",
307 old_zoom, m_zoom, m_pan.x(), m_pan.y());
309 if (m_rendering_item) {
310 spdlog::trace(
"[DisplayManager::zoomAt]: Updating zoom and pan on rendering item");
311 m_rendering_item->
setZoom(m_zoom);
312 m_rendering_item->
setPan(m_pan);
321 spdlog::debug(
"[DisplayManager::fitToView]: Fitting view to image");
323 spdlog::debug(
"[DisplayManager::fitToView]: m_display_image_size={}x{}, m_source_image_size={}x{}",
324 m_display_image_size.width(), m_display_image_size.height(),
325 m_source_image_size.width(), m_source_image_size.height());
327 spdlog::debug(
"[DisplayManager::fitToView]: viewport={}x{}",
328 m_viewport_manager ? m_viewport_manager->viewportSize().width() : -1,
329 m_viewport_manager ? m_viewport_manager->viewportSize().height() : -1);
331 bool image_valid { !m_downsample_size.isEmpty() };
332 bool viewport_manager_valid { m_viewport_manager !=
nullptr };
333 bool viewport_valid { m_viewport_manager && !m_viewport_manager->viewportSize().isEmpty() };
335 spdlog::debug(
"[DisplayManager::fitToView]: image_valid={}, viewport_manager_valid={}, viewport_valid={}",
336 image_valid, viewport_manager_valid, viewport_valid);
338 if (!image_valid || !viewport_manager_valid || !viewport_valid) {
339 spdlog::warn(
"[DisplayManager::fitToView]: Image size or viewport is empty, using defaults");
341 m_pan = QPointF(0.0, 0.0);
348 const float zoom_x {
static_cast<float>(m_display_image_size.width()) / m_downsample_size.width() };
349 const float zoom_y {
static_cast<float>(m_display_image_size.height()) / m_downsample_size.height() };
350 m_zoom = std::min(zoom_x, zoom_y);
355 const float image_displayed_width { m_downsample_size.width() * m_zoom };
356 const float image_displayed_height { m_downsample_size.height() * m_zoom };
358 const float pan_x { (m_display_image_size.width() - image_displayed_width) / 2.0f };
359 const float pan_y { (m_display_image_size.height() - image_displayed_height) / 2.0f };
360 m_pan = QPointF(pan_x, pan_y);
362 spdlog::debug(
"[DisplayManager::fitToView]: zoom={:.3f}, pan=({:.1f}, {:.1f})",
363 m_zoom, m_pan.x(), m_pan.y());
366 if (m_rendering_item)
368 spdlog::trace(
"[DisplayManager::fitToView]: Updating zoom and pan on rendering item");
369 m_rendering_item->
setZoom(m_zoom);
370 m_rendering_item->
setPan(m_pan);
378 spdlog::debug(
"[DisplayManager::resetView]: Resetting view");
399 if (!size.isValid() || size.isEmpty()) {
400 spdlog::warn(
"[DisplayManager::setViewportSize]: Invalid viewport size");
404 if (!m_viewport_manager) {
405 spdlog::error(
"[DisplayManager::setViewportSize]: ViewportManager not created");
410 const QSize old_viewport { m_viewport_manager->viewportSize() };
411 const int old_max { m_viewport_manager->maxDownsample() };
413 m_viewport_manager->setViewportSize(size);
416 const int new_max { m_viewport_manager->maxDownsample() };
417 if (new_max != old_max)
419 spdlog::debug(
"[DisplayManager::setViewportSize]: Max downsample changed: {} → {}", old_max, new_max);
423 if (m_source_image_size.isValid())
427 if (calc.isValid() && calc.downsample_size != m_downsample_size)
429 m_display_image_size = calc.display_size;
430 m_downsample_size = calc.downsample_size;
431 m_fit_zoom = calc.fit_zoom;
440 if (old_viewport != size) {
447 if (!m_viewport_manager) {
448 spdlog::error(
"[DisplayManager::setQualityMargin]: ViewportManager not created");
452 const int old_max { m_viewport_manager->maxDownsample() };
453 m_viewport_manager->setQualityMargin(margin);
454 const int new_max { m_viewport_manager->maxDownsample() };
456 if (new_max != old_max) {
465 return QPointF(backend_x * m_display_scale, backend_y * m_display_scale);
471 static_cast<int>(display_x / m_display_scale),
472 static_cast<int>(display_y / m_display_scale)
482void DisplayManager::constrainPan()
484 if (m_display_image_size.isEmpty()) {
485 spdlog::trace(
"[DisplayManager::constrainPan]: Display image size is empty, skipping constraint");
489 const float visible_width { m_display_image_size.width() * m_zoom };
490 const float visible_height { m_display_image_size.height() * m_zoom };
492 const float max_pan_x { std::max(0.0f, (visible_width - m_display_image_size.width()) / 2.0f) };
493 const float max_pan_y { std::max(0.0f, (visible_height - m_display_image_size.height()) / 2.0f) };
495 const QPointF old_pan { m_pan };
496 m_pan.setX(std::clamp(
static_cast<float>(m_pan.x()), -max_pan_x, max_pan_x));
497 m_pan.setY(std::clamp(
static_cast<float>(m_pan.y()), -max_pan_y, max_pan_y));
499 if (m_pan != old_pan) {
500 spdlog::trace(
"[DisplayManager::constrainPan]: Pan constrained: ({:.1f}, {:.1f}) → ({:.1f}, {:.1f})",
501 old_pan.x(), old_pan.y(), m_pan.x(), m_pan.y());
505QSize DisplayManager::calculateDisplaySize(
const QSize& source_size)
const
507 if (!m_viewport_manager || !source_size.isValid()) {
511 ViewportCalculation calc { m_viewport_manager->calculateDisplay(source_size) };
512 return calc.display_size;
521 return m_viewport_manager ? m_viewport_manager->maxDownsample() : 2048;
526 return m_viewport_manager ? m_viewport_manager->viewportSize() : QSize();
void viewportSizeChanged(const QSize &size)
Emitted when viewport size changes.
void displayScaleChanged(float scale)
Emitted when display scale changes.
void initialize()
Initializes with auto-detection of screen properties.
void panChanged(const QPointF &pan)
Emitted when pan changes.
void updateDisplayTile(std::unique_ptr< Core::Common::ImageRegion > source_tile)
Updates a specific tile region of the display image.
void displayImageSizeChanged(const QSize &size)
Emitted when display image size changes.
void setRenderingItem(Rendering::IRenderingItemBase *item)
Assigns the rendering item that will receive image updates.
void displayImageRequest(int target_width, int target_height)
Emitted when a new downsampled image is needed.
void createDisplayImage(std::unique_ptr< Core::Common::ImageRegion > source_image)
Creates the display buffer from a downsampled source image.
Q_INVOKABLE void setQualityMargin(float margin)
Sets the quality margin for zoom headroom.
Q_INVOKABLE QPointF mapBackendToDisplay(int backend_x, int backend_y) const
Maps backend (source) coordinates to display coordinates.
Q_INVOKABLE void setSourceImageSize(int width, int height)
Sets the source image size metadata.
Q_INVOKABLE void setZoom(float zoom)
Sets the zoom level.
Q_INVOKABLE QPoint mapDisplayToBackend(float display_x, float display_y) const
Maps display coordinates to backend (source) coordinates.
bool isInitialized() const noexcept
Checks if the manager has been initialized.
void maxDownsampleChanged(int max_dim)
Emitted when max downsample changes.
Q_INVOKABLE void resetView()
Resets the view to default fit state.
void downsampleSizeChanged(const QSize &size)
Emitted when downsample size changes.
Q_INVOKABLE void setViewportSize(const QSize &size)
Sets the viewport size.
void zoomChanged(float zoom)
Emitted when zoom changes.
Q_INVOKABLE void zoomAt(const QPointF &point, float zoom_delta)
Performs a zoom centered on a specific point.
Q_INVOKABLE void zoomOut()
Zooms out by 20% (divides zoom by 1.2).
void sourceImageSizeChanged(const QSize &size)
Emitted when source image size changes.
DisplayManager(QObject *parent=nullptr)
Constructs a DisplayManager.
void initializedChanged(bool initialized)
Emitted when initialization completes.
Q_INVOKABLE void setPan(const QPointF &pan)
Sets the pan offset.
Q_INVOKABLE void zoomIn()
Zooms in by 20%.
Q_INVOKABLE void fitToView()
Fits the image to fill the viewport.
Manages viewport calculations for optimal display across all screen types.
Abstract base class for image display items managing common state (zoom, pan, dimensions).
virtual void setImage(std::unique_ptr< Core::Common::ImageRegion > image)=0
Sets the full image to be displayed.
virtual void updateTile(std::unique_ptr< Core::Common::ImageRegion > tile)=0
Updates a specific tile of the displayed image.
virtual void setPan(const QPointF &pan)=0
Sets the pan offset.
virtual void setZoom(float zoom)=0
Sets the zoom level.
Manages image display with downsampling (with OIIO), zoom, and pan.
Namespace containing Qt-specific UI components for CaptureMoment.
Physical screen characteristics.
float dpr
Device pixel ratio (density)
int physical_width
Physical screen width in pixels.
int physical_height
Physical screen height in pixels.
bool isValid() const noexcept
Result of viewport calculation for an image.