compile
[kdegraphics.git] / kolourpaint / pixmapfx / kpPixmapFX_MaskOps.cpp
blob0dd8cecd6f10d401216b414bcb6f8c4cfc1f0451
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 (1, 1).depth () == QPixmap::defaultDepth ());
191 // initMaskOpsPre() should have ensured this (if it was 32 previously, it
192 // should now be 24 due to the disabling of XRENDER).
193 Q_ASSERT (QPixmap::defaultDepth () < 32);
197 // public static
198 bool kpPixmapFX::hasMask (const QPixmap &pixmap)
200 return pixmap.hasAlpha ();
203 // public static
204 bool kpPixmapFX::hasAlphaChannel (const QPixmap &pixmap)
206 return pixmap.hasAlphaChannel ();
209 // public static
210 bool kpPixmapFX::checkNoAlphaChannelInternal (const QPixmap &pixmap)
212 if (!kpPixmapFX::hasAlphaChannel (pixmap))
213 return true;
215 #if 1
216 kError () << "Pixmap has alpha channel. See the .h doc for"
217 " kpPixmapFX::ensureNoAlphaChannel() to fix this.";
218 // Ignore bug rather than crashing the program because I bet developers
219 // will inadvertently trigger this, when changing the code. The bug has
220 // very annoying effects under XRENDER but only causes serious failure
221 // without XRENDER (which is not so common).
222 return true;
223 #else
224 // Assert-crash the program.
225 return false;
226 #endif
230 // public static
231 void kpPixmapFX::ensureNoAlphaChannel (QPixmap *destPixmapPtr)
233 Q_ASSERT (destPixmapPtr);
235 if (kpPixmapFX::hasAlphaChannel (*destPixmapPtr))
237 // We need to change <destPixmapPtr> from 32-bit (RGBA
238 // i.e. hasAlphaChannel() returns true -- regardless of whether the
239 // channel has any actual content -- which causes trouble) to 24-bit
240 // (RGB with possible mask).
242 // "destPixmapPtr->setMask (destPixmapPtr->mask())" is not
243 // sufficient so do it a long way:
245 QPixmap oldPixmap = *destPixmapPtr;
247 QBitmap oldMask = oldPixmap.mask ();
248 // Kill RGB source mask in case it causes alpha channel / composition
249 // operations in the following copy.
250 oldPixmap.setMask (QBitmap ());
252 // Copy RGB layer.
253 *destPixmapPtr = QPixmap (oldPixmap.width (), oldPixmap.height ());
254 QPainter p (destPixmapPtr);
255 p.drawPixmap (QPoint (0, 0), oldPixmap);
256 p.end ();
258 // Copy mask layer (if any).
259 destPixmapPtr->setMask (oldMask);
262 // Note that we don't check this on function entry as the purpose of
263 // this function is to force the pixmap to satisfy this invariant.
264 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
268 // public static
269 QBitmap kpPixmapFX::getNonNullMask (const QPixmap &pm)
271 KP_PFX_CHECK_NO_ALPHA_CHANNEL (pm);
273 // (a bit slow so we cache it)
274 const QBitmap mask = pm.mask ();
276 if (!mask.isNull ())
277 return mask;
278 else
280 QBitmap maskBitmap (pm.width (), pm.height ());
281 maskBitmap.fill (Qt::color1/*opaque*/);
283 return maskBitmap;
288 // public static
289 void kpPixmapFX::ensureTransparentAt (QPixmap *destPixmapPtr, const QRect &destRect)
291 if (!destPixmapPtr)
292 return;
294 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
296 QBitmap maskBitmap = getNonNullMask (*destPixmapPtr);
298 QPainter p (&maskBitmap);
299 p.fillRect (destRect, Qt::color0/*transparent*/);
300 p.end ();
302 destPixmapPtr->setMask (maskBitmap);
304 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
308 // public static
309 void kpPixmapFX::paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, const QPoint &destAt,
310 const QPixmap &brushBitmap)
312 #if DEBUG_KP_PIXMAP_FX
313 kDebug () << "kpPixmapFX::paintMaskTransparentWithBrush(destAt="
314 << destAt << ") brushRect=" << brushBitmap.rect ()
315 << endl;
316 #endif
318 Q_ASSERT (destPixmapPtr);
320 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
322 Q_ASSERT (brushBitmap.depth () == 1);
324 const QRegion brushRegion = QRegion (brushBitmap).translated (destAt);
326 // OPT: Hopelessly inefficent due to function call overhead and
327 // fillRect() changing the mask every single iteration.
329 // kpPixmapFX should have a function that does this with only a
330 // single mask write.
331 foreach (const QRect &r, brushRegion.rects ())
333 #if DEBUG_KP_PIXMAP_FX && 0
334 kDebug () << "\tcopy rect=" << r;
335 #endif
336 kpPixmapFX::fillRect (destPixmapPtr,
337 r.x (), r.y (), r.width (), r.height (),
338 kpColor::Transparent);
341 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
344 // public static
345 void kpPixmapFX::paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, int destX, int destY,
346 const QPixmap &brushBitmap)
348 kpPixmapFX::paintMaskTransparentWithBrush (destPixmapPtr,
349 QPoint (destX, destY),
350 brushBitmap);
354 // public static
355 void kpPixmapFX::ensureOpaqueAt (QPixmap *destPixmapPtr, const QRect &destRect)
357 if (!destPixmapPtr)
358 return;
360 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);
362 // (a bit slow so we cache it)
363 QBitmap maskBitmap = destPixmapPtr->mask ();
365 // Already opaque?
366 if (maskBitmap.isNull ())
367 return;
369 QPainter p (&maskBitmap);
370 p.fillRect (destRect, Qt::color1/*opaque*/);
371 p.end ();
373 destPixmapPtr->setMask (maskBitmap);
375 KP_PFX_CHECK_NO_ALPHA_CHANNEL (*destPixmapPtr);