1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 
19 #include "PixmapFunctions.h"
20 
21 #include <QBitmap>
22 #include <QColor>
23 #include <QImage>
24 #include <QPainter>
25 #include <QPixmap>
26 
27 #include <iostream>
28 
29 namespace Rosegarden
30 {
31 
32 QBitmap
generateMask(const QPixmap & map,const QRgb & px)33 PixmapFunctions::generateMask(const QPixmap &map, const QRgb &px)
34 {
35     QImage i(map.toImage());
36     // QImage im(i.width(), i.height(), 1, 2, QImage::LittleEndian);
37     QImage im(i.width(), i.height(), QImage::Format_MonoLSB);
38 
39     for (int y = 0; y < i.height(); ++y) {
40         for (int x = 0; x < i.width(); ++x) {
41             if (i.pixel(x, y) != px) {
42                 im.setPixel(x, y, 1);
43             } else {
44                 im.setPixel(x, y, 0);
45             }
46         }
47     }
48 
49     QBitmap m = QBitmap::fromImage(im);
50     return m;
51 }
52 
53 QBitmap
generateMask(const QPixmap & map)54 PixmapFunctions::generateMask(const QPixmap &map)
55 {
56     QImage i(map.toImage());
57     QImage im(i.width(), i.height(), QImage::Format_MonoLSB);
58 
59     QRgb px0(i.pixel(0, 0));
60     QRgb px1(i.pixel(i.width() - 1, 0));
61     QRgb px2(i.pixel(i.width() - 1, i.height() - 1));
62     QRgb px3(i.pixel(0, i.height() - 1));
63 
64     QRgb px(px0);
65     if (px0 != px2 && px1 == px3)
66         px = px1;
67 
68     for (int y = 0; y < i.height(); ++y) {
69         for (int x = 0; x < i.width(); ++x) {
70             if (i.pixel(x, y) != px) {
71                 im.setPixel(x, y, 1);
72             } else {
73                 im.setPixel(x, y, 0);
74             }
75         }
76     }
77 
78     QBitmap m = QBitmap::fromImage(im);
79     return m;
80 }
81 
82 QPixmap
colourPixmap(const QPixmap & map,int hue,int minimum,int saturation)83 PixmapFunctions::colourPixmap(const QPixmap &map, int hue, int minimum, int saturation)
84 {
85     // assumes pixmap is currently in shades of grey; maps black ->
86     // solid colour and greys -> shades of colour
87 
88     QImage image = map.toImage();
89 
90 // This function has become obsolete and is no longer needed here anyway.
91     // save a copy of the original alpha channel
92 //  QImage alpha = image.alphaChannel();
93 
94     int s, v;
95 
96     bool warned = false;
97 
98     for (int y = 0; y < image.height(); ++y) {
99 
100         for (int x = 0; x < image.width(); ++x) {
101 
102             QRgb oldPixel = image.pixel(x, y);
103             QColor oldColour(oldPixel);  // This doesn't seem to pick up the alpha channel.
104 
105             // Explicitly set the alpha channel, making it a little stronger so that
106             // colored note heads won't appear smaller than ordinary black ones.
107             oldColour.setAlpha(int(qAlpha(oldPixel) * 1.5) > 255 ? 255 : int(qAlpha(oldPixel) * 1.5));
108 
109             int oldHue;
110             oldColour.getHsv(&oldHue, &s, &v);
111 
112             int newHue = hue;
113 
114             if (oldHue >= 0) {
115                 if (!warned) {
116                     std::cerr << "PixmapFunctions::recolour: Not a greyscale pixmap "
117                               << "(found rgb value " << oldColour.red() << ","
118                               << oldColour.green() << "," << oldColour.blue()
119                               << "), hoping for the best" << std::endl;
120                     warned = true;
121                 }
122                 newHue = hue;
123             }
124 
125             // use the specified saturation, if present; otherwise the old
126             // behaviour of subtracting the minimum value setting from a maximum
127             // saturation of 255
128             int newSaturation = (saturation == SaturationNotSpecified  ? 255 - v : saturation);
129 
130             QColor newColour = QColor::fromHsv(
131                                  newHue,
132                                  newSaturation,
133                                  v > minimum ? v : minimum);
134 
135 //          QRgb newPixel = qRgba(newColour.red(),
136 //                                newColour.green(),
137 //                                newColour.blue(),
138 //                                qAlpha(oldPixel));
139 
140             // For some reason, while in the raster-graphics mode, the alpha channel
141             // inverts the hues, rather than simply controlling pixel transparency.
142             //
143             // One way around the problem seems to be to modulate the individual
144             // color components with the alpha channel, in addition to applying the
145             // alpha channel in the usual way.
146             QRgb newPixel = qRgba(int(newColour.red() * oldColour.alphaF()),
147                                   int(newColour.green() * oldColour.alphaF()),
148                                   int(newColour.blue() * oldColour.alphaF()),
149                                   oldColour.alpha());
150 
151             image.setPixel(x, y, newPixel);
152         }
153     }
154 
155 // This function has become obsolete and is no longer needed here anyway.
156     // restore the original alpha channel
157 //  image.setAlphaChannel(alpha);
158 
159     QPixmap rmap = QPixmap::fromImage(image);
160 // This function is no longer needed here.
161 //  if (!map.mask().isNull()) rmap.setMask(map.mask());
162     return rmap;
163 }
164 
165 QPixmap
shadePixmap(const QPixmap & map)166 PixmapFunctions::shadePixmap(const QPixmap &map)
167 {
168     QImage image = map.toImage();
169 
170     int h, s, v;
171 
172     for (int y = 0; y < image.height(); ++y) {
173         for (int x = 0; x < image.width(); ++x) {
174 
175             QColor pixel(image.pixel(x, y));
176 
177             pixel.getHsv(&h, &s, &v);
178 
179             int newV =  255 - ((255 - v) / 2);
180             QColor newColor = QColor::fromHsv(h, s, newV);
181 
182             image.setPixel(x, y, newColor.rgb());
183 
184         }
185     }
186 
187     QPixmap rmap = QPixmap::fromImage(image);
188     if (!map.mask().isNull()) rmap.setMask(map.mask());
189     return rmap;
190 }
191 
192 QPixmap
flipVertical(const QPixmap & map)193 PixmapFunctions::flipVertical(const QPixmap &map)
194 {
195     QImage i(map.toImage());
196     QPixmap rmap = QPixmap::fromImage(i.mirrored(false, true));
197 
198     if (!map.mask().isNull()) {
199         QImage im(map.mask().toImage());
200         QBitmap newMask = QBitmap::fromImage(im.mirrored(false, true));
201         rmap.setMask(newMask);
202     }
203 
204     return rmap;
205 }
206 
207 QPixmap
flipHorizontal(const QPixmap & map)208 PixmapFunctions::flipHorizontal(const QPixmap &map)
209 {
210     QImage i(map.toImage());
211     QPixmap rmap = QPixmap::fromImage(i.mirrored(true, false));
212 
213     if (!map.mask().isNull()) {
214         QImage im(map.mask().toImage());
215         QBitmap newMask = QBitmap::fromImage(im.mirrored(true, false));
216         rmap.setMask(newMask);
217     }
218 
219     return rmap;
220 }
221 
222 std::pair<QPixmap, QPixmap>
splitPixmap(const QPixmap & pixmap,int x)223 PixmapFunctions::splitPixmap(const QPixmap &pixmap, int x)
224 {
225     //@@@ JAS ?need error check on pixmap.width and x? (x <= width)
226     QPixmap left(x, pixmap.height());
227     left.fill(Qt::transparent);
228 
229     QPixmap right(pixmap.width() - x, pixmap.height());
230     right.fill(Qt::transparent);
231 
232     QPainter paint;
233 
234     paint.begin(&left);
235     paint.drawPixmap(0, 0, pixmap, 0, 0, left.width(), left.height());
236     paint.end();
237 
238     paint.begin(&right);
239     paint.drawPixmap(0, 0, pixmap, left.width(), 0, right.width(), right.height());
240     paint.end();
241 
242     return std::pair<QPixmap, QPixmap>(left, right);
243 }
244 
245 void
drawPixmapMasked(QPixmap & dest,QBitmap & destMask,int x0,int y0,const QPixmap & src)246 PixmapFunctions::drawPixmapMasked(QPixmap &dest, QBitmap &destMask,
247                                   int x0, int y0,
248                                   const QPixmap &src)
249 {
250     QImage idp(dest.toImage());
251     QImage idm(destMask.toImage());
252     QImage isp(src.toImage());
253     QImage ism(src.mask().toImage());
254 
255     for (int y = 0; y < isp.height(); ++y) {
256         for (int x = 0; x < isp.width(); ++x) {
257 
258             if (x >= ism.width())
259                 continue;
260             if (y >= ism.height())
261                 continue;
262 
263             if (ism.depth() == 1 && ism.pixel(x, y) == 0)
264                 continue;
265             if (ism.pixel(x, y) == QColor(Qt::white).rgb())
266                 continue;
267 
268             int x1 = x + x0;
269             int y1 = y + y0;
270             if (x1 < 0 || x1 >= idp.width())
271                 continue;
272             if (y1 < 0 || y1 >= idp.height())
273                 continue;
274 
275             idp.setPixel(x1, y1, isp.pixel(x, y));
276             idm.setPixel(x1, y1, 1);
277         }
278     }
279 
280     dest = QPixmap::fromImage(idp);
281     destMask = QBitmap::fromImage(idm);
282 }
283 
284 }
285