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