1 /*
2 * Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
3 * Copyright (c) 2008 Bart Coppens <kde@bartcoppens.be>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include "KoRgbU8ColorSpaceTester.h"
21
22 #include "KoColorModelStandardIds.h"
23
24 #include <QTest>
25 #include <DebugPigment.h>
26 #include <string.h>
27
28 #include "KoColor.h"
29 #include "KoColorSpace.h"
30 #include "KoColorSpaceRegistry.h"
31 #include "KoCompositeOp.h"
32 #include "KoMixColorsOp.h"
33 #include <KoCompositeOpRegistry.h>
34
35
36 #define NUM_CHANNELS 4
37
38 #define RED_CHANNEL 0
39 #define GREEN_CHANNEL 1
40 #define BLUE_CHANNEL 2
41 #define ALPHA_CHANNEL 3
42 #include <KoColorProfile.h>
43
testBasics()44 void KoRgbU8ColorSpaceTester::testBasics()
45 {
46 }
47
48 #define PIXEL_RED 0
49 #define PIXEL_GREEN 1
50 #define PIXEL_BLUE 2
51 #define PIXEL_ALPHA 3
52
testMixColors()53 void KoRgbU8ColorSpaceTester::testMixColors()
54 {
55 const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
56 KoMixColorsOp * mixOp = cs->mixColorsOp();
57
58 // Test mixColors.
59 quint8 pixel1[4];
60 quint8 pixel2[4];
61 quint8 outputPixel[4];
62
63 pixel1[PIXEL_RED] = 255;
64 pixel1[PIXEL_GREEN] = 255;
65 pixel1[PIXEL_BLUE] = 255;
66 pixel1[PIXEL_ALPHA] = 255;
67
68 pixel2[PIXEL_RED] = 0;
69 pixel2[PIXEL_GREEN] = 0;
70 pixel2[PIXEL_BLUE] = 0;
71 pixel2[PIXEL_ALPHA] = 0;
72
73 const quint8 *pixelPtrs[2];
74 qint16 weights[2];
75
76 pixelPtrs[0] = pixel1;
77 pixelPtrs[1] = pixel2;
78
79 weights[0] = 255;
80 weights[1] = 0;
81
82 mixOp->mixColors(pixelPtrs, weights, 2, outputPixel);
83
84 QVERIFY((int)outputPixel[PIXEL_RED] == 255);
85 QVERIFY((int)outputPixel[PIXEL_GREEN] == 255);
86 QVERIFY((int)outputPixel[PIXEL_BLUE] == 255);
87 QVERIFY((int)outputPixel[PIXEL_ALPHA] == 255);
88
89 weights[0] = 0;
90 weights[1] = 255;
91
92 mixOp->mixColors(pixelPtrs, weights, 2, outputPixel);
93
94 QVERIFY((int)outputPixel[PIXEL_RED] == 0);
95 QVERIFY((int)outputPixel[PIXEL_GREEN] == 0);
96 QVERIFY((int)outputPixel[PIXEL_BLUE] == 0);
97 QVERIFY((int)outputPixel[PIXEL_ALPHA] == 0);
98
99 weights[0] = 128;
100 weights[1] = 127;
101
102 mixOp->mixColors(pixelPtrs, weights, 2, outputPixel);
103
104 QVERIFY((int)outputPixel[PIXEL_RED] == 255);
105 QVERIFY((int)outputPixel[PIXEL_GREEN] == 255);
106 QVERIFY((int)outputPixel[PIXEL_BLUE] == 255);
107 QVERIFY((int)outputPixel[PIXEL_ALPHA] == 128);
108
109 pixel1[PIXEL_RED] = 200;
110 pixel1[PIXEL_GREEN] = 100;
111 pixel1[PIXEL_BLUE] = 50;
112 pixel1[PIXEL_ALPHA] = 255;
113
114 pixel2[PIXEL_RED] = 100;
115 pixel2[PIXEL_GREEN] = 200;
116 pixel2[PIXEL_BLUE] = 20;
117 pixel2[PIXEL_ALPHA] = 255;
118
119 mixOp->mixColors(pixelPtrs, weights, 2, outputPixel);
120
121 QVERIFY((int)outputPixel[PIXEL_RED] == 150);
122 QCOMPARE((int)outputPixel[PIXEL_GREEN], 150);
123 QVERIFY((int)outputPixel[PIXEL_BLUE] == 35);
124 QVERIFY((int)outputPixel[PIXEL_ALPHA] == 255);
125
126 pixel1[PIXEL_RED] = 0;
127 pixel1[PIXEL_GREEN] = 0;
128 pixel1[PIXEL_BLUE] = 0;
129 pixel1[PIXEL_ALPHA] = 0;
130
131 pixel2[PIXEL_RED] = 255;
132 pixel2[PIXEL_GREEN] = 255;
133 pixel2[PIXEL_BLUE] = 255;
134 pixel2[PIXEL_ALPHA] = 254;
135
136 weights[0] = 89;
137 weights[1] = 166;
138
139 mixOp->mixColors(pixelPtrs, weights, 2, outputPixel);
140
141 QVERIFY((int)outputPixel[PIXEL_RED] == 255);
142 QVERIFY((int)outputPixel[PIXEL_GREEN] == 255);
143 QVERIFY((int)outputPixel[PIXEL_BLUE] == 255);
144 QVERIFY((int)outputPixel[PIXEL_ALPHA] == 165);
145 }
testMixColorsAverage()146 void KoRgbU8ColorSpaceTester::testMixColorsAverage()
147 {
148 const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
149 KoMixColorsOp * mixOp = cs->mixColorsOp();
150
151 // Test mixColors.
152 quint8 pixel1[4];
153 quint8 pixel2[4];
154 quint8 outputPixel[4];
155
156 pixel1[PIXEL_RED] = 255;
157 pixel1[PIXEL_GREEN] = 255;
158 pixel1[PIXEL_BLUE] = 255;
159 pixel1[PIXEL_ALPHA] = 255;
160
161 pixel2[PIXEL_RED] = 0;
162 pixel2[PIXEL_GREEN] = 0;
163 pixel2[PIXEL_BLUE] = 0;
164 pixel2[PIXEL_ALPHA] = 0;
165
166 const quint8 *pixelPtrs[2];
167
168 pixelPtrs[0] = pixel1;
169 pixelPtrs[1] = pixel2;
170
171 mixOp->mixColors(pixelPtrs, 2, outputPixel);
172
173 QCOMPARE((int)outputPixel[PIXEL_RED], 255);
174 QCOMPARE((int)outputPixel[PIXEL_GREEN], 255);
175 QCOMPARE((int)outputPixel[PIXEL_BLUE], 255);
176 QCOMPARE((int)outputPixel[PIXEL_ALPHA], 128);
177
178 pixel2[PIXEL_ALPHA] = 255;
179 mixOp->mixColors(pixelPtrs, 2, outputPixel);
180
181 QCOMPARE((int)outputPixel[PIXEL_RED], 128);
182 QCOMPARE((int)outputPixel[PIXEL_GREEN], 128);
183 QCOMPARE((int)outputPixel[PIXEL_BLUE], 128);
184 QCOMPARE((int)outputPixel[PIXEL_ALPHA], 255);
185 }
186
testCompositeOps()187 void KoRgbU8ColorSpaceTester::testCompositeOps()
188 {
189 // Just COMPOSITE_COPY for now
190
191 QList<KoID> depthIDs = KoColorSpaceRegistry::instance()->colorDepthList(RGBAColorModelID.id(),
192 KoColorSpaceRegistry::AllColorSpaces);
193
194 Q_FOREACH (const KoID& depthId, depthIDs) {
195
196 if (depthId.id().contains("Float")) continue;
197
198 dbgPigment << depthId.id();
199 const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(
200 RGBAColorModelID.id(), depthId.id(), "");
201
202 const KoCompositeOp* copyOp = cs->compositeOp(COMPOSITE_COPY);
203 KoColor src(cs), dst(cs);
204
205 QColor red(255, 0, 0);
206 QColor blue(0, 0, 255);
207 QColor transparentRed(255, 0, 0, 0);
208
209 // Copying a color over another color should replace the original color
210 src.fromQColor(red);
211 dst.fromQColor(blue);
212
213 dbgPigment << src.toQColor() << dst.toQColor();
214
215 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) != 0);
216
217 copyOp->composite(dst.data(), cs->pixelSize(), src.data(), cs->pixelSize(),
218 0, 0, 1, 1, OPACITY_OPAQUE_U8);
219
220 src.fromQColor(red);
221 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) == 0);
222
223 // Copying something transparent over something non-transparent should, of course, make the dst transparent
224 src.fromQColor(transparentRed);
225 dst.fromQColor(blue);
226
227 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) != 0);
228
229 copyOp->composite(dst.data(), cs->pixelSize(), src.data(), cs->pixelSize(),
230 0, 0, 1, 1, OPACITY_OPAQUE_U8);
231
232 src.fromQColor(transparentRed);
233 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) == 0);
234
235 // Copying something solid over something transparent
236 src.fromQColor(blue);
237 dst.fromQColor(transparentRed);
238
239 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) != 0);
240
241 copyOp->composite(dst.data(), cs->pixelSize(), src.data(), cs->pixelSize(),
242 0, 0, 1, 1, OPACITY_OPAQUE_U8);
243
244 src.fromQColor(blue);
245 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) == 0);
246
247 }
248 }
249
testCompositeOpsWithChannelFlags()250 void KoRgbU8ColorSpaceTester::testCompositeOpsWithChannelFlags()
251 {
252 const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
253 QList<KoCompositeOp*> ops = cs->compositeOps();
254
255 Q_FOREACH (const KoCompositeOp *op, ops) {
256 /**
257 * ALPHA_DARKEN composite op doesn't take channel
258 * flags into account, so just skip it
259 */
260 if (op->id() == COMPOSITE_ALPHA_DARKEN) continue;
261 if (op->id() == COMPOSITE_DISSOLVE) continue;
262
263 quint8 src[] = {128,128,128,129};
264 quint8 goodDst[] = {10,10,10,11};
265 quint8 badDst[] = {12,12,12,0};
266
267 KoCompositeOp::ParameterInfo params;
268 params.maskRowStart = 0;
269 params.dstRowStride = 0;
270 params.srcRowStride = 0;
271 params.maskRowStride = 0;
272 params.rows = 1;
273 params.cols = 1;
274 params.opacity = 1.0f;
275 params.flow = 1.0f;
276
277 QBitArray channelFlags(4, true);
278 channelFlags[2] = false;
279 params.channelFlags = channelFlags;
280
281 params.srcRowStart = src;
282
283 params.dstRowStart = goodDst;
284 op->composite(params);
285
286 params.dstRowStart = badDst;
287 op->composite(params);
288
289 /**
290 * The badDst has zero alpha, so the channels should be zeroed
291 * before increasing alpha of the pixel
292 */
293 if (badDst[3] != 0 && badDst[2] != 0) {
294 dbgPigment << op->id()
295 << "easy case:" << goodDst[2]
296 << "difficult case:" << badDst[2];
297
298 dbgPigment << "The composite op has failed to erase the color "
299 "channel which was hidden by zero alpha.";
300 dbgPigment << "Expected Blue channel:" << 0;
301 dbgPigment << "Actual Blue channel: " << badDst[2];
302
303 QFAIL("Failed to erase color channel");
304 }
305 }
306 }
307
308 QTEST_GUILESS_MAIN(KoRgbU8ColorSpaceTester)
309