1 // SPDX-License-Identifier: GPL-3.0-or-later
2 // SPDX-FileCopyrightText: 2021 Yurii Puchkov & Contributors
3 
4 #include "capturetoolobjects.h"
5 
6 #define SEARCH_RADIUS_NEAR 3
7 #define SEARCH_RADIUS_FAR 5
8 #define SEARCH_RADIUS_TEXT_HANDICAP 5
9 
CaptureToolObjects(QObject * parent)10 CaptureToolObjects::CaptureToolObjects(QObject* parent)
11   : QObject(parent)
12 {}
13 
append(const QPointer<CaptureTool> & captureTool)14 void CaptureToolObjects::append(const QPointer<CaptureTool>& captureTool)
15 {
16     if (!captureTool.isNull()) {
17         m_captureToolObjects.append(captureTool->copy(captureTool->parent()));
18         m_imageCache.clear();
19     }
20 }
21 
at(int index)22 QPointer<CaptureTool> CaptureToolObjects::at(int index)
23 {
24     if (index >= 0 && index < m_captureToolObjects.size()) {
25         return m_captureToolObjects[index];
26     }
27     return nullptr;
28 }
29 
clear()30 void CaptureToolObjects::clear()
31 {
32     m_captureToolObjects.clear();
33 }
34 
captureToolObjects()35 QList<QPointer<CaptureTool>> CaptureToolObjects::captureToolObjects()
36 {
37     return m_captureToolObjects;
38 }
39 
size()40 int CaptureToolObjects::size()
41 {
42     return m_captureToolObjects.size();
43 }
44 
removeAt(int index)45 void CaptureToolObjects::removeAt(int index)
46 {
47     if (index >= 0 && index < m_captureToolObjects.size()) {
48         m_captureToolObjects.removeAt(index);
49         m_imageCache.clear();
50     }
51 }
52 
find(const QPoint & pos,const QSize & captureSize)53 int CaptureToolObjects::find(const QPoint& pos, const QSize& captureSize)
54 {
55     if (m_captureToolObjects.empty()) {
56         return -1;
57     }
58     QPixmap pixmap(captureSize);
59     pixmap.fill(Qt::transparent);
60     QPainter painter(&pixmap);
61     // first attempt to find at exact position
62     int radius = SEARCH_RADIUS_NEAR;
63     int index = findWithRadius(painter, pixmap, pos, radius);
64     if (-1 == index) {
65         // second attempt to find at position with radius
66         radius = SEARCH_RADIUS_FAR;
67         pixmap.fill(Qt::transparent);
68         index = findWithRadius(painter, pixmap, pos, radius);
69     }
70     return index;
71 }
72 
findWithRadius(QPainter & painter,QPixmap & pixmap,const QPoint & pos,int radius)73 int CaptureToolObjects::findWithRadius(QPainter& painter,
74                                        QPixmap& pixmap,
75                                        const QPoint& pos,
76                                        int radius)
77 {
78     int index = m_captureToolObjects.size() - 1;
79     bool useCache = true;
80     m_imageCache.clear();
81     if (m_imageCache.size() != m_captureToolObjects.size() && index >= 0) {
82         // TODO - is not optimal and cache will be used just after first tool
83         // object selecting
84         m_imageCache.clear();
85         useCache = false;
86     }
87     for (; index >= 0; --index) {
88         int currentRadius = radius;
89         QImage image;
90         auto toolItem = m_captureToolObjects.at(index);
91         if (useCache) {
92             image = m_imageCache.at(index);
93         } else {
94             // create transparent image in memory and draw toolItem on it
95             toolItem->drawSearchArea(painter, pixmap);
96 
97             // get color at mouse clicked position in area +/- currentRadius
98             image = pixmap.toImage();
99             m_imageCache.insert(0, image);
100         }
101 
102         if (toolItem->nameID() == ToolType::TEXT) {
103             if (currentRadius > SEARCH_RADIUS_NEAR) {
104                 // Text already has a big currentRadius and no need to search
105                 // with a bit bigger currentRadius than
106                 // SEARCH_RADIUS_TEXT_HANDICAP + SEARCH_RADIUS_NEAR
107                 continue;
108             }
109 
110             // Text has spaces inside to need to take a bigger currentRadius for
111             // text objects search
112             currentRadius += SEARCH_RADIUS_TEXT_HANDICAP;
113         }
114 
115         for (int x = pos.x() - currentRadius; x <= pos.x() + currentRadius;
116              ++x) {
117             for (int y = pos.y() - currentRadius; y <= pos.y() + currentRadius;
118                  ++y) {
119                 QRgb rgb = image.pixel(x, y);
120                 if (rgb != 0) {
121                     // object was found, return it index (layer index)
122                     return index;
123                 }
124             }
125         }
126     }
127     // no object at current pos found
128     return -1;
129 }
130 
operator =(const CaptureToolObjects & other)131 CaptureToolObjects& CaptureToolObjects::operator=(
132   const CaptureToolObjects& other)
133 {
134     // remove extra items for this if size is bigger
135     while (this->m_captureToolObjects.size() >
136            other.m_captureToolObjects.size()) {
137         this->m_captureToolObjects.removeLast();
138     }
139 
140     int count = 0;
141     for (const auto& item : other.m_captureToolObjects) {
142         QPointer<CaptureTool> itemCopy = item->copy(item->parent());
143         if (count < this->m_captureToolObjects.size()) {
144             this->m_captureToolObjects[count] = itemCopy;
145         } else {
146             this->m_captureToolObjects.append(itemCopy);
147         }
148         count++;
149     }
150     return *this;
151 }