CVB++ 15.0
detail_ui.hpp
1#pragma once
2
3#include "../../global.hpp"
4#include "../../size_2d.hpp"
5#include "../../point_2d.hpp"
6#include "../../rect_lt.hpp"
7#include "../../image.hpp"
8
9#ifndef CVB_NO_QT5
10
11# ifdef _MSC_VER
12# pragma warning(push, 1)
13# pragma warning(disable : 4005)
14# endif
15# include <QtGlobal>
16# include <QObject>
17# ifdef _MSC_VER
18# pragma warning(pop)
19# endif
20
21#endif
22
23namespace Cvb
24{
25
26 CVB_BEGIN_INLINE_NS
27
28 namespace UI
29 {
30
31#ifndef CVB_NO_QT5
32 Q_NAMESPACE
33#endif
34
36 enum class UploadMode
37 {
54 };
55#ifndef CVB_NO_QT5
56 Q_ENUM_NS(UploadMode)
57#endif
58
108#ifndef CVB_NO_QT5
109 Q_ENUM_NS(ZoomID)
110#endif
111
113 enum class AutoRefresh
114 {
126 };
127
129 enum class LabelScale
130 {
131 /*
132 * The image view label will scale/zoom like the image (usefully to mark dimensions).
133 */
134 On,
135 /*
136 * The image view label will ignore image scale/zoom operations.
137 */
138 Off
139 };
140#ifndef CVB_NO_QT5
141 Q_ENUM_NS(LabelScale)
142#endif
143
145
147 struct ZoomDescriptor final
148 {
149
150#ifndef CVB_NO_QT5
151 Q_GADGET
152#endif
153 public:
158
159 ZoomDescriptor() = default;
160
162
167 bool operator==(const ZoomDescriptor &zoomDescriptor) const noexcept
168 {
169 return ID == zoomDescriptor.ID && Factor == zoomDescriptor.Factor;
170 }
171
173
178 bool operator!=(const ZoomDescriptor &zoomDescriptor) const noexcept
179 {
180 return !(*this == zoomDescriptor);
181 }
182
184
189 explicit ZoomDescriptor(ZoomID id) noexcept
190 : ID(id)
191 {
192 switch (id)
193 {
194 default:
195 case ZoomID::Invalid:
196 case ZoomID::Custom:
197 case ZoomID::Panorama:
198 break;
199
200 case ZoomID::Factor1:
201 Factor = 1.0;
202 break;
203
204 case ZoomID::Factor2:
205 Factor = 2.0;
206 break;
207
208 case ZoomID::Factor4:
209 Factor = 4.0;
210 break;
211
212 case ZoomID::Factor8:
213 Factor = 8.0;
214 break;
215
216 case ZoomID::Factor16:
217 Factor = 16.0;
218 break;
219
220 case ZoomID::Factor32:
221 Factor = 32.0;
222 break;
223
224 case ZoomID::Factor64:
225 Factor = 64.0;
226 break;
227
229 Factor = 128.0;
230 break;
231 }
232 }
233
235
240 explicit ZoomDescriptor(double customFactor) noexcept
242 , Factor(customFactor)
243 {
244 }
245 };
246
247 namespace Private
248 {
249
250 class IImageView
251 {
252 friend class ZoomHandler;
253
254 public:
255 IImageView(const IImageView &other) = delete;
256 IImageView &operator=(const IImageView &other) = delete;
257 IImageView(IImageView &&other) = delete;
258 IImageView &operator=(IImageView &&other) = delete;
259 virtual ~IImageView() = default;
260
261 protected:
262 IImageView() noexcept = default;
263
264 virtual Size2D<double> ViewSize() const noexcept = 0;
265 virtual Size2D<double> ImageSize() const noexcept = 0;
266
267 virtual void ApplyZoom(double factor) = 0;
268 };
269
270 class ZoomHandler
271 {
272 public:
273 explicit ZoomHandler(class IImageView &view) noexcept
274 : view_(view)
275 {
276 std::vector<UI::ZoomDescriptor> defaultDescriptorList(8);
277 defaultDescriptorList[0] = Cvb::UI::ZoomDescriptor(ZoomID::Factor1);
278 defaultDescriptorList[1] = Cvb::UI::ZoomDescriptor(ZoomID::Factor2);
279 defaultDescriptorList[2] = Cvb::UI::ZoomDescriptor(ZoomID::Factor4);
280 defaultDescriptorList[3] = Cvb::UI::ZoomDescriptor(ZoomID::Factor8);
281 defaultDescriptorList[4] = Cvb::UI::ZoomDescriptor(ZoomID::Factor16);
282 defaultDescriptorList[5] = Cvb::UI::ZoomDescriptor(ZoomID::Factor32);
283 defaultDescriptorList[6] = Cvb::UI::ZoomDescriptor(ZoomID::Factor64);
284 defaultDescriptorList[7] = Cvb::UI::ZoomDescriptor(ZoomID::Factor128);
285
286 defaultDescriptorList_.swap(defaultDescriptorList);
287
288 panoramaDescriptor_.ID = ZoomID::Panorama;
289 currentZoomDescriptor_ = panoramaDescriptor_; // NOLINT(cppcoreguidelines-prefer-member-initializer)
290 }
291
292 ZoomHandler(const ZoomHandler &other) = delete;
293 ZoomHandler &operator=(const ZoomHandler &other) = delete;
294 ZoomHandler(ZoomHandler &&other) = delete;
295 ZoomHandler &operator=(ZoomHandler &&other) = delete;
296 ~ZoomHandler() = default;
297
298 void UpdatePanoramaFactor() noexcept
299 {
300 auto viewportSize = view_.ViewSize();
301 auto sceneSize = view_.ImageSize();
302 if ((viewportSize_ == viewportSize && sceneSize_ == sceneSize)
303 || (sceneSize.Width() < 1 || sceneSize.Height() < 1))
304 return;
305
306 viewportSize_ = viewportSize;
307 sceneSize_ = sceneSize;
308#ifdef min
309# undef min
310#endif
311 double panoramaFactor =
312 std::min(viewportSize_.Width() / sceneSize_.Width(), viewportSize_.Height() / sceneSize_.Height());
313
314 panoramaDescriptor_.Factor = panoramaFactor;
315 if (currentZoomDescriptor_.ID == ZoomID::Panorama)
316 {
317 currentZoomDescriptor_ = panoramaDescriptor_;
318 Apply();
319 }
320 }
321
322 struct ZoomDescriptor ZoomDescriptor() const noexcept
323 {
324 return currentZoomDescriptor_;
325 }
326
327 bool TryZoomIn() noexcept
328 {
329 struct ZoomDescriptor result;
330
331 for (auto descriptor : defaultDescriptorList_)
332 {
333 if (descriptor.Factor > currentZoomDescriptor_.Factor
334 && (result.ID == ZoomID::Invalid || descriptor.Factor < result.Factor))
335 result = descriptor;
336 }
337
338 if (panoramaDescriptor_.Factor > currentZoomDescriptor_.Factor
339 && (result.ID == ZoomID::Invalid || panoramaDescriptor_.Factor < result.Factor))
340 result = panoramaDescriptor_;
341
342 if (result.ID == ZoomID::Invalid)
343 return false;
344
345 currentZoomDescriptor_ = result;
346 Apply();
347 return true;
348 }
349
350 bool TryZoomOut() noexcept
351 {
352 struct ZoomDescriptor result;
353
354 for (auto descriptor : defaultDescriptorList_)
355 {
356 if (descriptor.Factor < currentZoomDescriptor_.Factor
357 && (result.ID == ZoomID::Invalid || descriptor.Factor > result.Factor))
358 result = descriptor;
359 }
360
361 if (panoramaDescriptor_.Factor < currentZoomDescriptor_.Factor
362 && (result.ID == ZoomID::Invalid || panoramaDescriptor_.Factor > result.Factor))
363 result = panoramaDescriptor_;
364
365 if (result.ID == ZoomID::Invalid)
366 return false;
367
368 currentZoomDescriptor_ = result;
369 Apply();
370 return true;
371 }
372
373 void SetZoomDescriptor(struct ZoomDescriptor zoomDescriptor) noexcept
374 {
375
376 currentZoomDescriptor_ = zoomDescriptor;
377 Apply();
378 }
379
380 private:
381 enum class RenderDescriptor
382 {
383 AsyncImageUploadGl, // 1 0 no rendering, no upload
384 AsyncImageUploadRaster, // 2 1 no render, upload image in acquisitions thread
385 AsyncImageRepaintGl, // 3 1 waits for render, upload image in UI thread (image must be shared)
386 AsyncImageRepaintRaster, // 4 1 waits for render, upload image in acquisitions thread
387 AsyncImageScheduleGl, // 5 1 schedules rendering, upload image in UI thread (image must be shared)
388 AsyncImageScheduleRaster, // 6 1 schedules rendering, upload image in acquisitions thread
389 AsyncViewportUploadGl, // 7 0 equal to AsyncImageUploadGl
390 AsyncViewportUploadRaster, // 8 1 no rendering, upload viewport (image must be shared)
391 AsyncViewportRepaintGl, // 9 1 waits for render, upload viewport in UI thread (image must be shared)
392 AsyncViewportRepaintRaster, // 10 1 waits for render, upload viewport in Acquisition thread (image must be
393 // shared)
394 AsyncViewportScheduleGl, // 11 1 schedules rendering, upload viewport in UI thread (image must be shared)
395 AsyncViewportScheduleRaster, // 12 1 schedule for render, upload viewport in Acquisition thread (image must
396 // be shared)
397 SyncImageUploadGl, // 13 0 no rendering upload image into texture
398 SyncImageUploadRaster, // 14 0 no rendering upload image into QImage
399 SyncImageRepaintGl, // 15 -1 renders immediately, upload image into texture (measure render performance)
400 SyncImageRepaintRaster, // 16 -1 renders immediately, upload image into QImage (measure render performance)
401 SyncImageScheduleGl, // 17 1 schedules render, uploads image into texture
402 SyncImageScheduleRaster, // 18 1 schedules render, uploads image into QImage
403 SyncViewportUploadGl, // 19 0 no rendering, upload viewport into texture
404 SyncViewportUploadRaster, // 20 0 no rendering, upload viewport into QImage
405 SyncViewportRepaintGl, // 21 -1 renders immediately, upload viewport into texture (measure render
406 // performance) (image must be shared)
407 SyncViewportRepaintRaster, // 22 -1 renders immediately, upload viewport into QImage (measure render
408 // performance) (image must be shared)
409 SyncViewportScheduleGl, // 23 1 schedules render, uploads viewport into texture (image must be shared)
410 SyncViewportScheduleRaster, // 24 1 schedules render, uploads viewport into QImage (image must be shared)
411 };
412
413 void Apply() noexcept
414 {
415 if (currentZoomDescriptor_.ID == ZoomID::Invalid || std::isnan(currentZoomDescriptor_.Factor))
416 return;
417
418 view_.ApplyZoom(currentZoomDescriptor_.Factor);
419 }
420
421 IImageView &view_;
422
423 Size2D<double> sceneSize_;
424 Size2D<double> viewportSize_;
425 std::vector<struct ZoomDescriptor> defaultDescriptorList_;
426 struct ZoomDescriptor panoramaDescriptor_;
427 struct ZoomDescriptor currentZoomDescriptor_;
428 };
429
430 struct BGRAComponents
431 {
432 std::uint8_t Blue = 0;
433 std::uint8_t Green = 0;
434 std::uint8_t Red = 0;
435 std::uint8_t Alpha = 255;
436 };
437
438 struct RGBAComponents
439 {
440 std::uint8_t Red = 0;
441 std::uint8_t Green = 0;
442 std::uint8_t Blue = 0;
443 std::uint8_t Alpha = 255;
444 };
445
446 template <class T>
447 inline void CopyImageDataColorToColor(const Image &src, T *dst) noexcept
448 {
449 auto vpatR = src.Plane(0).Vpat();
450 auto vpatG = src.Plane(1).Vpat();
451 auto vpatB = src.Plane(2).Vpat();
452
453 auto entrysR = vpatR.VpatPtr();
454 auto entrysG = vpatG.VpatPtr();
455 auto entrysB = vpatB.VpatPtr();
456
457 auto baseR = vpatR.BasePtr();
458 auto baseG = vpatG.BasePtr();
459 auto baseB = vpatB.BasePtr();
460
461 auto widht = src.Width();
462 auto height = src.Height();
463
464 for (int h = 0; h < height; ++h)
465 {
466 auto lineOffsetR = baseR + entrysR[h].OffsetY;
467 auto lineOffsetG = baseG + entrysG[h].OffsetY;
468 auto lineOffsetB = baseB + entrysB[h].OffsetY;
469 for (int w = 0; w < widht; ++w)
470 {
471 (*dst).Alpha = 255;
472 (*dst).Red = *reinterpret_cast<std::uint8_t *>(lineOffsetR + entrysR[w].OffsetX);
473 (*dst).Green = *reinterpret_cast<std::uint8_t *>(lineOffsetG + entrysG[w].OffsetX);
474 (*dst).Blue = *reinterpret_cast<std::uint8_t *>(lineOffsetB + entrysB[w].OffsetX);
475 ++dst;
476 }
477 }
478 }
479
480 template <class T>
481 inline void CopyImageDataMonoToColor(const Image &src, T *dst) noexcept
482 {
483 auto vpat = src.Plane(0).Vpat();
484 auto entrys = vpat.VpatPtr();
485 auto base = vpat.BasePtr();
486
487 auto widht = src.Width();
488 auto height = src.Height();
489
490 for (int h = 0; h < height; ++h)
491 {
492 auto lineOffset = base + entrys[h].OffsetY;
493 for (int w = 0; w < widht; ++w)
494 {
495 (*dst).Alpha = 255;
496 auto value = *reinterpret_cast<std::uint8_t *>(lineOffset + entrys[w].OffsetX);
497 (*dst).Blue = (*dst).Green = (*dst).Red = value;
498 ++dst;
499 }
500 }
501 }
502
503 inline void CopyImageDataMonoToMono(const Image &src, void *dst, int linePad) noexcept
504 {
505 auto dstMono = reinterpret_cast<std::uint8_t *>(dst);
506 auto vpat = src.Plane(0).Vpat();
507 auto entrys = vpat.VpatPtr();
508 auto base = vpat.BasePtr();
509
510 auto widht = src.Width();
511 auto height = src.Height();
512
513 for (int h = 0; h < height; ++h)
514 {
515 auto lineOffset = base + entrys[h].OffsetY;
516 for (int w = 0; w < widht; ++w)
517 {
518 (*dstMono) = *reinterpret_cast<std::uint8_t *>(lineOffset + entrys[w].OffsetX);
519 ++dstMono;
520 }
521 dstMono += linePad;
522 }
523 }
524
525 inline void CopyImageDataMonoToBGRA(const Image &src, void *dst) noexcept
526 {
527 CopyImageDataMonoToColor(src, reinterpret_cast<BGRAComponents *>(dst));
528 }
529
530 inline void CopyImageDataMonoToRGBA(const Image &src, void *dst) noexcept
531 {
532 CopyImageDataMonoToColor(src, reinterpret_cast<RGBAComponents *>(dst));
533 }
534
535 inline void CopyImageDataRGBToBGRA(const Image &src, void *dst) noexcept
536 {
537 CopyImageDataColorToColor(src, reinterpret_cast<BGRAComponents *>(dst));
538 }
539
540 inline void CopyImageDataRGBToRGBA(const Image &src, void *dst) noexcept
541 {
542 CopyImageDataColorToColor(src, reinterpret_cast<RGBAComponents *>(dst));
543 }
544
545 class ImageViewDispatcher : public UI::Private::IImageView
546 {
547 friend class ImageViewDispatcherGuard;
548
549 public:
550 explicit ImageViewDispatcher(std::function<void()> notifyZoom)
551 : zoomHandler_(*this)
552 , notifyZoom_(notifyZoom)
553 {
554 }
555
556 UI::ZoomID ZoomID() const noexcept
557 {
558 return zoomHandler_.ZoomDescriptor().ID;
559 }
560
561 void UpdateZoomID(UI::ZoomID id, std::function<void()> notify)
562 {
563 if (id == ZoomID())
564 return;
565
566 zoomHandler_.SetZoomDescriptor(ZoomDescriptor(id));
567 notify();
568 }
569
570 void UpdateUploadMode(UI::UploadMode uploadMode, std::function<void()> notify)
571 {
572 if (uploadMode_ == uploadMode)
573 return;
574
575 {
576 std::unique_lock<std::mutex> gurad(
577 renderMutex_); // prevent mixing upload modes during render (innermost mutex)
578 uploadMode_ = uploadMode;
579 }
580 notify();
581 }
582
583 double ZoomFactor() const noexcept
584 {
585 return zoomHandler_.ZoomDescriptor().Factor;
586 }
587
588 void UpdateZoomFactor(double factor, std::function<void()> notify)
589 {
590 if (factor == ZoomFactor())
591 return;
592
593 zoomHandler_.SetZoomDescriptor(ZoomDescriptor(factor));
594 notify();
595 }
596
597 Point2D<double> ImageAnchor() const noexcept
598 {
599 return imageAnchor_;
600 }
601
602 void UpdateImageAnchor(Point2D<double> imageAnchor, std::function<void()> notify)
603 {
604 if (imageAnchor_ == imageAnchor)
605 return;
606
607 imageAnchor_ = imageAnchor;
608 notify();
609 }
610
611 Point2D<double> ViewAnchor() const noexcept
612 {
613 return viewAnchor_;
614 }
615
616 void UpdateViewAnchor(Point2D<double> viewAnchor, std::function<void()> notify)
617 {
618 if (viewAnchor_ == viewAnchor)
619 return;
620
621 viewAnchor_ = viewAnchor;
622 notify();
623 }
624
625 Point2D<double> LastMousePosition() const noexcept
626 {
627 return *lastPos_;
628 }
629
630 Point2D<double> HoverPosition() const noexcept
631 {
632 return *hoverPosition_;
633 }
634
635 bool TryTranslate(Vector2D<double> translation, std::function<void()> notifySourceRect)
636 {
637 auto zoom = zoomHandler_.ZoomDescriptor().Factor;
638
639 auto imageTranslation = translation / zoom;
640 auto xRange = imageRect_.Width() - sourceRect_.Width();
641 auto yRange = imageRect_.Height() - sourceRect_.Height();
642
643 auto sourceCandidate = sourceRect_;
644
645 auto translationCandidate = sourceCandidate.Location() - imageTranslation;
646
647#ifdef min
648# undef min
649#endif
650#ifdef max
651# undef max
652#endif
653
654 // ensure min range
655 translationCandidate.SetX(std::max(translationCandidate.X(), 0.0));
656 translationCandidate.SetY(std::max(translationCandidate.Y(), 0.0));
657
658 // ensure max range
659 Point2D<double> location(std::min(xRange, translationCandidate.X()),
660 std::min(yRange, translationCandidate.Y()));
661 sourceCandidate.SetLocation(location);
662
663 auto result = sourceRect_ != sourceCandidate;
664 UpdateSourceRect(sourceCandidate, notifySourceRect);
665 return result;
666 }
667
668 bool TryZoomIn(std::function<void()> notifyTargetRect, std::function<void()> notifySourceRect)
669 {
670 auto result = zoomHandler_.TryZoomIn();
671 UpdateTargetSource(notifyTargetRect, notifySourceRect);
672 return result;
673 }
674
675 bool TryZoomOut(std::function<void()> notifyTargetRect, std::function<void()> notifySourceRect)
676 {
677 auto result = zoomHandler_.TryZoomOut();
678 UpdateTargetSource(notifyTargetRect, notifySourceRect);
679 return result;
680 }
681
682 RectLT<double> ViewRect() const noexcept
683 {
684 return viewRect_;
685 }
686
687 RectLT<double> ImageRect() const noexcept
688 {
689 return imageRect_;
690 }
691
692 RectLT<double> SourceRect() const noexcept
693 {
694 return sourceRect_;
695 }
696
697 RectLT<double> TargetRect() const noexcept
698 {
699 return targetRect_;
700 }
701
702 void UpdateViewRect(RectLT<double> rect, std::function<void()> notify)
703 {
704 if (viewRect_ == rect)
705 return;
706 viewRect_ = rect;
707 notify();
708 }
709
710 UI::UploadMode UploadMode() const noexcept
711 {
712 return uploadMode_;
713 }
714
715 Point2D<double> MapViewToTarget(Point2D<double> viewPoint) const
716 {
717 return viewPoint - targetRect_.Location();
718 }
719
720 Point2D<double> MapTargetToSource(Point2D<double> targetPoint) const
721 {
722 return targetPoint / zoomHandler_.ZoomDescriptor().Factor;
723 }
724
725 Point2D<double> MapSourceToImage(Point2D<double> sourcePoint) const
726 {
727 return sourcePoint + sourceRect_.Location();
728 }
729
730 Point2D<double> MapTargetToView(Point2D<double> targetPoint) const
731 {
732 return targetPoint + targetRect_.Location();
733 }
734
735 Point2D<double> MapSourceToTarget(Point2D<double> sourcePoint) const
736 {
737 return sourcePoint * zoomHandler_.ZoomDescriptor().Factor;
738 }
739
740 Point2D<double> MapImageToSource(Point2D<double> imagePoint) const
741 {
742 return imagePoint - sourceRect_.Location();
743 }
744
745 Point2D<double> MapViewToImage(Point2D<double> viewPoint) const
746 {
747 return MapSourceToImage(MapTargetToSource(MapViewToTarget(viewPoint)));
748 }
749
750 void UploadImage(const Image &image, std::function<void()> notifyImageRect)
751 {
752 std::unique_lock<std::mutex> guard(renderMutex_);
753 UpdateImageRect(image, notifyImageRect);
754
755 auto mappedImage = UpdateRenderTarget(image);
756
757 if (uploadMode_ == UI::UploadMode::Image)
758 CopyImageData(image);
759 else if (mappedImage)
760 CopyImageData(*mappedImage);
761 }
762
763 void CopyImageData(const Image &image) noexcept
764 {
765 switch (image.PlanesCount())
766 {
767 default:
768 return;
769
770 case 1:
771 UI::Private::CopyImageDataMonoToBGRA(image, buffer_.data());
772 return;
773
774 case 3:
775 case 4: // RGBA8 support
776 UI::Private::CopyImageDataRGBToBGRA(image, buffer_.data());
777 return;
778 }
779 }
780
781 void UpdateImageRect(const Cvb::Image &image, std::function<void()> notify)
782 {
783 RectLT<double> requestedImageRect(
784 Point2D<double>(0.0, 0.0),
785 Size2D<double>(static_cast<double>(image.Width()), static_cast<double>(image.Height())));
786
787 if (imageRect_ == requestedImageRect)
788 return;
789
790 imageRect_ = requestedImageRect;
791 notify();
792 }
793
794 void UpdateTargetRect(RectLT<double> rect, std::function<void()> notify)
795 {
796 if (targetRect_ == rect)
797 return;
798 targetRect_ = rect;
799 notify();
800 }
801
802 void UpdateSourceRect(RectLT<double> rect, std::function<void()> notify)
803 {
804 if (sourceRect_ == rect)
805 return;
806 sourceRect_ = rect;
807 notify();
808 }
809
810 void UpdateBuffer(Size2D<int> size)
811 {
812 bufferSize_ = size;
813 std::vector<std::uint8_t>(static_cast<size_t>(bufferSize_.Width()) * static_cast<size_t>(bufferSize_.Height())
814 * 4)
815 .swap(buffer_);
816 }
817
818 std::unique_ptr<class Image> UpdateRenderTarget(const Cvb::Image &image)
819 {
820 if (uploadMode_ == UI::UploadMode::Image)
821 {
822 auto currentWidth = image.Width();
823 auto currentHeight = image.Height();
824 if (bufferSize_ == Size2D<int>(currentWidth, currentHeight))
825 return std::unique_ptr<class Image>();
826
827 UpdateBuffer(Size2D<int>(currentWidth, currentHeight));
828 return std::unique_ptr<class Image>();
829 }
830 else
831 {
832 // get the part of the image that is visible in as integer pixel coordinates (for some pixels only a tiny
833 // part might be visible)
834 auto sourceLeft = std::max(static_cast<int>(std::floor(sourceRect_.Left())), 0);
835 auto sourceTop = std::max(static_cast<int>(std::floor(sourceRect_.Top())), 0);
836 auto sourceRight = std::min(static_cast<int>(std::ceil(sourceRect_.Right())), image.Width() - 1);
837 auto sourceBottom = std::min(static_cast<int>(std::ceil(sourceRect_.Bottom())), image.Height() - 1);
838 Size2D<int> sourceSize(sourceRight - sourceLeft, sourceBottom - sourceTop);
839 Rect<int> sourceRect(Point2D<int>(sourceLeft, sourceTop), sourceSize);
840
841 // validate source rectangle is positive
842 if (sourceRect.Width() <= 0 || sourceRect.Height() <= 0)
843 return std::unique_ptr<class Image>();
844
845 // validate source rectangle is positive
846 if (!targetRect_.IsValid())
847 return std::unique_ptr<class Image>();
848
849 Size2D<int> targetSize;
850 if (sourceRect_.Width() <= targetRect_.Width()) // source pixel is bigger in target -> zoom
851 {
852 targetSize = sourceRect.Size();
853 sourceRectAdj_ = RectLT<double>(
854 Point2D<double>(sourceRect_.Left() - std::floor(sourceRect_.Left()),
855 sourceRect_.Top() - std::floor(sourceRect_.Top())),
856 Size2D<double>(std::min(sourceRect_.Width(), static_cast<double>(sourceRect.Width())),
857 std::min(sourceRect_.Height(), static_cast<double>(sourceRect.Height()))));
858 }
859 else
860 {
861 targetSize = Size2D<int>(static_cast<int>(targetRect_.Width()), static_cast<int>(targetRect_.Height()));
862 sourceRectAdj_ =
863 RectLT<double>(Point2D<double>(0.0, 0.0), Size2D<double>(targetSize.Width(), targetSize.Height()));
864 }
865
866 if (targetSize.Width() <= 0 || targetSize.Height() <= 0)
867 return std::unique_ptr<class Image>();
868
869 auto mappedImage = image.Map(sourceRect, targetSize);
870 if (bufferSize_ == Size2D<int>(targetSize.Width(), targetSize.Height()))
871 return mappedImage;
872
873 UpdateBuffer(targetSize);
874 return mappedImage;
875 }
876 }
877
878 Point2D<double> LimitPointToRect(Point2D<double> point, RectLT<double> rect)
879 {
880 auto result = point;
881 if (result.X() < 0.0)
882 result.SetX(0.0);
883 else if (result.X() > rect.Width())
884 result.SetX(rect.Width());
885 if (result.Y() < 0.0)
886 result.SetY(0.0);
887 else if (result.Y() > rect.Height())
888 result.SetY(rect.Height());
889 return result;
890 }
891
892 void UpdateTargetSource(std::function<void()> notifyTargetRect, std::function<void()> notifySourceRect)
893 {
894 zoomHandler_.UpdatePanoramaFactor();
895 auto zoom = zoomHandler_.ZoomDescriptor().Factor;
896
897 auto contentWidth = zoom * imageRect_.Width();
898 auto contentHeight = zoom * imageRect_.Height();
899
900 UpdateTargetRect(
901 RectLT<double>(
902 Point2D<double>(
903 (contentWidth <= viewRect_.Width()) ? (viewRect_.Width() - contentWidth) / 2.0
904 : 0.0, // if all the image content width fits into the view
905 // port center it otherwise fill the target
906 (contentHeight <= viewRect_.Height()) ? (viewRect_.Height() - contentHeight) / 2.0
907 : 0.0), // if all the image content height fits into the
908 // view port center it otherwise fill the target
909 Size2D<double>((contentWidth < viewRect_.Width())
910 ? contentWidth
911 : viewRect_.Width(), // if all the image content width fits into the view port
912 // draw all of it otherwise fill the viewport
913 (contentHeight < viewRect_.Height()) ? contentHeight : viewRect_.Height())),
914 notifyTargetRect); // if all the image content height fits into the view port draw all of it otherwise
915 // fill the viewport
916
917 // The view port rectangle adjusted by the current zoom factor is our maximum candidate source rectangle
918 auto sourceCandidate = viewRect_; // TODO verify
919 sourceCandidate.SetWidth(sourceCandidate.Width() / zoom);
920 sourceCandidate.SetHeight(sourceCandidate.Height() / zoom);
921
922 // However the source rectangle might be smaller as our candidate rectangle
923 sourceCandidate.SetWidth(std::min(sourceCandidate.Width(), imageRect_.Width()));
924 sourceCandidate.SetHeight(std::min(sourceCandidate.Height(), imageRect_.Height()));
925
926 auto targetPoint = MapViewToTarget(viewAnchor_);
927 targetPoint = LimitPointToRect(targetPoint, targetRect_);
928 auto sourcePoint = MapTargetToSource(targetPoint);
929
930 auto offsetCandidate = imageAnchor_ - sourcePoint;
931
932 // Valid range for offset
933 auto xRange = imageRect_.Width() - sourceCandidate.Width();
934 auto yRange = imageRect_.Height() - sourceCandidate.Height();
935
936 // ancor point for zoom in the center of your image if possible
937 auto xOffsetCandidate = offsetCandidate.X();
938 auto yOffsetCandidate = offsetCandidate.Y();
939
940 // ensure min range
941 xOffsetCandidate = std::max(xOffsetCandidate, 0.0);
942 yOffsetCandidate = std::max(yOffsetCandidate, 0.0);
943
944 // ensure max range
945 Point2D<double> location(std::min(xRange, xOffsetCandidate), std::min(yRange, yOffsetCandidate));
946 sourceCandidate.SetLocation(location);
947
948 UpdateSourceRect(sourceCandidate, notifySourceRect);
949 }
950
951 void UpdateHoverPosition(Point2D<double> point, std::function<void()> notify)
952 {
953 if (hoverPosition_ && *hoverPosition_ == point)
954 return;
955
956 hoverPosition_ = std::make_unique<Point2D<double>>(point);
957 notify();
958 }
959
960 void InvalidateLastMousePosition() noexcept
961 {
962 lastPos_.reset();
963 }
964
965 void InvalidateLastMousePosition(Point2D<double> point) noexcept
966 {
967 lastPos_ = std::make_unique<Point2D<double>>(point);
968 }
969
970 bool IsLastMousePositionValid() const noexcept
971 {
972 return (lastPos_) ? true : false;
973 }
974
975 bool IsHoverPositionValid() const noexcept
976 {
977 return (hoverPosition_) ? true : false;
978 }
979
980 Size2D<double> ViewSize() const noexcept override
981 {
982 return viewRect_.Size();
983 }
984
985 Size2D<double> ImageSize() const noexcept override
986 {
987 return imageRect_.Size();
988 }
989
990 void ApplyZoom(double) override
991 {
992 notifyZoom_();
993 }
994
995 RectLT<double> SourceRectAdj() const noexcept
996 {
997 return sourceRectAdj_;
998 }
999
1000 bool IsBufferValid() const noexcept
1001 {
1002 return (buffer_.size()) ? true : false;
1003 }
1004
1005 std::uint8_t *Buffer() const noexcept
1006 {
1007 return buffer_.data();
1008 }
1009
1010 Size2D<int> BufferSize() const noexcept
1011 {
1012 return bufferSize_;
1013 }
1014
1015 private:
1016 RectLT<double> imageRect_;
1017 RectLT<double> viewRect_;
1018 RectLT<double> sourceRect_;
1019 RectLT<double> targetRect_;
1020
1021 RectLT<double> sourceRectAdj_;
1022
1023 Point2D<double> viewAnchor_;
1024 Point2D<double> imageAnchor_;
1025
1026 std::mutex renderMutex_;
1027
1028 UI::Private::ZoomHandler zoomHandler_;
1029
1030 std::unique_ptr<Point2D<double>> lastPos_;
1031 std::unique_ptr<Point2D<double>> hoverPosition_;
1032
1033 Cvb::UI::UploadMode uploadMode_ = UI::UploadMode::Image;
1034
1035 mutable std::vector<std::uint8_t> buffer_;
1036 Size2D<int> bufferSize_;
1037
1038 std::function<void()> notifyZoom_;
1039 };
1040
1041 class ImageViewDispatcherGuard
1042 {
1043 public:
1044 explicit ImageViewDispatcherGuard(ImageViewDispatcher &dispatcher)
1045 : dispatcher_(dispatcher)
1046 {
1047 dispatcher_.renderMutex_.lock();
1048 }
1049
1050 ImageViewDispatcherGuard(const ImageViewDispatcherGuard &other) = delete;
1051 ImageViewDispatcherGuard &operator=(const ImageViewDispatcherGuard &other) = delete;
1052 ImageViewDispatcherGuard(ImageViewDispatcherGuard &&other) = delete;
1053 ImageViewDispatcherGuard &operator=(ImageViewDispatcherGuard &&other) = delete;
1054 ~ImageViewDispatcherGuard()
1055 {
1056 dispatcher_.renderMutex_.unlock();
1057 }
1058
1059 private:
1060 ImageViewDispatcher &dispatcher_;
1061 };
1062
1063 } // namespace Private
1064
1065 } // namespace UI
1066
1067 CVB_END_INLINE_NS
1068
1069} // namespace Cvb
int Width() const noexcept
Width of the image in pixels.
Definition decl_image.hpp:309
int Height() const noexcept
Height of the image in pixels.
Definition decl_image.hpp:299
std::unique_ptr< Image > Map(Rect< int > rect) const
Creates a mapped image of the region of this image.
Definition decl_image.hpp:352
Stores a pair of numbers that represents the width and the height of a subject, typically a rectangle...
Definition size_2d.hpp:20
T min(T... args)
Namespace for user interface components.
Definition decl_image_scene.hpp:39
ZoomID
Identifier for a zoom factor.
Definition detail_ui.hpp:61
@ Factor4
Definition detail_ui.hpp:85
@ Factor2
Definition detail_ui.hpp:81
@ Factor64
Definition detail_ui.hpp:101
@ Invalid
Definition detail_ui.hpp:65
@ Factor32
Definition detail_ui.hpp:97
@ Factor8
Definition detail_ui.hpp:89
@ Factor128
Definition detail_ui.hpp:105
@ Factor1
Definition detail_ui.hpp:77
@ Panorama
Definition detail_ui.hpp:73
@ Custom
Definition detail_ui.hpp:69
@ Factor16
Definition detail_ui.hpp:93
AutoRefresh
Allows to automatically refresh, if image content changes.
Definition detail_ui.hpp:114
@ On
Definition detail_ui.hpp:121
@ Off
Definition detail_ui.hpp:125
LabelScale
Switch defining if image view labels are sensitive to zoom operations.
Definition detail_ui.hpp:130
UploadMode
Defines the upload behavior.
Definition detail_ui.hpp:37
@ Viewport
Definition detail_ui.hpp:53
@ Image
Definition detail_ui.hpp:45
Root namespace for the Image Manager interface.
Definition c_bayer_to_rgb.h:17
T quiet_NaN(T... args)
Describes an zoom setting for the display.
Definition detail_ui.hpp:148
bool operator==(const ZoomDescriptor &zoomDescriptor) const noexcept
Compares to an other zoom descriptor.
Definition detail_ui.hpp:167
bool operator!=(const ZoomDescriptor &zoomDescriptor) const noexcept
Compares to an other zoom descriptor.
Definition detail_ui.hpp:178
ZoomDescriptor(double customFactor) noexcept
Constructor for a custom zoom descriptor.
Definition detail_ui.hpp:240
double Factor
Numeric representation of the zoom factor.
Definition detail_ui.hpp:157
ZoomDescriptor(ZoomID id) noexcept
Constructor for a standard zoom descriptor.
Definition detail_ui.hpp:189
ZoomID ID
Zoom identifier for known factors.
Definition detail_ui.hpp:155