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