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], 149);
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 }
146
testCompositeOps()147 void KoRgbU8ColorSpaceTester::testCompositeOps()
148 {
149 // Just COMPOSITE_COPY for now
150
151 QList<KoID> depthIDs = KoColorSpaceRegistry::instance()->colorDepthList(RGBAColorModelID.id(),
152 KoColorSpaceRegistry::AllColorSpaces);
153
154 foreach(const KoID& depthId, depthIDs) {
155
156 if (depthId.id().contains("Float")) continue;
157
158 dbgPigment << depthId.id();
159 const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(
160 RGBAColorModelID.id(), depthId.id(), "");
161
162 const KoCompositeOp* copyOp = cs->compositeOp(COMPOSITE_COPY);
163 KoColor src(cs), dst(cs);
164
165 QColor red(255, 0, 0);
166 QColor blue(0, 0, 255);
167 QColor transparentRed(255, 0, 0, 0);
168
169 // Copying a color over another color should replace the original color
170 src.fromQColor(red);
171 dst.fromQColor(blue);
172
173 dbgPigment << src.toQColor() << dst.toQColor();
174
175 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) != 0);
176
177 copyOp->composite(dst.data(), cs->pixelSize(), src.data(), cs->pixelSize(),
178 0, 0, 1, 1, OPACITY_OPAQUE_U8);
179
180 src.fromQColor(red);
181 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) == 0);
182
183 // Copying something transparent over something non-transparent should, of course, make the dst transparent
184 src.fromQColor(transparentRed);
185 dst.fromQColor(blue);
186
187 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) != 0);
188
189 copyOp->composite(dst.data(), cs->pixelSize(), src.data(), cs->pixelSize(),
190 0, 0, 1, 1, OPACITY_OPAQUE_U8);
191
192 src.fromQColor(transparentRed);
193 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) == 0);
194
195 // Copying something solid over something transparent
196 src.fromQColor(blue);
197 dst.fromQColor(transparentRed);
198
199 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) != 0);
200
201 copyOp->composite(dst.data(), cs->pixelSize(), src.data(), cs->pixelSize(),
202 0, 0, 1, 1, OPACITY_OPAQUE_U8);
203
204 src.fromQColor(blue);
205 QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) == 0);
206
207 }
208 }
209
testCompositeOpsWithChannelFlags()210 void KoRgbU8ColorSpaceTester::testCompositeOpsWithChannelFlags()
211 {
212 const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
213 QList<KoCompositeOp*> ops = cs->compositeOps();
214
215 foreach(const KoCompositeOp *op, ops) {
216 /**
217 * ALPHA_DARKEN composite op doesn't take channel
218 * flags into account, so just skip it
219 */
220 if (op->id() == COMPOSITE_ALPHA_DARKEN) continue;
221 if (op->id() == COMPOSITE_DISSOLVE) continue;
222
223 quint8 src[] = {128,128,128,129};
224 quint8 goodDst[] = {10,10,10,11};
225 quint8 badDst[] = {12,12,12,0};
226
227 KoCompositeOp::ParameterInfo params;
228 params.maskRowStart = 0;
229 params.dstRowStride = 0;
230 params.srcRowStride = 0;
231 params.maskRowStride = 0;
232 params.rows = 1;
233 params.cols = 1;
234 params.opacity = 1.0f;
235 params.flow = 1.0f;
236
237 QBitArray channelFlags(4, true);
238 channelFlags[2] = false;
239 params.channelFlags = channelFlags;
240
241 params.srcRowStart = src;
242
243 params.dstRowStart = goodDst;
244 op->composite(params);
245
246 params.dstRowStart = badDst;
247 op->composite(params);
248
249 /**
250 * The badDst has zero alpha, so the channels should be zeroed
251 * before increasing alpha of the pixel
252 */
253 if (badDst[3] != 0 && badDst[2] != 0) {
254 dbgPigment << op->id()
255 << "easy case:" << goodDst[2]
256 << "difficult case:" << badDst[2];
257
258 dbgPigment << "The composite op has failed to erase the color "
259 "channel which was hidden by zero alpha.";
260 dbgPigment << "Expected Blue channel:" << 0;
261 dbgPigment << "Actual Blue channel: " << badDst[2];
262
263 QFAIL("Failed to erase color channel");
264 }
265 }
266 }
267
268 QTEST_GUILESS_MAIN(KoRgbU8ColorSpaceTester)
269