1 /************************************************************************
2  *
3  * Copyright 2013 Jakob Leben (jakob.leben@gmail.com)
4  *
5  * This file is part of SuperCollider Qt GUI.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  ************************************************************************/
21 
22 #pragma once
23 
24 #include "../image.h"
25 #include "../debug.h"
26 
27 #include <QPainter>
28 
29 namespace QtCollider {
30 
31 struct ImagePainter {
32     enum HorizontalMode {
33         AlignLeft,
34         AlignHCenter,
35         AlignRight,
36         TileHorizontally,
37         StretchHorizontally,
38     };
39 
40     enum VerticalMode { AlignTop, AlignVCenter, AlignBottom, TileVertically, StretchVertically };
41 
42     SharedImage image;
43     QRectF sourceRect;
44     HorizontalMode horizontalMode;
45     VerticalMode verticalMode;
46     bool scaleToFit;
47     qreal opacity;
48 
ImagePainterImagePainter49     ImagePainter(): horizontalMode(AlignLeft), verticalMode(AlignTop), scaleToFit(false), opacity(1.0) {}
50 
isValidImagePainter51     bool isValid() const { return !image.isNull(); }
52 
53     void setImage(const SharedImage& image, const QRectF& rect = QRectF(), int tileMode = 1, qreal opacity = 1.0) {
54         this->image = image;
55         this->sourceRect = rect;
56         this->opacity = opacity;
57         setTileMode(tileMode);
58     }
59 
clearImagePainter60     void clear() { image.clear(); }
61 
paintImagePainter62     void paint(QPainter* painter, const QRectF& targetRect) {
63         if (!image)
64             return;
65 
66         if (image->isPainting()) {
67             qcErrorMsg("Can not draw image while being painted.");
68             return;
69         }
70 
71         const QPixmap& pixmap = image->pixmap();
72 
73         if (sourceRect.isNull() || targetRect.isNull())
74             return;
75 
76         painter->save();
77         painter->setOpacity(opacity);
78         painter->setRenderHint(QPainter::SmoothPixmapTransform, image->transformationMode == Qt::SmoothTransformation);
79 
80         QRectF rect = sourceRect;
81         qreal ratio = pixmap.devicePixelRatio();
82         rect.setWidth(rect.width() / ratio);
83         rect.setHeight(rect.height() / ratio);
84 
85         if (horizontalMode == StretchHorizontally) {
86             rect.moveLeft(targetRect.left());
87             rect.setWidth(targetRect.width());
88         }
89         if (verticalMode == StretchVertically) {
90             rect.moveTop(targetRect.top());
91             rect.setHeight(targetRect.height());
92         }
93         if (horizontalMode != StretchHorizontally && verticalMode != StretchVertically && scaleToFit) {
94             float aspect_ratio = rect.width() / rect.height();
95             rect.setHeight(targetRect.height());
96             rect.setWidth(rect.height() * aspect_ratio);
97             if (rect.width() > targetRect.width()) {
98                 rect.setWidth(targetRect.width());
99                 rect.setHeight(rect.width() / aspect_ratio);
100             }
101         }
102 
103         switch (horizontalMode) {
104         case AlignLeft:
105         case TileHorizontally:
106             rect.moveLeft(targetRect.left());
107             break;
108         case AlignHCenter:
109             rect.moveLeft(targetRect.left() + targetRect.width() / 2 - rect.width() / 2);
110             break;
111         case AlignRight:
112             rect.moveRight(targetRect.right());
113             break;
114         default:
115             break;
116         };
117 
118         switch (verticalMode) {
119         case AlignTop:
120         case TileVertically:
121             rect.moveTop(targetRect.top());
122             break;
123         case AlignVCenter:
124             rect.moveTop(targetRect.top() + targetRect.height() / 2 - rect.height() / 2);
125             break;
126         case AlignRight:
127             rect.moveBottom(targetRect.bottom());
128             break;
129         default:
130             break;
131         };
132 
133         bool tileVertically = verticalMode == TileVertically;
134         bool tileHorizontally = horizontalMode == TileHorizontally;
135 
136         qreal y_origin = rect.top();
137         do {
138             do {
139                 painter->drawPixmap(rect, pixmap, sourceRect);
140 
141                 if (tileVertically)
142                     rect.moveTop(rect.top() + rect.height());
143                 else
144                     break;
145             } while (rect.top() <= targetRect.bottom());
146 
147             if (tileHorizontally) {
148                 rect.moveTop(y_origin);
149                 rect.moveLeft(rect.left() + rect.width());
150             } else
151                 break;
152         } while (rect.left() <= targetRect.right());
153 
154         painter->restore();
155     }
156 
setTileModeImagePainter157     void setTileMode(const int mode) {
158         /*
159             modes :
160             1 - fixed to left, fixed to top
161             2 - horizontally tile, fixed to top
162             3 - fixed to right, fixed to top
163             4 - fixed to left, vertically tile
164             5 - horizontally tile, vertically tile
165             6 - fixed to right, vertically tile
166             7 - fixed to left, fixed to bottom
167             8 - horizontally tile, fixed to bottom
168             9 - fixed to right, fixed to bottom
169             10 - fit
170             11 - center, center (scale)
171             12 - center , fixed to top
172             13 - center , fixed to bottom
173             14 - fixed to left, center
174             15 - fixed to right, center
175             16 - center, center (no scale)
176         */
177 
178         int mode_map_index = mode - 1;
179         if (mode_map_index < 0 || mode_map_index >= 16)
180             return;
181 
182         static int mode_map[16][2] = { { AlignLeft, AlignTop },
183                                        { TileHorizontally, AlignTop },
184                                        { AlignRight, AlignTop },
185                                        { AlignLeft, TileVertically },
186                                        { TileHorizontally, TileVertically },
187                                        { AlignRight, TileVertically },
188                                        { AlignLeft, AlignBottom },
189                                        { TileHorizontally, AlignBottom },
190                                        { AlignRight, AlignBottom },
191                                        { StretchHorizontally, StretchVertically },
192                                        { AlignHCenter, AlignVCenter },
193                                        { AlignHCenter, AlignTop },
194                                        { AlignHCenter, AlignBottom },
195                                        { AlignLeft, AlignVCenter },
196                                        { AlignRight, AlignVCenter },
197                                        { AlignHCenter, AlignVCenter } };
198 
199         horizontalMode = (HorizontalMode)mode_map[mode_map_index][0];
200         verticalMode = (VerticalMode)mode_map[mode_map_index][1];
201         scaleToFit = (mode == 11);
202     }
203 };
204 
205 } // namespace QtCollider
206