there is no moc file generated for this class
[kdegraphics.git] / kolourpaint / pixmapfx / kpPixmapFX_MaskOps.cpp
blob88c1f69d827ca82e889e347e47177405972f4034
2 /*
3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #define DEBUG_KP_PIXMAP_FX 0
32 #include <kpPixmapFX.h>
34 #include <cstdlib>
35 #include <cmath>
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #include <unistd.h>
40 #include <qapplication.h>
41 #include <qbitmap.h>
42 #include <qdatetime.h>
43 #include <qimage.h>
44 #include <qpainter.h>
45 #include <qpainterpath.h>
46 #include <qpixmap.h>
47 #include <qpoint.h>
48 #include <qpolygon.h>
49 #include <qrect.h>
51 #include <KApplication>
52 #include <kconfig.h>
53 #include <kconfiggroup.h>
54 #include <kdebug.h>
55 #include <kglobal.h>
56 #include <klocale.h>
57 #include <kmessagebox.h>
59 #include <kpAbstractSelection.h>
60 #include <kpColor.h>
61 #include <kpDefs.h>
62 #include <kpTool.h>
65 #if DEBUG_KP_PIXMAP_FX
66 #define KP_PRINTF if (1) printf
67 #else
68 #define KP_PRINTF if (0) (void)
69 #endif
72 #ifdef Q_WS_X11
73 // Same as QPixmap::defaultDepth(), but returns a meaningful answer when
74 // called before QApplication has been constructed, rather than always 32.
75 // After QApplication has been constructed, you must use QPixmap::defaultDepth()
76 // instead.
78 // Returns 0 if it encounters an error.
80 // Internally, this method forks the process. In the child, a KApplication
81 // is constructed, QPixmap::defaultDepth() is called and the result is sent to
82 // the parent. The child is then killed.
83 static int QPixmapCalculateDefaultDepthWithoutQApplication ()
85 KP_PRINTF (("QPixmapCalculateDefaultDepthWithoutQApplication()\n"));
87 // [0] = read
88 // [1] = write
89 enum
91 Read = 0, Write = 1
93 int fds [2];
94 if (pipe (fds) != 0)
96 perror ("pipe");
97 return 0;
100 pid_t pid = fork ();
101 if (pid == -1)
103 perror ("fork");
104 close (fds [Read]);
105 close (fds [Write]);
106 return 0;
109 // In child?
110 if (pid == 0)
112 KP_PRINTF ("Child: created\n");
113 close (fds [Read]);
115 KApplication app;
117 const int depth = QPixmap::defaultDepth ();
118 KP_PRINTF (("Child: writing default depth\n"));
119 write (fds [Write], &depth, sizeof (depth));
120 KP_PRINTF (("Child: wrote default depth\n"));
122 close (fds [Write]);
123 KP_PRINTF ("Child: exit\n");
124 exit (0);
126 // In parent?
127 else
129 KP_PRINTF ("Parent: in here\n");
130 close (fds [Write]);
132 int depth = 0;
133 KP_PRINTF ("Parent: reading default depth\n");
134 read (fds [Read], &depth, sizeof (depth));
135 KP_PRINTF ("Parent: read default depth %d\n", depth);
137 close (fds [Read]);
139 // Kill zombie child.
140 KP_PRINTF ("Parent: waiting for child\n");
141 int status;
142 (void) waitpid (pid, &status, 0/*options*/);
144 KP_PRINTF ("Parent: complete\n");
145 return depth;
148 #endif
151 // public static
152 // (KApplication has not been constructed yet)
153 void kpPixmapFX::initMaskOpsPre ()
155 #ifdef Q_WS_X11
156 const int defaultDepth = QPixmapCalculateDefaultDepthWithoutQApplication ();
158 // This is important for diagnosing KolourPaint bugs so always print it
159 // -- even in release mode.
160 printf ("Starting KolourPaint on a %d-bit screen...\n", defaultDepth);
162 if (defaultDepth == 32)
164 KP_PRINTF ("\tCannot handle alpha channel - disabling XRENDER\n");
166 // SYNC: Might break with Qt upgrades.
167 setenv ("QT_X11_NO_XRENDER", "1", 1/*overwrite value*/);
169 #else
170 #ifdef __GNUC__
171 #warning "KolourPaint is heavily dependent on the behavior of QPixmap under X11."
172 #warning "Until KolourPaint gets a proper image library, it is unlikely to work under non-X11."
173 #endif
174 #endif
177 // public static
178 void kpPixmapFX::initMaskOpsPost ()
180 #if DEBUG_KP_PIXMAP_FX
181 kDebug () << "kpPixmapFX::initMaskOpsPost():"
182 << "QPixmap().depth()=" << QPixmap ().depth ()
183 << "QPixmap::defaultDepth()=" << QPixmap::defaultDepth ();
184 #endif
186 // Check KolourPaint invariant.
187 KP_PFX_CHECK_NO_ALPHA_CHANNEL (QPixmap ());
188 KP_PFX_CHECK_NO_ALPHA_CHANNEL (QPixmap (1, 1));
189 Q_ASSERT (QPixmap ().depth () == QPixmap::defaultDepth ());
190 Q_ASSERT (QPixmap (1, 1).depth () == QPixmap::defaultDepth ());
192 // initMaskOpsPre() should have ensured this (if it was 32 previously, it
193 // should now be 24 due to the disabling of XRENDER).
194 Q_ASSERT (QPixmap::defaultDepth () < 32);
198 // public static
199 bool kpPixmapFX::hasMask (const QPixmap &pixmap)
201 return pixmap.hasAlpha ();
204 // public static
205 bool kpPixmapFX::hasAlphaChannel (const QPixmap &pixmap)
207 return pixmap.hasAlphaChannel ();
210 // public static
211 bool kpPixmapFX::checkNoAlphaChannelInternal (const QPixmap &pixmap)
213 if (!kpPixmapFX::hasAlphaChannel (pixmap))
214 return true;
216 #if 1
217 kError () << "Pixmap has alpha channel. See the .h doc for"
218 " kpPixmapFX::ensureNoAlphaChannel() to fix this.";
219 // Ignore bug rather than crashing the program because I bet developers
220 // will inadvertently trigger this, when changing the code. The bug has
221 // very annoying effects under XRENDER but only causes serious failure
222 // without XRENDER (which is not so common).
223 return true;
224 #else
225 // Assert-crash the program.
226 return false;
227 #endif
231 // public static
232 void kpPixmapFX::ensureNoAlphaChannel (QPixmap *destPixmapPtr)
234 Q_ASSERT (destPixmapPtr);
236 if (kpPixmapFX::hasAlphaChannel (*destPixmapPtr))
238 // We need to change <destPixmapPtr> from 32-bit (RGBA
239 // i.e. hasAlphaChannel() returns true -- regardless of whether the
240 // channel has any actual content -- which causes trouble) to 24-bit
241 // (RGB with possible mask).
243 // "destPixmapPtr->setMask (destPixmapPtr->mask())" is not
244 // sufficient so do it a long way:
246 QPixmap oldPixmap = *destPixmapPtr;
248 QBitmap oldMask = oldPixmap.mask ();
249 // Kill RGB source mask in case it causes alpha channel / composition
250 // operations in the following copy.
251 oldPixmap.setMask (QBitmap ());
253 // Copy RGB layer.
254 *destPixmapPtr = QPixmap (oldPixmap.width (), oldPixmap.height ());
255 QPainter p (destPixmapPtr);
256 p.drawPixmap (QPoint (0, 0), oldPixmap);
257 p.end ();
259 // Copy mask layer (if any).
260 destPixmapPtr->setMask (oldMask);
263 // Note that we don't check this on function entry as the purpose of
264 // this function is to force the pixmap to satisfy this invariant.
265 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
269 // public static
270 QBitmap kpPixmapFX::getNonNullMask (const QPixmap &pm)
272 KP_PFX_CHECK_NO_ALPHA_CHANNEL (pm);
274 // (a bit slow so we cache it)
275 const QBitmap mask = pm.mask ();
277 if (!mask.isNull ())
278 return mask;
279 else
281 QBitmap maskBitmap (pm.width (), pm.height ());
282 maskBitmap.fill (Qt::color1/*opaque*/);
284 return maskBitmap;
289 // public static
290 void kpPixmapFX::ensureTransparentAt (QPixmap *destPixmapPtr, const QRect &destRect)
292 if (!destPixmapPtr)
293 return;
295 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
297 QBitmap maskBitmap = getNonNullMask (*destPixmapPtr);
299 QPainter p (&maskBitmap);
300 p.fillRect (destRect, Qt::color0/*transparent*/);
301 p.end ();
303 destPixmapPtr->setMask (maskBitmap);
305 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
309 // public static
310 void kpPixmapFX::paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, const QPoint &destAt,
311 const QPixmap &brushBitmap)
313 #if DEBUG_KP_PIXMAP_FX
314 kDebug () << "kpPixmapFX::paintMaskTransparentWithBrush(destAt="
315 << destAt << ") brushRect=" << brushBitmap.rect ()
316 << endl;
317 #endif
319 Q_ASSERT (destPixmapPtr);
321 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
323 Q_ASSERT (brushBitmap.depth () == 1);
325 const QRegion brushRegion = QRegion (brushBitmap).translated (destAt);
327 // OPT: Hopelessly inefficent due to function call overhead and
328 // fillRect() changing the mask every single iteration.
330 // kpPixmapFX should have a function that does this with only a
331 // single mask write.
332 foreach (const QRect &r, brushRegion.rects ())
334 #if DEBUG_KP_PIXMAP_FX && 0
335 kDebug () << "\tcopy rect=" << r;
336 #endif
337 kpPixmapFX::fillRect (destPixmapPtr,
338 r.x (), r.y (), r.width (), r.height (),
339 kpColor::Transparent);
342 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
345 // public static
346 void kpPixmapFX::paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, int destX, int destY,
347 const QPixmap &brushBitmap)
349 kpPixmapFX::paintMaskTransparentWithBrush (destPixmapPtr,
350 QPoint (destX, destY),
351 brushBitmap);
355 // public static
356 void kpPixmapFX::ensureOpaqueAt (QPixmap *destPixmapPtr, const QRect &destRect)
358 if (!destPixmapPtr)
359 return;
361 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
363 // (a bit slow so we cache it)
364 QBitmap maskBitmap = destPixmapPtr->mask ();
366 // Already opaque?
367 if (maskBitmap.isNull ())
368 return;
370 QPainter p (&maskBitmap);
371 p.fillRect (destRect, Qt::color1/*opaque*/);
372 p.end ();
374 destPixmapPtr->setMask (maskBitmap);
376 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);